opencode-tbot 0.1.25 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../src/infra/utils/redact.ts","../src/infra/logger/index.ts","../src/repositories/pending-action.repo.ts","../src/repositories/permission-approval.repo.ts","../src/repositories/session.repo.ts","../src/services/opencode/opencode.client.ts","../src/services/storage/bot-state.ts","../src/services/storage/json-state-store.ts","../src/services/telegram/telegram.client.ts","../src/services/session-activity/foreground-session-tracker.ts","../src/use-cases/abort-prompt.usecase.ts","../src/use-cases/session-creation.ts","../src/use-cases/create-session.usecase.ts","../src/use-cases/get-health.usecase.ts","../src/use-cases/get-path.usecase.ts","../src/use-cases/get-status.usecase.ts","../src/use-cases/agent-selection.ts","../src/use-cases/binding-update.ts","../src/use-cases/list-agents.usecase.ts","../src/use-cases/list-lsp.usecase.ts","../src/use-cases/list-mcp.usecase.ts","../src/use-cases/list-models.usecase.ts","../src/use-cases/list-sessions.usecase.ts","../src/use-cases/rename-session.usecase.ts","../src/use-cases/send-prompt.usecase.ts","../src/use-cases/switch-agent.usecase.ts","../src/use-cases/switch-model.usecase.ts","../src/use-cases/switch-session.usecase.ts","../src/use-cases/upload-file.usecase.ts","../src/app/container.ts","../src/app/bootstrap.ts","../src/services/permission/telegram-approval.ts","../src/app/plugin-events.ts","../src/bot/copy.ts","../src/bot/commands/command-list.ts","../src/bot/commands/sync-commands.ts","../src/bot/i18n.ts","../src/bot/presenters/keyboard.presenter.ts","../src/bot/presenters/error.presenter.ts","../src/bot/presenters/message.presenter.ts","../src/bot/commands/agents.ts","../src/bot/sessions-menu.ts","../src/bot/session-rename.ts","../src/bot/commands/cancel.ts","../src/bot/commands/language.ts","../src/bot/commands/models.ts","../src/bot/commands/new.ts","../src/services/telegram/telegram-format.ts","../src/bot/commands/status.ts","../src/bot/commands/sessions.ts","../src/bot/presenters/static.presenter.ts","../src/bot/commands/start.ts","../src/bot/handlers/callback.handler.ts","../src/bot/handlers/prompt.handler.ts","../src/bot/handlers/file.handler.ts","../src/bot/handlers/message.handler.ts","../src/bot/handlers/voice.handler.ts","../src/bot/middlewares/auth.ts","../src/bot/middlewares/logging.ts","../src/bot/index.ts","../src/app/runtime.ts","../src/plugin.ts"],"sourcesContent":["const REDACTED = \"[REDACTED]\";\nconst DEFAULT_PREVIEW_LENGTH = 160;\nconst TELEGRAM_TOKEN_PATTERN = /\\b\\d{6,}:[A-Za-z0-9_-]{20,}\\b/g;\nconst BEARER_TOKEN_PATTERN = /\\bBearer\\s+[A-Za-z0-9._~+/-]+=*\\b/gi;\nconst NAMED_SECRET_PATTERN = /\\b(api[_\\s-]?key|token|secret|password)\\b(\\s*[:=]\\s*)([^\\s,;]+)/gi;\nconst API_KEY_LIKE_PATTERN = /\\b(?:sk|pk)_[A-Za-z0-9_-]{10,}\\b/g;\n\nexport function redactSensitiveText(input: string): string {\n return input\n .replace(BEARER_TOKEN_PATTERN, `Bearer ${REDACTED}`)\n .replace(TELEGRAM_TOKEN_PATTERN, REDACTED)\n .replace(NAMED_SECRET_PATTERN, (_, name: string, separator: string) => `${name}${separator}${REDACTED}`)\n .replace(API_KEY_LIKE_PATTERN, REDACTED);\n}\n\nexport function createRedactedPreview(input: string, maxLength = DEFAULT_PREVIEW_LENGTH): string {\n const redacted = redactSensitiveText(input).replace(/\\s+/g, \" \").trim();\n\n if (redacted.length <= maxLength) {\n return redacted;\n }\n\n return `${redacted.slice(0, Math.max(0, maxLength - 3))}...`;\n}\n","import { redactSensitiveText } from \"../utils/redact.js\";\n\nexport type AppLogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport interface AppLogger {\n debug(input?: unknown, message?: string): void;\n info(input?: unknown, message?: string): void;\n warn(input?: unknown, message?: string): void;\n error(input?: unknown, message?: string): void;\n flush(): Promise<void>;\n}\n\ninterface OpenCodeAppLogClientLike {\n app: {\n log(input: {\n service: string;\n level: AppLogLevel;\n message: string;\n extra?: Record<string, unknown>;\n }): Promise<unknown>;\n };\n}\n\ninterface CreateOpenCodeAppLoggerOptions {\n level?: string | null;\n service?: string;\n}\n\nconst DEFAULT_SERVICE_NAME = \"opencode-tbot\";\nconst LEVEL_PRIORITY: Record<AppLogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\nexport function createOpenCodeAppLogger(\n client: OpenCodeAppLogClientLike,\n options: CreateOpenCodeAppLoggerOptions = {},\n): AppLogger {\n const service = normalizeServiceName(options.service);\n const minimumLevel = normalizeLogLevel(options.level);\n let queue = Promise.resolve();\n\n const enqueue = (level: AppLogLevel, input?: unknown, message?: string) => {\n if (LEVEL_PRIORITY[level] < LEVEL_PRIORITY[minimumLevel]) {\n return;\n }\n\n const { extra, text } = normalizeLogArguments(input, message);\n const payload = {\n service,\n level,\n message: text,\n ...(extra ? { extra } : {}),\n };\n\n queue = queue\n .catch(() => undefined)\n .then(async () => {\n try {\n await client.app.log(payload);\n } catch {\n // Swallow logging failures to avoid breaking the plugin runtime.\n }\n });\n };\n\n return {\n debug(input?: unknown, message?: string) {\n enqueue(\"debug\", input, message);\n },\n info(input?: unknown, message?: string) {\n enqueue(\"info\", input, message);\n },\n warn(input?: unknown, message?: string) {\n enqueue(\"warn\", input, message);\n },\n error(input?: unknown, message?: string) {\n enqueue(\"error\", input, message);\n },\n async flush() {\n await queue.catch(() => undefined);\n },\n };\n}\n\nfunction normalizeServiceName(value: string | undefined): string {\n const normalized = value?.trim();\n\n return normalized && normalized.length > 0 ? normalized : DEFAULT_SERVICE_NAME;\n}\n\nfunction normalizeLogLevel(value: string | null | undefined): AppLogLevel {\n switch (value?.trim().toLowerCase()) {\n case \"debug\":\n return \"debug\";\n case \"warn\":\n return \"warn\";\n case \"error\":\n return \"error\";\n default:\n return \"info\";\n }\n}\n\nfunction normalizeLogArguments(input?: unknown, message?: string): {\n extra?: Record<string, unknown>;\n text: string;\n} {\n if (typeof input === \"string\") {\n return {\n text: input,\n };\n }\n\n if (message && message.trim().length > 0) {\n const extra = serializeExtra(input);\n\n return {\n ...(extra ? { extra } : {}),\n text: message.trim(),\n };\n }\n\n if (input instanceof Error) {\n return {\n extra: serializeExtra(input) ?? undefined,\n text: input.message.trim() || input.name,\n };\n }\n\n const extra = serializeExtra(input);\n\n return {\n ...(extra ? { extra } : {}),\n text: \"log\",\n };\n}\n\nfunction serializeExtra(input: unknown): Record<string, unknown> | null {\n if (input === null || input === undefined) {\n return null;\n }\n\n if (input instanceof Error) {\n return {\n error: serializeError(input),\n };\n }\n\n if (Array.isArray(input)) {\n return {\n items: input.map((item) => sanitizeValue(item)),\n };\n }\n\n if (typeof input !== \"object\") {\n return {\n value: sanitizeValue(input),\n };\n }\n\n return sanitizeRecord(input as Record<string, unknown>);\n}\n\nfunction sanitizeRecord(record: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(record).map(([key, value]) => [\n key,\n isSensitiveKey(key)\n ? redactSensitiveFieldValue(value)\n : sanitizeValue(value),\n ]),\n );\n}\n\nfunction sanitizeValue(value: unknown): unknown {\n if (value instanceof Error) {\n return serializeError(value);\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => sanitizeValue(item));\n }\n\n if (typeof value === \"string\") {\n return redactSensitiveText(value);\n }\n\n if (!value || typeof value !== \"object\") {\n return value;\n }\n\n return sanitizeRecord(value as Record<string, unknown>);\n}\n\nfunction serializeError(error: Error): Record<string, unknown> {\n return {\n name: error.name,\n message: redactSensitiveText(error.message),\n ...(error.stack ? { stack: redactSensitiveText(error.stack) } : {}),\n ...(\"data\" in error && error.data && typeof error.data === \"object\"\n ? { data: sanitizeValue(error.data) }\n : {}),\n };\n}\n\nfunction isSensitiveKey(key: string): boolean {\n return /token|secret|api[-_]?key|authorization|password/iu.test(key);\n}\n\nfunction redactSensitiveFieldValue(value: unknown): unknown {\n if (typeof value === \"string\" && value.trim().length > 0) {\n return \"[REDACTED]\";\n }\n\n if (Array.isArray(value)) {\n return value.map(() => \"[REDACTED]\");\n }\n\n if (value && typeof value === \"object\") {\n return \"[REDACTED]\";\n }\n\n return value;\n}\n","import type { PendingChatAction } from \"../domain/pending-action/pending-action.entity.js\";\nimport type { PendingActionRepository } from \"../domain/pending-action/pending-action.repository.js\";\nimport type { OpencodeTbotState } from \"../services/storage/bot-state.js\";\nimport type { JsonStateStore } from \"../services/storage/json-state-store.js\";\n\nexport class InMemoryPendingActionRepository implements PendingActionRepository {\n private readonly actionsByChatId = new Map<number, PendingChatAction>();\n\n async getByChatId(chatId: number): Promise<PendingChatAction | null> {\n return this.actionsByChatId.get(chatId) ?? null;\n }\n\n async set(action: PendingChatAction): Promise<void> {\n this.actionsByChatId.set(action.chatId, action);\n }\n\n async clear(chatId: number): Promise<void> {\n this.actionsByChatId.delete(chatId);\n }\n}\n\nexport class FilePendingActionRepository implements PendingActionRepository {\n constructor(private readonly store: JsonStateStore<OpencodeTbotState>) { }\n\n async getByChatId(chatId: number): Promise<PendingChatAction | null> {\n const state = await this.store.read();\n\n return state.pendingActions[String(chatId)] ?? null;\n }\n\n async set(action: PendingChatAction): Promise<void> {\n await this.store.update((state) => {\n state.pendingActions[String(action.chatId)] = action;\n });\n }\n\n async clear(chatId: number): Promise<void> {\n await this.store.update((state) => {\n delete state.pendingActions[String(chatId)];\n });\n }\n}\n","import type { PendingPermissionApproval } from \"../domain/permission/approval.entity.js\";\nimport type { PermissionApprovalRepository } from \"../domain/permission/approval.repository.js\";\nimport type { OpencodeTbotState } from \"../services/storage/bot-state.js\";\nimport type { JsonStateStore } from \"../services/storage/json-state-store.js\";\n\nfunction buildApprovalKey(requestId: string, chatId: number): string {\n return `${requestId}:${chatId}`;\n}\n\nexport class InMemoryPermissionApprovalRepository implements PermissionApprovalRepository {\n private readonly approvalsByKey = new Map<string, PendingPermissionApproval>();\n\n async listByRequestId(requestId: string): Promise<PendingPermissionApproval[]> {\n return [...this.approvalsByKey.values()].filter((approval) => approval.requestId === requestId);\n }\n\n async set(approval: PendingPermissionApproval): Promise<void> {\n this.approvalsByKey.set(buildApprovalKey(approval.requestId, approval.chatId), approval);\n }\n}\n\nexport class FilePermissionApprovalRepository implements PermissionApprovalRepository {\n constructor(private readonly store: JsonStateStore<OpencodeTbotState>) { }\n\n async listByRequestId(requestId: string): Promise<PendingPermissionApproval[]> {\n const state = await this.store.read();\n\n return Object.values(state.pendingPermissions).filter((approval) => approval.requestId === requestId);\n }\n\n async set(approval: PendingPermissionApproval): Promise<void> {\n await this.store.update((state) => {\n state.pendingPermissions[buildApprovalKey(approval.requestId, approval.chatId)] = approval;\n });\n }\n}\n","import type { ChatSessionBinding } from \"../domain/session/session.entity.js\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { OpencodeTbotState } from \"../services/storage/bot-state.js\";\nimport type { JsonStateStore } from \"../services/storage/json-state-store.js\";\n\nexport class InMemorySessionRepository implements SessionRepository {\n private readonly sessionsByChatId = new Map<number, ChatSessionBinding>();\n\n async getByChatId(chatId: number): Promise<ChatSessionBinding | null> {\n return this.sessionsByChatId.get(chatId) ?? null;\n }\n\n async listBySessionId(sessionId: string): Promise<ChatSessionBinding[]> {\n return [...this.sessionsByChatId.values()].filter((binding) => binding.sessionId === sessionId);\n }\n\n async setCurrent(binding: ChatSessionBinding): Promise<void> {\n this.sessionsByChatId.set(binding.chatId, binding);\n }\n\n async touch(chatId: number): Promise<void> {\n const binding = this.sessionsByChatId.get(chatId);\n\n if (!binding) {\n return;\n }\n\n this.sessionsByChatId.set(chatId, {\n ...binding,\n updatedAt: new Date().toISOString(),\n });\n }\n}\n\nexport class FileSessionRepository implements SessionRepository {\n constructor(private readonly store: JsonStateStore<OpencodeTbotState>) { }\n\n async getByChatId(chatId: number): Promise<ChatSessionBinding | null> {\n const state = await this.store.read();\n\n return state.sessions[String(chatId)] ?? null;\n }\n\n async listBySessionId(sessionId: string): Promise<ChatSessionBinding[]> {\n const state = await this.store.read();\n\n return Object.values(state.sessions).filter((binding) => binding.sessionId === sessionId);\n }\n\n async setCurrent(binding: ChatSessionBinding): Promise<void> {\n await this.store.update((state) => {\n state.sessions[String(binding.chatId)] = binding;\n });\n }\n\n async touch(chatId: number): Promise<void> {\n await this.store.update((state) => {\n const current = state.sessions[String(chatId)];\n\n if (!current) {\n return;\n }\n\n state.sessions[String(chatId)] = {\n ...current,\n updatedAt: new Date().toISOString(),\n };\n });\n }\n}\n","import {\n type Agent,\n createOpencodeClient,\n type Config,\n type LspStatus,\n type McpStatus,\n type Model,\n type OpencodeClient,\n type Part,\n type PermissionRequest,\n type Path,\n type Provider,\n type Project,\n type Session,\n type SessionStatus,\n} from \"@opencode-ai/sdk/v2/client\";\nimport { z } from \"zod\";\n\nexport interface OpenCodeClientOptions {\n baseUrl: string;\n apiKey?: string;\n}\n\nexport function buildOpenCodeSdkConfig(options: OpenCodeClientOptions) {\n const apiKey = options.apiKey?.trim();\n\n return {\n baseUrl: options.baseUrl,\n ...(apiKey ? { auth: apiKey } : {}),\n };\n}\n\nexport interface PromptSessionInput {\n sessionId: string;\n prompt?: string | null;\n agent?: string | null;\n structured?: boolean;\n model?: {\n providerID: string;\n modelID: string;\n };\n variant?: string | null;\n files?: PromptSessionFilePartInput[];\n}\n\nexport interface PromptSessionFilePartInput {\n filename?: string;\n mime: string;\n url: string;\n}\n\nexport interface PromptSessionTokenMetrics {\n total: number | null;\n input: number | null;\n output: number | null;\n reasoning: number | null;\n cacheRead: number | null;\n cacheWrite: number | null;\n}\n\nexport interface PromptSessionMetrics {\n durationMs: number | null;\n tokens: PromptSessionTokenMetrics;\n}\n\nexport interface PromptSessionResult {\n assistantError: OpenCodeAssistantError | null;\n bodyMd: string | null;\n fallbackText: string;\n info: OpenCodeAssistantMessage | null;\n metrics: PromptSessionMetrics;\n parts: Part[];\n structured: unknown;\n}\n\nexport type OpenCodeProject = Project;\nexport type OpenCodeSession = Session;\nexport type OpenCodeModel = Model;\nexport type OpenCodeProvider = Provider;\nexport type OpenCodeConfig = Config;\nexport type OpenCodeAgent = Agent;\nexport type OpenCodeLspStatus = LspStatus;\nexport type OpenCodePath = Path;\nexport type OpenCodeMcpStatus = McpStatus;\nexport type OpenCodeSessionStatus = SessionStatus;\nexport type OpenCodePermissionRequest = PermissionRequest;\nexport interface OpenCodeHealth {\n healthy: boolean;\n version: string;\n}\n\nexport interface OpenCodeConfiguredModel {\n id: string;\n providerID: string;\n providerName: string;\n name: string;\n qualifiedId: string;\n reasoning: boolean;\n variants: Record<string, Record<string, unknown>>;\n}\n\ntype RawSdkRequestOptions = {\n body?: unknown;\n path?: Record<string, unknown>;\n query?: Record<string, unknown>;\n responseStyle?: \"data\" | \"fields\";\n signal?: AbortSignal;\n throwOnError?: boolean;\n url: string;\n};\n\ntype RawSdkHttpClient = {\n delete?(options: RawSdkRequestOptions): Promise<unknown>;\n get?(options: RawSdkRequestOptions): Promise<unknown>;\n patch?(options: RawSdkRequestOptions): Promise<unknown>;\n post?(options: RawSdkRequestOptions): Promise<unknown>;\n};\n\nexport type OpenCodeSdkClientLike = Pick<OpencodeClient, \"app\" | \"config\" | \"global\" | \"lsp\" | \"mcp\" | \"path\" | \"permission\" | \"project\" | \"session\"> & {\n _client?: RawSdkHttpClient;\n};\ntype MaybeWrappedResponse<T> = T | { data: T };\ntype OpenCodeProviderCatalog = {\n providers: OpenCodeProvider[];\n default: Record<string, string>;\n};\ntype OpenCodeProviderAvailability = Map<string, Set<string> | null>;\ntype OpenCodeProviderConfig = NonNullable<OpenCodeConfig[\"provider\"]>[string];\ntype OpenCodeModelConfig = NonNullable<OpenCodeProviderConfig[\"models\"]>[string];\ntype OpenCodePromptResponse = {\n info: unknown;\n parts?: Part[] | null;\n};\n\nexport type OpenCodePromptTimeoutStage =\n | \"capture-known-messages\"\n | \"send-prompt\"\n | \"poll-message\"\n | \"poll-messages\"\n | \"poll-status\"\n | \"final-scan\";\n\nexport interface OpenCodePromptTimeoutErrorData {\n elapsedMs: number;\n message?: string;\n messageId?: string;\n sessionId: string;\n stage: OpenCodePromptTimeoutStage;\n timeoutMs: number;\n}\n\nexport class OpenCodePromptTimeoutError extends Error {\n readonly data: OpenCodePromptTimeoutErrorData;\n\n constructor(data: OpenCodePromptTimeoutErrorData) {\n super(data.message ?? \"OpenCode prompt timed out.\");\n this.name = \"OpenCodePromptTimeoutError\";\n this.data = data;\n }\n}\n\nexport interface OpenCodeAssistantError {\n data?: Record<string, unknown>;\n name: string;\n [key: string]: unknown;\n}\n\nexport interface OpenCodeAssistantMessage {\n agent?: string;\n cost?: number;\n error?: OpenCodeAssistantError;\n finish?: string;\n id?: string;\n mode?: string;\n modelID?: string;\n parentID?: string;\n path?: {\n cwd?: string;\n root?: string;\n };\n providerID?: string;\n role?: \"assistant\";\n sessionID?: string;\n structured?: unknown;\n summary?: boolean;\n time?: {\n completed?: number;\n created?: number;\n };\n tokens?: {\n cache?: {\n read?: number;\n write?: number;\n };\n input?: number;\n output?: number;\n reasoning?: number;\n total?: number;\n };\n variant?: string;\n}\n\nconst EMPTY_RESPONSE_TEXT = \"OpenCode returned empty response.\";\nconst PROMPT_MESSAGE_POLL_INITIAL_DELAYS_MS = [0, 100, 250, 500, 1000];\nconst PROMPT_MESSAGE_POLL_INTERVAL_MS = 2000;\nconst PROMPT_POLL_REQUEST_TIMEOUT_MS = 15_000;\nconst PROMPT_SEND_TIMEOUT_MS = 30_000;\nconst PROMPT_MESSAGE_POLL_TIMEOUT_MS = 60_000;\nconst PROMPT_MESSAGE_POLL_LIMIT = 20;\nconst PROMPT_LOG_SERVICE = \"opencode-tbot\";\nconst STRUCTURED_REPLY_SCHEMA = {\n type: \"json_schema\" as const,\n retryCount: 2,\n schema: {\n type: \"object\",\n additionalProperties: false,\n required: [\"body_md\"],\n properties: {\n body_md: {\n type: \"string\",\n description: \"Markdown body only. Do not include duration, token usage, or any footer. Do not wrap the whole answer in ```md or ```markdown fences. Use Markdown formatting directly unless the user explicitly asks for raw Markdown source.\",\n },\n },\n },\n};\nconst SDK_OPTIONS = {\n responseStyle: \"data\" as const,\n throwOnError: true as const,\n};\nconst StructuredReplySchema = z.object({\n body_md: z.string(),\n});\n\nexport interface OpenCodeClientLike {\n readonly client?: OpenCodeSdkClientLike;\n abortSession(sessionId: string): Promise<boolean>;\n deleteSession(sessionId: string): Promise<boolean>;\n forkSession(sessionId: string, messageId?: string): Promise<OpenCodeSession>;\n getHealth(): Promise<OpenCodeHealth>;\n getPath(): Promise<OpenCodePath>;\n listLspStatuses(directory?: string): Promise<OpenCodeLspStatus[]>;\n listMcpStatuses(directory?: string): Promise<Record<string, OpenCodeMcpStatus>>;\n getSessionStatuses(): Promise<Record<string, OpenCodeSessionStatus>>;\n listProjects(): Promise<OpenCodeProject[]>;\n listSessions(): Promise<OpenCodeSession[]>;\n getCurrentProject(): Promise<OpenCodeProject>;\n createSessionForDirectory(directory: string, title?: string): Promise<OpenCodeSession>;\n renameSession(sessionId: string, title: string): Promise<OpenCodeSession>;\n listAgents(): Promise<OpenCodeAgent[]>;\n listPendingPermissions(directory?: string): Promise<OpenCodePermissionRequest[]>;\n replyToPermission(requestId: string, reply: \"once\" | \"always\" | \"reject\", message?: string): Promise<boolean>;\n listModels(): Promise<OpenCodeConfiguredModel[]>;\n promptSession(input: PromptSessionInput): Promise<PromptSessionResult>;\n}\n\nexport class OpenCodeClient implements OpenCodeClientLike {\n readonly client: OpenCodeSdkClientLike;\n private readonly fetchFn: typeof fetch;\n private promptRequestTimeouts = {\n pollRequestMs: PROMPT_POLL_REQUEST_TIMEOUT_MS,\n sendMs: PROMPT_SEND_TIMEOUT_MS,\n totalPollMs: PROMPT_MESSAGE_POLL_TIMEOUT_MS,\n };\n private modelCache: {\n expiresAt: number;\n promise: Promise<OpenCodeConfiguredModel[]> | null;\n value: OpenCodeConfiguredModel[] | null;\n } = {\n expiresAt: 0,\n promise: null,\n value: null,\n };\n\n constructor(\n options?: OpenCodeClientOptions,\n client?: OpenCodeSdkClientLike,\n fetchFn: typeof fetch = fetch,\n ) {\n if (!options && !client) {\n throw new Error(\"OpenCodeClient requires either base URL options or an injected SDK client.\");\n }\n\n this.client = client ?? createOpencodeClient(buildOpenCodeSdkConfig(options as OpenCodeClientOptions));\n this.fetchFn = fetchFn;\n }\n\n async getHealth(): Promise<OpenCodeHealth> {\n const rawClient = getRawSdkClient(this.client);\n\n if (rawClient?.get) {\n const response = await this.requestRaw<OpenCodeHealth>(\n \"get\",\n { url: \"/global/health\" },\n );\n\n return response;\n }\n\n const healthEndpoint = this.client.global?.health;\n\n if (typeof healthEndpoint === \"function\") {\n const response = await healthEndpoint.call(\n this.client.global,\n SDK_OPTIONS,\n ) as MaybeWrappedResponse<OpenCodeHealth>;\n\n return unwrapSdkData(response);\n }\n\n if (!rawClient?.get) {\n throw new Error(\"OpenCode SDK client does not expose a compatible health endpoint.\");\n }\n\n return this.requestRaw<OpenCodeHealth>(\n \"get\",\n { url: \"/global/health\" },\n );\n }\n\n async abortSession(sessionId: string): Promise<boolean> {\n if (hasRawSdkMethod(this.client, \"post\")) {\n return this.requestRaw<boolean>(\n \"post\",\n {\n url: \"/session/{sessionID}/abort\",\n path: {\n sessionID: sessionId,\n },\n },\n );\n }\n\n const response = await this.client.session.abort(\n { sessionID: sessionId },\n SDK_OPTIONS,\n ) as MaybeWrappedResponse<boolean>;\n\n return unwrapSdkData(response);\n }\n\n async deleteSession(sessionId: string): Promise<boolean> {\n if (hasRawSdkMethod(this.client, \"delete\")) {\n return this.requestRaw<boolean>(\n \"delete\",\n {\n url: \"/session/{sessionID}\",\n path: {\n sessionID: sessionId,\n },\n },\n );\n }\n\n const response = await this.client.session.delete(\n { sessionID: sessionId },\n SDK_OPTIONS,\n ) as MaybeWrappedResponse<boolean>;\n\n return unwrapSdkData(response);\n }\n\n async forkSession(sessionId: string, messageId?: string): Promise<OpenCodeSession> {\n if (hasRawSdkMethod(this.client, \"post\")) {\n return this.requestRaw<OpenCodeSession>(\n \"post\",\n {\n url: \"/session/{sessionID}/fork\",\n path: {\n sessionID: sessionId,\n },\n ...(messageId?.trim()\n ? {\n body: {\n messageID: messageId.trim(),\n },\n }\n : {}),\n },\n );\n }\n\n const response = await this.client.session.fork(\n {\n sessionID: sessionId,\n ...(messageId?.trim()\n ? { messageID: messageId.trim() }\n : {}),\n },\n SDK_OPTIONS,\n ) as MaybeWrappedResponse<OpenCodeSession>;\n\n return unwrapSdkData(response);\n }\n\n async getPath(): Promise<OpenCodePath> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodePath>(\n \"get\",\n { url: \"/path\" },\n );\n }\n\n const response = await this.client.path.get(undefined, SDK_OPTIONS) as MaybeWrappedResponse<OpenCodePath>;\n\n return unwrapSdkData(response);\n }\n\n async listLspStatuses(directory?: string): Promise<OpenCodeLspStatus[]> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodeLspStatus[]>(\n \"get\",\n {\n url: \"/lsp\",\n ...(directory ? { query: { directory } } : {}),\n },\n );\n }\n\n const response = await this.client.lsp.status(\n directory\n ? { directory }\n : undefined,\n SDK_OPTIONS,\n ) as MaybeWrappedResponse<OpenCodeLspStatus[]>;\n\n return unwrapSdkData(response);\n }\n\n async listMcpStatuses(directory?: string): Promise<Record<string, OpenCodeMcpStatus>> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<Record<string, OpenCodeMcpStatus>>(\n \"get\",\n {\n url: \"/mcp\",\n ...(directory ? { query: { directory } } : {}),\n },\n );\n }\n\n const response = await this.client.mcp.status(\n directory\n ? { directory }\n : undefined,\n SDK_OPTIONS,\n ) as MaybeWrappedResponse<Record<string, OpenCodeMcpStatus>>;\n\n return unwrapSdkData(response);\n }\n\n async getSessionStatuses(): Promise<Record<string, OpenCodeSessionStatus>> {\n return this.loadSessionStatuses();\n }\n\n async listProjects(): Promise<OpenCodeProject[]> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodeProject[]>(\n \"get\",\n { url: \"/project\" },\n );\n }\n\n const response = await this.client.project.list(undefined, SDK_OPTIONS) as MaybeWrappedResponse<OpenCodeProject[]>;\n\n return unwrapSdkData(response);\n }\n\n async listSessions(): Promise<OpenCodeSession[]> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodeSession[]>(\n \"get\",\n { url: \"/session\" },\n );\n }\n\n const response = await this.client.session.list(undefined, SDK_OPTIONS) as MaybeWrappedResponse<OpenCodeSession[]>;\n\n return unwrapSdkData(response);\n }\n\n async getCurrentProject(): Promise<OpenCodeProject> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodeProject>(\n \"get\",\n { url: \"/project/current\" },\n );\n }\n\n const response = await this.client.project.current(undefined, SDK_OPTIONS) as MaybeWrappedResponse<OpenCodeProject>;\n\n return unwrapSdkData(response);\n }\n\n async createSessionForDirectory(directory: string, title?: string): Promise<OpenCodeSession> {\n if (hasRawSdkMethod(this.client, \"post\")) {\n return this.requestRaw<OpenCodeSession>(\n \"post\",\n {\n url: \"/session\",\n query: { directory },\n ...(title\n ? {\n body: {\n title,\n },\n }\n : {}),\n },\n );\n }\n\n const response = await this.client.session.create(\n title ? { directory, title } : { directory },\n SDK_OPTIONS,\n ) as MaybeWrappedResponse<OpenCodeSession>;\n\n return unwrapSdkData(response);\n }\n\n async renameSession(sessionId: string, title: string): Promise<OpenCodeSession> {\n if (hasRawSdkMethod(this.client, \"patch\")) {\n return this.requestRaw<OpenCodeSession>(\n \"patch\",\n {\n url: \"/session/{sessionID}\",\n path: {\n sessionID: sessionId,\n },\n body: {\n title,\n },\n },\n );\n }\n\n const response = await this.client.session.update(\n { sessionID: sessionId, title },\n SDK_OPTIONS,\n ) as MaybeWrappedResponse<OpenCodeSession>;\n\n return unwrapSdkData(response);\n }\n\n async listAgents(): Promise<OpenCodeAgent[]> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodeAgent[]>(\n \"get\",\n { url: \"/agent\" },\n );\n }\n\n const response = await this.client.app.agents(undefined, SDK_OPTIONS) as MaybeWrappedResponse<OpenCodeAgent[]>;\n\n return unwrapSdkData(response);\n }\n\n async listPendingPermissions(directory?: string): Promise<OpenCodePermissionRequest[]> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodePermissionRequest[]>(\n \"get\",\n {\n url: \"/permission\",\n ...(directory ? { query: { directory } } : {}),\n },\n );\n }\n\n const response = await this.client.permission.list(\n directory\n ? { directory }\n : undefined,\n SDK_OPTIONS,\n ) as MaybeWrappedResponse<OpenCodePermissionRequest[]>;\n\n return unwrapSdkData(response);\n }\n\n async replyToPermission(\n requestId: string,\n reply: \"once\" | \"always\" | \"reject\",\n message?: string,\n ): Promise<boolean> {\n if (hasRawSdkMethod(this.client, \"post\")) {\n return this.requestRaw<boolean>(\n \"post\",\n {\n url: \"/permission/{requestID}/reply\",\n path: {\n requestID: requestId,\n },\n body: {\n reply,\n ...(message?.trim()\n ? { message: message.trim() }\n : {}),\n },\n },\n );\n }\n\n const response = await this.client.permission.reply(\n {\n requestID: requestId,\n reply,\n ...(message?.trim()\n ? { message: message.trim() }\n : {}),\n },\n SDK_OPTIONS,\n ) as MaybeWrappedResponse<boolean>;\n\n return unwrapSdkData(response);\n }\n\n async listModels(): Promise<OpenCodeConfiguredModel[]> {\n const now = Date.now();\n\n if (this.modelCache.value && this.modelCache.expiresAt > now) {\n return this.modelCache.value;\n }\n\n if (this.modelCache.promise) {\n return this.modelCache.promise;\n }\n\n const refreshPromise = this.loadModels().finally(() => {\n if (this.modelCache.promise === refreshPromise) {\n this.modelCache.promise = null;\n }\n });\n\n this.modelCache.promise = refreshPromise;\n\n return refreshPromise;\n }\n\n async promptSession(input: PromptSessionInput): Promise<PromptSessionResult> {\n const startedAt = Date.now();\n const promptText = input.prompt?.trim() ?? \"\";\n const parts = [\n ...(promptText\n ? [{\n type: \"text\" as const,\n text: promptText,\n }]\n : []),\n ...((input.files ?? []).map((file) => ({\n type: \"file\" as const,\n filename: file.filename,\n mime: file.mime,\n url: file.url,\n }))),\n ];\n\n if (parts.length === 0) {\n throw new Error(\"Prompt requires text or file attachments.\");\n }\n\n const knownMessageIds = await this.captureKnownMessageIds(input.sessionId);\n const initialData = await this.sendPromptRequest(input, parts);\n const resolvedData = await this.resolvePromptResponse(\n input,\n initialData,\n knownMessageIds,\n startedAt,\n );\n const finishedAt = Date.now();\n\n return buildPromptSessionResult(resolvedData, {\n emptyResponseText: EMPTY_RESPONSE_TEXT,\n finishedAt,\n startedAt,\n structured: input.structured ?? false,\n });\n }\n\n private async resolvePromptResponse(\n input: PromptSessionInput,\n data: OpenCodePromptResponse | null,\n knownMessageIds: Set<string>,\n startedAt: number,\n ): Promise<OpenCodePromptResponse> {\n const structured = input.structured ?? false;\n\n if (data && !shouldPollPromptMessage(data, structured)) {\n return data;\n }\n\n const messageId = data ? extractMessageId(data.info) : null;\n const candidateOptions: PromptResponseCandidateOptions = {\n initialMessageId: messageId,\n initialParentId: data ? toAssistantMessage(data.info)?.parentID ?? null : null,\n knownMessageIds,\n requestStartedAt: resolvePromptCandidateStartTime(startedAt, data),\n structured,\n };\n let bestCandidate = selectPromptResponseCandidate(\n data ? [data] : [],\n candidateOptions,\n );\n const deadlineAt = Date.now() + this.promptRequestTimeouts.totalPollMs;\n let idleStatusSeen = false;\n let attempt = 0;\n\n while (true) {\n const delayMs = getPromptMessagePollDelayMs(attempt);\n attempt += 1;\n\n if (delayMs > 0) {\n const remainingMs = deadlineAt - Date.now();\n\n if (remainingMs <= 0) {\n break;\n }\n\n await delay(Math.min(delayMs, remainingMs));\n }\n\n if (messageId) {\n const next = await this.fetchPromptMessage(input.sessionId, messageId);\n\n if (next) {\n const nextCandidate = selectPromptResponseCandidate(\n [bestCandidate, next],\n candidateOptions,\n );\n\n if (nextCandidate) {\n bestCandidate = nextCandidate;\n }\n\n if (bestCandidate && !shouldPollPromptMessage(bestCandidate, structured)) {\n return bestCandidate;\n }\n }\n }\n\n const latest = await this.findLatestPromptResponse(\n input.sessionId,\n candidateOptions,\n \"poll-messages\",\n );\n\n if (latest) {\n const nextCandidate = selectPromptResponseCandidate(\n [bestCandidate, latest],\n candidateOptions,\n );\n\n if (nextCandidate) {\n bestCandidate = nextCandidate;\n }\n\n if (bestCandidate && !shouldPollPromptMessage(bestCandidate, structured)) {\n return bestCandidate;\n }\n }\n\n const status = await this.fetchPromptSessionStatus(input.sessionId);\n\n if (status?.type === \"idle\") {\n if (idleStatusSeen) {\n break;\n }\n\n idleStatusSeen = true;\n }\n\n if (Date.now() >= deadlineAt) {\n break;\n }\n }\n\n const latest = await this.findLatestPromptResponse(\n input.sessionId,\n candidateOptions,\n \"final-scan\",\n );\n\n const resolved = selectPromptResponseCandidate(\n [bestCandidate, latest],\n candidateOptions,\n );\n\n if (!resolved || shouldPollPromptMessage(resolved, structured)) {\n const error = createOpenCodePromptTimeoutError({\n sessionId: input.sessionId,\n stage: \"final-scan\",\n timeoutMs: this.promptRequestTimeouts.totalPollMs,\n messageId: messageId ?? undefined,\n });\n this.logPromptRequestFailure(error, {\n sessionId: input.sessionId,\n stage: \"final-scan\",\n timeoutMs: this.promptRequestTimeouts.totalPollMs,\n messageId,\n });\n throw error;\n }\n\n return resolved;\n }\n\n private async fetchPromptMessage(\n sessionId: string,\n messageId: string,\n ): Promise<OpenCodePromptResponse | null> {\n try {\n return await this.runPromptRequestWithTimeout(\n {\n sessionId,\n stage: \"poll-message\",\n timeoutMs: this.promptRequestTimeouts.pollRequestMs,\n messageId,\n },\n async (signal) => {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return normalizePromptResponse(await this.requestRaw<OpenCodePromptResponse>(\n \"get\",\n {\n url: \"/session/{sessionID}/message/{messageID}\",\n path: {\n sessionID: sessionId,\n messageID: messageId,\n },\n signal,\n },\n ));\n }\n\n if (typeof this.client.session.message !== \"function\") {\n return null;\n }\n\n const response = await this.client.session.message(\n {\n sessionID: sessionId,\n messageID: messageId,\n },\n {\n ...SDK_OPTIONS,\n signal,\n },\n ) as MaybeWrappedResponse<OpenCodePromptResponse>;\n\n return normalizePromptResponse(unwrapSdkData(response));\n },\n );\n } catch (error) {\n this.logPromptRequestFailure(error, {\n sessionId,\n stage: \"poll-message\",\n timeoutMs: this.promptRequestTimeouts.pollRequestMs,\n messageId,\n });\n return null;\n }\n }\n\n private async captureKnownMessageIds(sessionId: string): Promise<Set<string>> {\n const messages = await this.fetchRecentPromptMessages(sessionId, \"capture-known-messages\");\n\n if (!messages) {\n return new Set();\n }\n\n return new Set(\n messages\n .map((message) => extractMessageId(message.info))\n .filter((id): id is string => typeof id === \"string\" && id.length > 0),\n );\n }\n\n private async fetchRecentPromptMessages(\n sessionId: string,\n stage: Extract<OpenCodePromptTimeoutStage, \"capture-known-messages\" | \"poll-messages\" | \"final-scan\">,\n ): Promise<OpenCodePromptResponse[] | null> {\n try {\n return await this.runPromptRequestWithTimeout(\n {\n sessionId,\n stage,\n timeoutMs: this.promptRequestTimeouts.pollRequestMs,\n },\n async (signal) => {\n if (hasRawSdkMethod(this.client, \"get\")) {\n const response = await this.requestRaw<OpenCodePromptResponse[]>(\n \"get\",\n {\n url: \"/session/{sessionID}/message\",\n path: {\n sessionID: sessionId,\n },\n query: {\n limit: PROMPT_MESSAGE_POLL_LIMIT,\n },\n signal,\n },\n );\n\n return normalizePromptResponses(response);\n }\n\n if (typeof this.client.session.messages !== \"function\") {\n return null;\n }\n\n const response = await this.client.session.messages(\n {\n sessionID: sessionId,\n limit: PROMPT_MESSAGE_POLL_LIMIT,\n },\n {\n ...SDK_OPTIONS,\n signal,\n },\n ) as MaybeWrappedResponse<OpenCodePromptResponse[]>;\n\n return normalizePromptResponses(unwrapSdkData(response));\n },\n );\n } catch (error) {\n this.logPromptRequestFailure(error, {\n sessionId,\n stage,\n timeoutMs: this.promptRequestTimeouts.pollRequestMs,\n });\n return null;\n }\n }\n\n private async fetchPromptSessionStatus(sessionId: string): Promise<OpenCodeSessionStatus | null> {\n try {\n const statuses = await this.runPromptRequestWithTimeout(\n {\n sessionId,\n stage: \"poll-status\",\n timeoutMs: this.promptRequestTimeouts.pollRequestMs,\n },\n async (signal) => this.loadSessionStatuses(signal),\n );\n\n return statuses[sessionId] ?? null;\n } catch (error) {\n this.logPromptRequestFailure(error, {\n sessionId,\n stage: \"poll-status\",\n timeoutMs: this.promptRequestTimeouts.pollRequestMs,\n });\n return null;\n }\n }\n\n private async findLatestPromptResponse(\n sessionId: string,\n options: PromptResponseCandidateOptions,\n stage: Extract<OpenCodePromptTimeoutStage, \"poll-messages\" | \"final-scan\">,\n ): Promise<OpenCodePromptResponse | null> {\n const messages = await this.fetchRecentPromptMessages(sessionId, stage);\n\n if (!messages || messages.length === 0) {\n return null;\n }\n\n return selectPromptResponseCandidate(messages, options);\n }\n\n private async loadModels(): Promise<OpenCodeConfiguredModel[]> {\n const [config, providerCatalog] = await Promise.all([\n this.loadConfig(),\n this.loadProviderCatalog(),\n ]);\n const providerAvailability = await resolveProviderAvailability(config, this.fetchFn);\n const models = buildSelectableModels(\n config,\n providerCatalog.providers,\n providerAvailability,\n );\n\n this.modelCache = {\n expiresAt: Date.now() + 60_000,\n promise: null,\n value: models,\n };\n\n return models;\n }\n\n private async loadConfig(): Promise<OpenCodeConfig> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodeConfig>(\n \"get\",\n { url: \"/config\" },\n );\n }\n\n const response = await this.client.config.get(\n undefined,\n SDK_OPTIONS,\n ) as MaybeWrappedResponse<OpenCodeConfig>;\n\n return unwrapSdkData(response);\n }\n\n private async loadProviderCatalog(): Promise<OpenCodeProviderCatalog> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodeProviderCatalog>(\n \"get\",\n { url: \"/config/providers\" },\n );\n }\n\n const response = await this.client.config.providers(\n undefined,\n SDK_OPTIONS,\n ) as MaybeWrappedResponse<OpenCodeProviderCatalog>;\n\n return unwrapSdkData(response);\n }\n\n private async sendPromptRequest(\n input: PromptSessionInput,\n parts: Array<\n | { type: \"text\"; text: string }\n | { type: \"file\"; filename?: string; mime: string; url: string }\n >,\n ): Promise<OpenCodePromptResponse | null> {\n const requestBody = {\n ...(input.agent ? { agent: input.agent } : {}),\n ...(input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {}),\n ...(input.model ? { model: input.model } : {}),\n ...(input.variant ? { variant: input.variant } : {}),\n parts,\n };\n const requestParameters = {\n sessionID: input.sessionId,\n ...requestBody,\n };\n\n try {\n if (typeof this.client.session?.promptAsync === \"function\") {\n await this.runPromptRequestWithTimeout(\n {\n sessionId: input.sessionId,\n stage: \"send-prompt\",\n timeoutMs: this.promptRequestTimeouts.sendMs,\n },\n async (signal) => {\n await this.client.session.promptAsync(requestParameters, {\n ...SDK_OPTIONS,\n signal,\n });\n },\n );\n\n return null;\n }\n\n return await this.runPromptRequestWithTimeout(\n {\n sessionId: input.sessionId,\n stage: \"send-prompt\",\n timeoutMs: this.promptRequestTimeouts.sendMs,\n },\n async (signal) => {\n if (hasRawSdkMethod(this.client, \"post\")) {\n return normalizePromptResponse(await this.requestRaw<OpenCodePromptResponse>(\n \"post\",\n {\n url: \"/session/{sessionID}/message\",\n path: {\n sessionID: input.sessionId,\n },\n body: requestBody,\n signal,\n },\n ));\n }\n\n const response = await this.client.session.prompt(requestParameters, {\n ...SDK_OPTIONS,\n signal,\n }) as MaybeWrappedResponse<OpenCodePromptResponse>;\n\n return normalizePromptResponse(unwrapSdkData(response));\n },\n );\n } catch (error) {\n this.logPromptRequestFailure(error, {\n sessionId: input.sessionId,\n stage: \"send-prompt\",\n timeoutMs: this.promptRequestTimeouts.sendMs,\n });\n throw error;\n }\n }\n\n private async requestRaw<T>(\n method: keyof Pick<RawSdkHttpClient, \"delete\" | \"get\" | \"patch\" | \"post\">,\n options: {\n body?: unknown;\n path?: Record<string, unknown>;\n query?: Record<string, unknown>;\n signal?: AbortSignal;\n url: string;\n },\n ): Promise<T> {\n const rawClient = getRawSdkClient(this.client);\n const handler = rawClient?.[method];\n\n if (typeof handler !== \"function\") {\n throw new Error(`OpenCode SDK client does not expose a compatible raw ${method.toUpperCase()} method.`);\n }\n\n const response = await handler({\n ...SDK_OPTIONS,\n ...options,\n }) as MaybeWrappedResponse<T>;\n\n return unwrapSdkData(response);\n }\n\n private async loadSessionStatuses(\n signal?: AbortSignal,\n ): Promise<Record<string, OpenCodeSessionStatus>> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<Record<string, OpenCodeSessionStatus>>(\n \"get\",\n {\n url: \"/session/status\",\n ...(signal ? { signal } : {}),\n },\n );\n }\n\n const response = await this.client.session.status(\n undefined,\n {\n ...SDK_OPTIONS,\n ...(signal ? { signal } : {}),\n },\n ) as MaybeWrappedResponse<Record<string, OpenCodeSessionStatus>>;\n\n return unwrapSdkData(response);\n }\n\n private async runPromptRequestWithTimeout<T>(\n input: {\n messageId?: string | null;\n sessionId: string;\n stage: OpenCodePromptTimeoutStage;\n timeoutMs: number;\n },\n operation: (signal: AbortSignal) => Promise<T>,\n ): Promise<T> {\n const startedAt = Date.now();\n const controller = new AbortController();\n let timeoutHandle: ReturnType<typeof setTimeout> | null = null;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n reject(createOpenCodePromptTimeoutError({\n sessionId: input.sessionId,\n stage: input.stage,\n timeoutMs: input.timeoutMs,\n messageId: input.messageId ?? undefined,\n elapsedMs: Date.now() - startedAt,\n }));\n controller.abort();\n }, input.timeoutMs);\n });\n\n try {\n return await Promise.race([\n operation(controller.signal),\n timeoutPromise,\n ]);\n } finally {\n if (timeoutHandle !== null) {\n clearTimeout(timeoutHandle);\n }\n }\n }\n\n private logPromptRequestFailure(\n error: unknown,\n input: {\n messageId?: string | null;\n sessionId: string;\n stage: OpenCodePromptTimeoutStage;\n timeoutMs: number;\n },\n ) {\n if (error instanceof OpenCodePromptTimeoutError) {\n this.logPromptRequest(\"warn\", {\n elapsedMs: error.data.elapsedMs,\n messageId: error.data.messageId,\n sessionId: error.data.sessionId,\n stage: error.data.stage,\n timeoutMs: error.data.timeoutMs,\n }, \"OpenCode prompt request timed out\");\n return;\n }\n\n this.logPromptRequest(\"warn\", {\n error,\n messageId: input.messageId ?? undefined,\n sessionId: input.sessionId,\n stage: input.stage,\n timeoutMs: input.timeoutMs,\n }, \"OpenCode prompt request failed\");\n }\n\n private logPromptRequest(\n level: \"warn\" | \"error\" | \"info\",\n extra: Record<string, unknown>,\n message: string,\n ) {\n const log = this.client.app?.log;\n\n if (typeof log !== \"function\") {\n return;\n }\n\n void log.call(this.client.app, {\n service: PROMPT_LOG_SERVICE,\n level,\n message,\n extra,\n }).catch(() => undefined);\n }\n}\n\nexport function createOpenCodeClientFromSdkClient(\n client: OpenCodeSdkClientLike,\n fetchFn: typeof fetch = fetch,\n): OpenCodeClientLike {\n return new OpenCodeClient(undefined, client, fetchFn);\n}\n\nfunction buildSelectableModels(\n config: OpenCodeConfig,\n providers: OpenCodeProvider[],\n providerAvailability: OpenCodeProviderAvailability = new Map(),\n): OpenCodeConfiguredModel[] {\n const configuredProviders = config.provider ?? {};\n const providersById = new Map(providers.map((provider) => [provider.id, provider]));\n const models = new Map<string, OpenCodeConfiguredModel>();\n\n for (const [providerId, providerConfig] of Object.entries(configuredProviders)) {\n const configuredModels = providerConfig.models ?? {};\n\n for (const [configuredModelKey, modelConfig] of Object.entries(configuredModels)) {\n const model = buildConfiguredModel(\n providerId,\n providerConfig,\n configuredModelKey,\n modelConfig,\n providersById.get(providerId),\n providerAvailability.get(providerId) ?? null,\n );\n\n if (model) {\n models.set(model.qualifiedId, model);\n }\n }\n }\n\n for (const provider of providers) {\n for (const model of buildCatalogProviderModels(provider, providerAvailability.get(provider.id) ?? null)) {\n if (!models.has(model.qualifiedId)) {\n models.set(model.qualifiedId, model);\n }\n }\n }\n\n return [...models.values()];\n}\n\nfunction buildConfiguredModel(\n providerId: string,\n providerConfig: OpenCodeProviderConfig,\n configuredModelKey: string,\n modelConfig: OpenCodeModelConfig,\n providerCatalog: OpenCodeProvider | undefined,\n availableModelIds: Set<string> | null,\n): OpenCodeConfiguredModel | null {\n const modelId = modelConfig.id ?? configuredModelKey;\n const catalogModel = providerCatalog?.models[modelId];\n\n if (!isTextModel(modelConfig, catalogModel)) {\n return null;\n }\n\n if (!isModelAvailable(modelId, availableModelIds)) {\n return null;\n }\n\n return {\n id: modelId,\n providerID: providerId,\n providerName: resolveProviderDisplayName(providerId, providerConfig, providerCatalog),\n name: modelConfig.name ?? catalogModel?.name ?? modelId,\n qualifiedId: `${providerId}/${modelId}`,\n reasoning: modelConfig.reasoning ?? catalogModel?.capabilities.reasoning ?? false,\n variants: normalizeVariants(modelConfig.variants ?? catalogModel?.variants),\n };\n}\n\nfunction buildCatalogProviderModels(\n provider: OpenCodeProvider,\n availableModelIds: Set<string> | null,\n): OpenCodeConfiguredModel[] {\n return Object.values(provider.models)\n .filter((model) => isCatalogTextModel(model) && isModelAvailable(model.id, availableModelIds))\n .map((model) => ({\n id: model.id,\n providerID: provider.id,\n providerName: provider.name,\n name: model.name,\n qualifiedId: `${provider.id}/${model.id}`,\n reasoning: model.capabilities.reasoning,\n variants: normalizeVariants(model.variants),\n }));\n}\n\nfunction isTextModel(modelConfig: OpenCodeModelConfig, catalogModel: OpenCodeModel | undefined): boolean {\n if (modelConfig.modalities) {\n return modelConfig.modalities.input.includes(\"text\") && modelConfig.modalities.output.includes(\"text\");\n }\n\n if (catalogModel) {\n return catalogModel.capabilities.input.text && catalogModel.capabilities.output.text;\n }\n\n return true;\n}\n\nfunction isCatalogTextModel(catalogModel: OpenCodeModel): boolean {\n return catalogModel.capabilities.input.text && catalogModel.capabilities.output.text;\n}\n\nfunction isModelAvailable(modelId: string, availableModelIds: Set<string> | null): boolean {\n return availableModelIds === null || availableModelIds.has(modelId);\n}\n\nfunction resolveProviderDisplayName(\n providerId: string,\n providerConfig: OpenCodeProviderConfig,\n providerCatalog: OpenCodeProvider | undefined,\n): string {\n if (providerConfig.name) {\n return providerConfig.name;\n }\n\n const configuredBaseUrl = extractConfiguredBaseUrl(providerConfig);\n\n if (configuredBaseUrl) {\n const host = safeParseUrlHost(configuredBaseUrl);\n const label = providerCatalog?.name ?? providerId;\n\n return host ? `${label} (${host})` : label;\n }\n\n return providerCatalog?.name ?? providerId;\n}\n\nfunction extractConfiguredBaseUrl(providerConfig: OpenCodeProviderConfig): string | null {\n const options = providerConfig.options;\n\n if (!options || typeof options !== \"object\") {\n return null;\n }\n\n const baseUrl = \"baseURL\" in options ? options.baseURL : null;\n\n return typeof baseUrl === \"string\" && baseUrl.trim().length > 0\n ? baseUrl.trim()\n : null;\n}\n\nfunction safeParseUrlHost(value: string): string | null {\n try {\n return new URL(value).host || null;\n } catch {\n return null;\n }\n}\n\nfunction normalizeVariants(\n variants: OpenCodeModelConfig[\"variants\"] | OpenCodeModel[\"variants\"] | undefined,\n): Record<string, Record<string, unknown>> {\n if (!variants) {\n return {};\n }\n\n return Object.fromEntries(\n Object.entries(variants)\n .filter(([, config]) => !config?.disabled)\n .map(([variant, config]) => {\n const normalized = Object.fromEntries(\n Object.entries(config).filter(([key]) => key !== \"disabled\"),\n );\n\n return [variant, normalized];\n }),\n );\n}\n\nfunction extractTextFromParts(parts: Part[] | null | undefined): string {\n if (!Array.isArray(parts)) {\n return \"\";\n }\n\n return parts\n .filter((part): part is Extract<Part, { type: \"text\"; text: string }> => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\")\n .trim();\n}\n\nfunction buildPromptSessionResult(\n data: OpenCodePromptResponse,\n options: {\n emptyResponseText: string;\n finishedAt: number;\n startedAt: number;\n structured: boolean;\n },\n): PromptSessionResult {\n const assistantInfo = toAssistantMessage(data.info);\n const structuredPayload = extractStructuredPayload(assistantInfo);\n const bodyMd = options.structured ? extractStructuredMarkdown(structuredPayload) : null;\n const responseParts = Array.isArray(data.parts) ? data.parts : [];\n const fallbackText = extractTextFromParts(responseParts) || bodyMd || options.emptyResponseText;\n\n return {\n assistantError: assistantInfo?.error ?? null,\n bodyMd,\n fallbackText,\n info: assistantInfo,\n metrics: extractPromptMetrics(assistantInfo, options.startedAt, options.finishedAt),\n parts: responseParts,\n structured: structuredPayload ?? null,\n };\n}\n\nfunction shouldPollPromptMessage(\n data: OpenCodePromptResponse,\n structured: boolean,\n): boolean {\n const assistantInfo = toAssistantMessage(data.info);\n const bodyMd = structured ? extractStructuredMarkdown(extractStructuredPayload(assistantInfo)) : null;\n const responseParts = Array.isArray(data.parts) ? data.parts : [];\n const hasText = extractTextFromParts(responseParts).length > 0;\n const hasAssistantError = !!assistantInfo?.error;\n\n return !hasText && !bodyMd && !hasAssistantError;\n}\n\nfunction normalizePromptResponse(response: OpenCodePromptResponse | null | undefined): OpenCodePromptResponse {\n return {\n info: isPlainRecord(response?.info) ? response.info : null,\n parts: normalizePromptParts(response?.parts),\n };\n}\n\nfunction normalizePromptResponses(\n responses: OpenCodePromptResponse[] | null | undefined,\n): OpenCodePromptResponse[] | null {\n if (!Array.isArray(responses)) {\n return null;\n }\n\n return responses.map((response) => normalizePromptResponse(response));\n}\n\nfunction normalizePromptParts(parts: Part[] | null | undefined): Part[] {\n return Array.isArray(parts) ? parts : [];\n}\n\nfunction toAssistantMessage(message: unknown): OpenCodeAssistantMessage | null {\n if (!message || typeof message !== \"object\") {\n return null;\n }\n\n if (\"role\" in message && message.role !== \"assistant\") {\n return null;\n }\n\n const normalized: OpenCodeAssistantMessage = {};\n\n if (\"agent\" in message && typeof message.agent === \"string\" && message.agent.trim().length > 0) {\n normalized.agent = message.agent;\n }\n\n if (\"cost\" in message && typeof message.cost === \"number\" && Number.isFinite(message.cost)) {\n normalized.cost = message.cost;\n }\n\n const error = normalizeAssistantError(\"error\" in message ? message.error : undefined);\n\n if (error) {\n normalized.error = error;\n }\n\n if (\"finish\" in message && typeof message.finish === \"string\" && message.finish.trim().length > 0) {\n normalized.finish = message.finish;\n }\n\n if (\"id\" in message && typeof message.id === \"string\" && message.id.trim().length > 0) {\n normalized.id = message.id;\n }\n\n if (\"mode\" in message && typeof message.mode === \"string\" && message.mode.trim().length > 0) {\n normalized.mode = message.mode;\n }\n\n if (\"modelID\" in message && typeof message.modelID === \"string\" && message.modelID.trim().length > 0) {\n normalized.modelID = message.modelID;\n }\n\n if (\"parentID\" in message && typeof message.parentID === \"string\" && message.parentID.trim().length > 0) {\n normalized.parentID = message.parentID;\n }\n\n if (\"path\" in message && isPlainRecord(message.path)) {\n normalized.path = {\n ...(typeof message.path.cwd === \"string\" && message.path.cwd.trim().length > 0\n ? { cwd: message.path.cwd }\n : {}),\n ...(typeof message.path.root === \"string\" && message.path.root.trim().length > 0\n ? { root: message.path.root }\n : {}),\n };\n }\n\n if (\"providerID\" in message && typeof message.providerID === \"string\" && message.providerID.trim().length > 0) {\n normalized.providerID = message.providerID;\n }\n\n if (\"role\" in message && message.role === \"assistant\") {\n normalized.role = \"assistant\";\n }\n\n if (\"sessionID\" in message && typeof message.sessionID === \"string\" && message.sessionID.trim().length > 0) {\n normalized.sessionID = message.sessionID;\n }\n\n const structuredPayload = extractStructuredPayload(message);\n\n if (structuredPayload !== null) {\n normalized.structured = structuredPayload;\n }\n\n if (\"summary\" in message && typeof message.summary === \"boolean\") {\n normalized.summary = message.summary;\n }\n\n if (\"time\" in message && isPlainRecord(message.time)) {\n normalized.time = {\n ...(typeof message.time.created === \"number\" && Number.isFinite(message.time.created)\n ? { created: message.time.created }\n : {}),\n ...(typeof message.time.completed === \"number\" && Number.isFinite(message.time.completed)\n ? { completed: message.time.completed }\n : {}),\n };\n }\n\n if (\"tokens\" in message && isPlainRecord(message.tokens)) {\n normalized.tokens = {\n ...(typeof message.tokens.input === \"number\" && Number.isFinite(message.tokens.input)\n ? { input: message.tokens.input }\n : {}),\n ...(typeof message.tokens.output === \"number\" && Number.isFinite(message.tokens.output)\n ? { output: message.tokens.output }\n : {}),\n ...(typeof message.tokens.reasoning === \"number\" && Number.isFinite(message.tokens.reasoning)\n ? { reasoning: message.tokens.reasoning }\n : {}),\n ...(typeof message.tokens.total === \"number\" && Number.isFinite(message.tokens.total)\n ? { total: message.tokens.total }\n : {}),\n ...(isPlainRecord(message.tokens.cache)\n ? {\n cache: {\n ...(typeof message.tokens.cache.read === \"number\" && Number.isFinite(message.tokens.cache.read)\n ? { read: message.tokens.cache.read }\n : {}),\n ...(typeof message.tokens.cache.write === \"number\" && Number.isFinite(message.tokens.cache.write)\n ? { write: message.tokens.cache.write }\n : {}),\n },\n }\n : {}),\n };\n }\n\n if (\"variant\" in message && typeof message.variant === \"string\" && message.variant.trim().length > 0) {\n normalized.variant = message.variant;\n }\n\n return normalized;\n}\n\nfunction extractMessageId(message: unknown): string | null {\n if (!isPlainRecord(message)) {\n return null;\n }\n\n return typeof message.id === \"string\" && message.id.trim().length > 0\n ? message.id\n : null;\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction createOpenCodePromptTimeoutError(\n input: Omit<OpenCodePromptTimeoutErrorData, \"elapsedMs\" | \"message\"> & {\n elapsedMs?: number;\n message?: string;\n },\n): OpenCodePromptTimeoutError {\n return new OpenCodePromptTimeoutError({\n ...input,\n elapsedMs: input.elapsedMs ?? input.timeoutMs,\n message: input.message ?? \"The OpenCode host did not finish this request in time.\",\n });\n}\n\nfunction getPromptMessagePollDelayMs(attempt: number): number {\n return PROMPT_MESSAGE_POLL_INITIAL_DELAYS_MS[attempt] ?? PROMPT_MESSAGE_POLL_INTERVAL_MS;\n}\n\nfunction extractStructuredMarkdown(structured: unknown): string | null {\n const parsed = StructuredReplySchema.safeParse(structured);\n\n if (!parsed.success) {\n return null;\n }\n\n const bodyMd = parsed.data.body_md.replace(/\\r\\n?/g, \"\\n\").trim();\n\n return bodyMd.length > 0 ? bodyMd : null;\n}\n\nfunction extractPromptMetrics(\n info: OpenCodeAssistantMessage | null | undefined,\n startedAt: number,\n finishedAt: number,\n): PromptSessionMetrics {\n const createdAt = typeof info?.time?.created === \"number\" && Number.isFinite(info.time.created)\n ? info.time.created\n : null;\n const completedAt = typeof info?.time?.completed === \"number\" && Number.isFinite(info.time.completed)\n ? info.time.completed\n : null;\n const completedDuration = createdAt !== null && completedAt !== null\n ? completedAt - createdAt\n : null;\n const durationMs = completedDuration !== null && completedDuration >= 0\n ? completedDuration\n : Math.max(0, finishedAt - startedAt);\n\n return {\n durationMs,\n tokens: extractTokenMetrics(info),\n };\n}\n\nfunction extractTokenMetrics(info: OpenCodeAssistantMessage | null | undefined): PromptSessionTokenMetrics {\n if (!info?.tokens) {\n return {\n total: null,\n input: null,\n output: null,\n reasoning: null,\n cacheRead: null,\n cacheWrite: null,\n };\n }\n\n const input = coerceFiniteNumber(info.tokens.input);\n const output = coerceFiniteNumber(info.tokens.output);\n const reasoning = coerceFiniteNumber(info.tokens.reasoning);\n const cacheRead = coerceFiniteNumber(info.tokens.cache?.read);\n const cacheWrite = coerceFiniteNumber(info.tokens.cache?.write);\n const total = coerceFiniteNumber(info.tokens.total);\n const fallbackTotal = sumTokenMetrics(input, output, cacheRead, cacheWrite);\n\n return {\n total: total ?? fallbackTotal,\n input,\n output,\n reasoning,\n cacheRead,\n cacheWrite,\n };\n}\n\nfunction coerceFiniteNumber(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\n\nfunction sumTokenMetrics(...values: Array<number | null>): number | null {\n if (values.every((value) => value === null)) {\n return null;\n }\n\n const total = values.reduce<number>((sum, value) => sum + (value ?? 0), 0);\n\n return Number.isFinite(total) ? total : null;\n}\n\nfunction unwrapSdkData<T>(response: MaybeWrappedResponse<T>): T {\n if (response && typeof response === \"object\" && \"data\" in response) {\n return response.data;\n }\n\n return response;\n}\n\nfunction getRawSdkClient(client: OpenCodeSdkClientLike): RawSdkHttpClient | null {\n const withRawClient = client as OpenCodeSdkClientLike & {\n client?: RawSdkHttpClient;\n };\n\n return withRawClient.client ?? client._client ?? null;\n}\n\nfunction hasRawSdkMethod(\n client: OpenCodeSdkClientLike,\n method: keyof Pick<RawSdkHttpClient, \"delete\" | \"get\" | \"patch\" | \"post\">,\n): boolean {\n const rawClient = getRawSdkClient(client);\n\n return typeof rawClient?.[method] === \"function\";\n}\n\nfunction normalizeAssistantError(value: unknown): OpenCodeAssistantError | undefined {\n if (!isPlainRecord(value) || typeof value.name !== \"string\" || value.name.trim().length === 0) {\n return undefined;\n }\n\n return {\n ...value,\n name: value.name,\n ...(isPlainRecord(value.data)\n ? { data: value.data }\n : {}),\n } as OpenCodeAssistantError;\n}\n\nfunction extractStructuredPayload(message: unknown): unknown | null {\n if (!isPlainRecord(message)) {\n return null;\n }\n\n if (\"structured\" in message && message.structured !== undefined) {\n return message.structured;\n }\n\n if (\"structured_output\" in message && message.structured_output !== undefined) {\n return message.structured_output;\n }\n\n return null;\n}\n\ntype PromptResponseCandidateOptions = {\n initialMessageId: string | null;\n initialParentId: string | null;\n knownMessageIds: Set<string>;\n requestStartedAt: number | null;\n structured: boolean;\n};\n\nfunction selectPromptResponseCandidate(\n candidates: Array<OpenCodePromptResponse | null | undefined>,\n options: PromptResponseCandidateOptions,\n): OpenCodePromptResponse | null {\n const availableCandidates = candidates\n .filter((candidate): candidate is OpenCodePromptResponse => !!candidate)\n .filter((candidate) => toAssistantMessage(candidate.info) !== null);\n\n if (availableCandidates.length === 0) {\n return null;\n }\n\n return [...availableCandidates].sort((left, right) =>\n comparePromptResponseCandidates(left, right, options)\n )[0] ?? null;\n}\n\nfunction comparePromptResponseCandidates(\n left: OpenCodePromptResponse,\n right: OpenCodePromptResponse,\n options: PromptResponseCandidateOptions,\n): number {\n const leftRank = getPromptResponseCandidateRank(left, options);\n const rightRank = getPromptResponseCandidateRank(right, options);\n\n return Number(rightRank.isUsable) - Number(leftRank.isUsable)\n || Number(rightRank.isInitial) - Number(leftRank.isInitial)\n || Number(rightRank.sharesParent) - Number(leftRank.sharesParent)\n || Number(rightRank.isNewSinceRequestStart) - Number(leftRank.isNewSinceRequestStart)\n || rightRank.createdAt - leftRank.createdAt;\n}\n\nfunction getPromptResponseCandidateRank(\n message: OpenCodePromptResponse,\n options: PromptResponseCandidateOptions,\n): {\n createdAt: number;\n isInitial: boolean;\n isNewSinceRequestStart: boolean;\n isUsable: boolean;\n sharesParent: boolean;\n} {\n const assistant = toAssistantMessage(message.info);\n const id = assistant?.id ?? null;\n const createdAt = typeof assistant?.time?.created === \"number\" && Number.isFinite(assistant.time.created)\n ? assistant.time.created\n : 0;\n\n return {\n createdAt,\n isInitial: !!id && id === options.initialMessageId,\n isNewSinceRequestStart: isPromptResponseNewSinceRequestStart(\n id,\n createdAt,\n options.knownMessageIds,\n options.requestStartedAt,\n ),\n isUsable: !shouldPollPromptMessage(message, options.structured),\n sharesParent: !!assistant?.parentID && assistant.parentID === options.initialParentId,\n };\n}\n\nfunction resolvePromptCandidateStartTime(\n startedAt: number,\n initialMessage: OpenCodePromptResponse | null,\n): number | null {\n if (!initialMessage) {\n return null;\n }\n\n const initialCreatedAt = coerceFiniteNumber(toAssistantMessage(initialMessage.info)?.time?.created);\n\n if (initialCreatedAt === null) {\n return startedAt;\n }\n\n return areComparablePromptTimestamps(startedAt, initialCreatedAt)\n ? startedAt\n : initialCreatedAt;\n}\n\nfunction isPromptResponseNewSinceRequestStart(\n messageId: string | null,\n createdAt: number,\n knownMessageIds: Set<string>,\n requestStartedAt: number | null,\n): boolean {\n if (!messageId || knownMessageIds.has(messageId)) {\n return false;\n }\n\n if (requestStartedAt === null) {\n return true;\n }\n\n return createdAt >= requestStartedAt;\n}\n\nfunction areComparablePromptTimestamps(left: number, right: number): boolean {\n const epochThresholdMs = 1_000_000_000_000;\n\n return left >= epochThresholdMs && right >= epochThresholdMs;\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, any> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nasync function resolveProviderAvailability(\n config: OpenCodeConfig,\n fetchFn: typeof fetch,\n): Promise<OpenCodeProviderAvailability> {\n const configuredProviders = Object.entries(config.provider ?? {});\n const availabilityEntries = await Promise.all(\n configuredProviders.map(async ([providerId, providerConfig]) => [\n providerId,\n await fetchProviderAvailableModelIds(providerConfig, fetchFn),\n ] as const),\n );\n\n return new Map(availabilityEntries);\n}\n\nasync function fetchProviderAvailableModelIds(\n providerConfig: OpenCodeProviderConfig,\n fetchFn: typeof fetch,\n): Promise<Set<string> | null> {\n const baseUrl = extractConfiguredBaseUrl(providerConfig);\n const apiKey = extractConfiguredApiKey(providerConfig);\n\n if (!baseUrl || !apiKey) {\n return null;\n }\n\n try {\n const response = await fetchFn(\n buildModelsEndpointUrl(baseUrl),\n {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n Accept: \"application/json\",\n },\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (!response.ok) {\n return null;\n }\n\n const payload = await response.json();\n const modelIds = extractModelIdsFromPayload(payload);\n\n return modelIds ? new Set(modelIds) : null;\n } catch {\n return null;\n }\n}\n\nfunction extractConfiguredApiKey(providerConfig: OpenCodeProviderConfig): string | null {\n const options = providerConfig.options;\n\n if (!options || typeof options !== \"object\") {\n return null;\n }\n\n const apiKey = \"apiKey\" in options ? options.apiKey : null;\n\n return typeof apiKey === \"string\" && apiKey.trim().length > 0\n ? apiKey.trim()\n : null;\n}\n\nfunction buildModelsEndpointUrl(baseUrl: string): string {\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`;\n\n return new URL(\"models\", normalizedBaseUrl).toString();\n}\n\nfunction extractModelIdsFromPayload(payload: unknown): string[] | null {\n if (Array.isArray(payload)) {\n return payload\n .map(extractModelId)\n .filter((id): id is string => !!id);\n }\n\n if (payload && typeof payload === \"object\" && \"data\" in payload && Array.isArray(payload.data)) {\n return payload.data\n .map(extractModelId)\n .filter((id): id is string => !!id);\n }\n\n return null;\n}\n\nfunction extractModelId(value: unknown): string | null {\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value.trim();\n }\n\n if (value && typeof value === \"object\" && \"id\" in value) {\n const id = (value as { id?: unknown }).id;\n\n return typeof id === \"string\" && id.trim().length > 0\n ? id.trim()\n : null;\n }\n\n return null;\n}\n","import type { PendingChatAction } from \"../../domain/pending-action/pending-action.entity.js\";\nimport type { PendingPermissionApproval } from \"../../domain/permission/approval.entity.js\";\nimport type { ChatSessionBinding } from \"../../domain/session/session.entity.js\";\n\nexport interface OpencodeTbotState {\n version: 1;\n sessions: Record<string, ChatSessionBinding>;\n pendingActions: Record<string, PendingChatAction>;\n pendingPermissions: Record<string, PendingPermissionApproval>;\n}\n\nexport function createDefaultOpencodeTbotState(): OpencodeTbotState {\n return {\n version: 1,\n sessions: {},\n pendingActions: {},\n pendingPermissions: {},\n };\n}\n","import { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\n\nexport interface JsonStateStoreOptions<TState> {\n createDefaultState: () => TState;\n filePath: string;\n}\n\nexport class JsonStateStore<TState> {\n private readonly createDefaultState: () => TState;\n private readonly filePath: string;\n private statePromise: Promise<TState> | null = null;\n private writeQueue = Promise.resolve();\n\n constructor(options: JsonStateStoreOptions<TState>) {\n this.createDefaultState = options.createDefaultState;\n this.filePath = options.filePath;\n }\n\n async read(): Promise<TState> {\n const state = await this.loadState();\n\n return cloneState(state);\n }\n\n async update(mutator: (state: TState) => void): Promise<TState> {\n const nextStatePromise = this.writeQueue.then(async () => {\n const state = await this.loadState();\n const draft = cloneState(state);\n\n mutator(draft);\n await writeStateFile(this.filePath, draft);\n this.statePromise = Promise.resolve(draft);\n\n return cloneState(draft);\n });\n\n this.writeQueue = nextStatePromise.then(() => undefined, () => undefined);\n\n return nextStatePromise;\n }\n\n private async loadState(): Promise<TState> {\n if (!this.statePromise) {\n this.statePromise = readStateFile(this.filePath, this.createDefaultState);\n }\n\n return this.statePromise;\n }\n}\n\nasync function readStateFile<TState>(\n filePath: string,\n createDefaultState: () => TState,\n): Promise<TState> {\n try {\n const content = await readFile(filePath, \"utf8\");\n\n return JSON.parse(content) as TState;\n } catch (error) {\n if (isMissingFileError(error)) {\n return createDefaultState();\n }\n\n throw error;\n }\n}\n\nasync function writeStateFile<TState>(filePath: string, state: TState): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n\n const temporaryFilePath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;\n const serialized = `${JSON.stringify(state, null, 2)}\\n`;\n\n await writeFile(temporaryFilePath, serialized, \"utf8\");\n await rename(temporaryFilePath, filePath);\n}\n\nfunction cloneState<TState>(state: TState): TState {\n return JSON.parse(JSON.stringify(state)) as TState;\n}\n\nfunction isMissingFileError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n}\n","export interface DownloadTelegramFileInput {\n filePath: string;\n}\n\nexport interface DownloadTelegramFileResult {\n data: Uint8Array;\n mimeType: string | null;\n}\n\nexport interface TelegramFileClientLike {\n downloadFile(input: DownloadTelegramFileInput): Promise<DownloadTelegramFileResult>;\n}\n\nexport interface TelegramFileClientOptions {\n baseUrl?: string;\n apiRoot?: string;\n botToken: string;\n}\n\nexport class TelegramFileDownloadError extends Error {\n readonly data: { message: string };\n\n constructor(message: string) {\n super(message);\n this.name = \"TelegramFileDownloadError\";\n this.data = { message };\n }\n}\n\nexport class TelegramFileClient implements TelegramFileClientLike {\n private readonly baseUrl: string;\n private readonly fetchFn: typeof fetch;\n\n constructor(options: TelegramFileClientOptions, fetchFn: typeof fetch = fetch) {\n this.baseUrl = options.baseUrl ?? buildTelegramFileApiRoot(options.apiRoot, options.botToken);\n this.fetchFn = fetchFn;\n }\n\n async downloadFile(input: DownloadTelegramFileInput): Promise<DownloadTelegramFileResult> {\n const filePath = input.filePath.trim();\n\n if (!filePath) {\n throw new TelegramFileDownloadError(\"Telegram did not provide a downloadable file path.\");\n }\n\n let response: Response;\n\n try {\n response = await this.fetchFn(new URL(filePath, this.baseUrl));\n } catch (error) {\n throw new TelegramFileDownloadError(extractErrorMessage(error) ?? \"Failed to download the Telegram file.\");\n }\n\n if (!response.ok) {\n throw new TelegramFileDownloadError(await buildDownloadFailureMessage(response));\n }\n\n const data = new Uint8Array(await response.arrayBuffer());\n\n if (data.byteLength === 0) {\n throw new TelegramFileDownloadError(\"Telegram returned an empty file.\");\n }\n\n return {\n data,\n mimeType: response.headers.get(\"content-type\"),\n };\n }\n}\n\nexport function buildTelegramFileApiRoot(apiRoot: string | undefined, botToken: string): string {\n const normalizedApiRoot = (apiRoot ?? \"https://api.telegram.org\").trim().replace(/\\/+$/u, \"\");\n\n return `${normalizedApiRoot}/file/bot${botToken}/`;\n}\n\nasync function buildDownloadFailureMessage(response: Response): Promise<string> {\n const responseText = await safeReadResponseText(response);\n const normalizedText = responseText?.trim();\n\n return normalizedText\n ? `Telegram file download failed with status ${response.status}: ${normalizedText}`\n : `Telegram file download failed with status ${response.status}.`;\n}\n\nasync function safeReadResponseText(response: Response): Promise<string | null> {\n try {\n const text = await response.text();\n\n return text.trim().length > 0 ? text : null;\n } catch {\n return null;\n }\n}\n\nfunction extractErrorMessage(error: unknown): string | null {\n return error instanceof Error && error.message.trim().length > 0\n ? error.message.trim()\n : null;\n}\n","export interface ForegroundSessionTrackerLike {\n begin(chatId: number, sessionId: string): () => void;\n clear(sessionId: string): boolean;\n getActiveSessionId(chatId: number): string | null;\n isForeground(sessionId: string): boolean;\n listChatIds(sessionId: string): number[];\n}\n\nexport class ForegroundSessionTracker implements ForegroundSessionTrackerLike {\n private readonly chatStacks = new Map<number, string[]>();\n private readonly counts = new Map<string, number>();\n private readonly sessionChats = new Map<string, Map<number, number>>();\n\n begin(chatId: number, sessionId: string): () => void {\n const currentCount = this.counts.get(sessionId) ?? 0;\n\n this.counts.set(sessionId, currentCount + 1);\n this.incrementChat(chatId, sessionId);\n\n return () => {\n this.decrement(chatId, sessionId);\n };\n }\n\n clear(sessionId: string): boolean {\n const wasForeground = this.counts.has(sessionId);\n const chatCounts = this.sessionChats.get(sessionId);\n\n this.counts.delete(sessionId);\n this.sessionChats.delete(sessionId);\n\n for (const chatId of chatCounts?.keys() ?? []) {\n const stack = this.chatStacks.get(chatId);\n\n if (!stack) {\n continue;\n }\n\n const nextStack = stack.filter((trackedSessionId) => trackedSessionId !== sessionId);\n\n if (nextStack.length === 0) {\n this.chatStacks.delete(chatId);\n continue;\n }\n\n this.chatStacks.set(chatId, nextStack);\n }\n\n return wasForeground;\n }\n\n getActiveSessionId(chatId: number): string | null {\n const stack = this.chatStacks.get(chatId);\n\n return stack?.at(-1) ?? null;\n }\n\n isForeground(sessionId: string): boolean {\n return this.counts.has(sessionId);\n }\n\n listChatIds(sessionId: string): number[] {\n return [...(this.sessionChats.get(sessionId)?.keys() ?? [])];\n }\n\n private decrement(chatId: number, sessionId: string) {\n const currentCount = this.counts.get(sessionId);\n\n if (!currentCount || currentCount <= 1) {\n this.counts.delete(sessionId);\n } else {\n this.counts.set(sessionId, currentCount - 1);\n }\n\n this.decrementChat(chatId, sessionId);\n }\n\n private incrementChat(chatId: number, sessionId: string) {\n const stack = this.chatStacks.get(chatId) ?? [];\n\n stack.push(sessionId);\n this.chatStacks.set(chatId, stack);\n\n const chatCounts = this.sessionChats.get(sessionId) ?? new Map<number, number>();\n const currentCount = chatCounts.get(chatId) ?? 0;\n\n chatCounts.set(chatId, currentCount + 1);\n this.sessionChats.set(sessionId, chatCounts);\n }\n\n private decrementChat(chatId: number, sessionId: string) {\n const stack = this.chatStacks.get(chatId);\n\n if (stack) {\n const index = stack.lastIndexOf(sessionId);\n\n if (index >= 0) {\n stack.splice(index, 1);\n }\n\n if (stack.length === 0) {\n this.chatStacks.delete(chatId);\n } else {\n this.chatStacks.set(chatId, stack);\n }\n }\n\n const chatCounts = this.sessionChats.get(sessionId);\n\n if (!chatCounts) {\n return;\n }\n\n const currentCount = chatCounts.get(chatId) ?? 0;\n\n if (currentCount <= 1) {\n chatCounts.delete(chatId);\n } else {\n chatCounts.set(chatId, currentCount - 1);\n }\n\n if (chatCounts.size === 0) {\n this.sessionChats.delete(sessionId);\n } else {\n this.sessionChats.set(sessionId, chatCounts);\n }\n }\n}\n\nexport const NOOP_FOREGROUND_SESSION_TRACKER: ForegroundSessionTrackerLike = {\n begin() {\n return () => undefined;\n },\n clear() {\n return false;\n },\n getActiveSessionId() {\n return null;\n },\n isForeground() {\n return false;\n },\n listChatIds() {\n return [];\n },\n};\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { OpenCodeClientLike, OpenCodeSessionStatus } from \"../services/opencode/opencode.client.js\";\nimport {\n NOOP_FOREGROUND_SESSION_TRACKER,\n type ForegroundSessionTrackerLike,\n} from \"../services/session-activity/foreground-session-tracker.js\";\n\nexport interface AbortPromptResult {\n sessionId: string | null;\n status: \"aborted\" | \"no_session\" | \"not_running\";\n sessionStatus: OpenCodeSessionStatus | null;\n}\n\nexport class AbortPromptUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n private readonly foregroundSessionTracker: ForegroundSessionTrackerLike = NOOP_FOREGROUND_SESSION_TRACKER,\n ) { }\n\n async execute(input: { chatId: number }): Promise<AbortPromptResult> {\n const activeSessionId = this.foregroundSessionTracker.getActiveSessionId(input.chatId);\n const binding = activeSessionId\n ? null\n : await this.sessionRepo.getByChatId(input.chatId);\n\n const sessionId = activeSessionId ?? binding?.sessionId ?? null;\n\n if (!sessionId) {\n return {\n sessionId: null,\n status: \"no_session\",\n sessionStatus: null,\n };\n }\n\n const sessionStatus = (await this.opencodeClient.getSessionStatuses())[sessionId] ?? null;\n\n if (!sessionStatus || sessionStatus.type === \"idle\") {\n return {\n sessionId,\n status: \"not_running\",\n sessionStatus,\n };\n }\n\n const aborted = await this.opencodeClient.abortSession(sessionId);\n\n return {\n sessionId,\n status: aborted ? \"aborted\" : \"not_running\",\n sessionStatus,\n };\n }\n}\n","import type { ChatSessionBinding } from \"../domain/session/session.entity.js\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type {\n OpenCodeClientLike,\n OpenCodeProject,\n OpenCodeSession,\n} from \"../services/opencode/opencode.client.js\";\n\ninterface CreateAndBindCurrentProjectSessionInput {\n chatId: number;\n title?: string | null;\n binding?: ChatSessionBinding | null;\n}\n\nexport interface CreateAndBindCurrentProjectSessionResult {\n binding: ChatSessionBinding;\n project: OpenCodeProject;\n session: OpenCodeSession;\n}\n\nexport async function createAndBindCurrentProjectSession(\n sessionRepo: SessionRepository,\n opencodeClient: OpenCodeClientLike,\n input: CreateAndBindCurrentProjectSessionInput,\n): Promise<CreateAndBindCurrentProjectSessionResult> {\n const binding = input.binding ?? await sessionRepo.getByChatId(input.chatId);\n const project = await opencodeClient.getCurrentProject();\n const title = normalizeOptionalText(input.title);\n const session = await opencodeClient.createSessionForDirectory(project.worktree, title ?? undefined);\n const nextBinding: ChatSessionBinding = {\n chatId: input.chatId,\n sessionId: session.id,\n projectId: session.projectID,\n directory: session.directory,\n agentName: binding?.agentName ?? null,\n modelProviderId: binding?.modelProviderId ?? null,\n modelId: binding?.modelId ?? null,\n modelVariant: binding?.modelVariant ?? null,\n language: binding?.language ?? null,\n updatedAt: new Date().toISOString(),\n };\n\n await sessionRepo.setCurrent(nextBinding);\n\n return {\n binding: nextBinding,\n project,\n session,\n };\n}\n\nfunction normalizeOptionalText(value: string | null | undefined): string | null {\n const normalized = value?.trim();\n\n return normalized ? normalized : null;\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { AppLogger } from \"../infra/logger/index.js\";\nimport type {\n OpenCodeClientLike,\n OpenCodeSession,\n} from \"../services/opencode/opencode.client.js\";\nimport { createAndBindCurrentProjectSession } from \"./session-creation.js\";\n\nexport interface CreateSessionResult {\n session: OpenCodeSession;\n}\n\nexport class CreateSessionUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n private readonly logger: AppLogger,\n ) { }\n\n async execute(input: { chatId: number; title?: string | null }): Promise<CreateSessionResult> {\n const binding = await this.sessionRepo.getByChatId(input.chatId);\n const result = await createAndBindCurrentProjectSession(\n this.sessionRepo,\n this.opencodeClient,\n {\n chatId: input.chatId,\n title: input.title,\n binding,\n },\n );\n\n this.logger.info(\n {\n chatId: input.chatId,\n sessionId: result.session.id,\n projectId: result.session.projectID,\n directory: result.session.directory,\n title: input.title?.trim() || null,\n },\n \"session created\",\n );\n\n return {\n session: result.session,\n };\n }\n}\n","import type {\n OpenCodeClientLike,\n OpenCodeHealth,\n} from \"../services/opencode/opencode.client.js\";\n\nexport class GetHealthUseCase {\n constructor(private readonly opencodeClient: OpenCodeClientLike) { }\n\n async execute(): Promise<OpenCodeHealth> {\n return this.opencodeClient.getHealth();\n }\n}\n","import type { OpenCodeClientLike, OpenCodePath } from \"../services/opencode/opencode.client.js\";\n\nexport class GetPathUseCase {\n constructor(private readonly opencodeClient: OpenCodeClientLike) { }\n\n async execute(): Promise<OpenCodePath> {\n return this.opencodeClient.getPath();\n }\n}\n","import { readFile, stat } from \"node:fs/promises\";\nimport { isAbsolute, join } from \"node:path\";\nimport { parse, printParseErrorCode, type ParseError } from \"jsonc-parser\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type {\n OpenCodeHealth,\n OpenCodePath,\n OpenCodeSession,\n} from \"../services/opencode/opencode.client.js\";\nimport type { GetHealthUseCase } from \"./get-health.usecase.js\";\nimport type { GetPathUseCase } from \"./get-path.usecase.js\";\nimport type { ListLspResult, ListLspUseCase } from \"./list-lsp.usecase.js\";\nimport type { ListMcpResult, ListMcpUseCase } from \"./list-mcp.usecase.js\";\nimport type { ListSessionsUseCase } from \"./list-sessions.usecase.js\";\n\nexport interface StatusSectionOk<TData> {\n data: TData;\n status: \"ok\";\n}\n\nexport interface StatusSectionError {\n error: unknown;\n status: \"error\";\n}\n\nexport type StatusSectionResult<TData> = StatusSectionOk<TData> | StatusSectionError;\n\nexport interface ConfiguredPluginsResult {\n configFilePath: string;\n plugins: string[];\n}\n\nexport interface WorkspaceStatusResult {\n currentProject: string;\n currentSession: string | null;\n}\n\nexport interface GetStatusResult {\n health: StatusSectionResult<OpenCodeHealth>;\n lsp: StatusSectionResult<ListLspResult>;\n mcp: StatusSectionResult<ListMcpResult>;\n path: StatusSectionResult<OpenCodePath>;\n plugins: StatusSectionResult<ConfiguredPluginsResult>;\n workspace: StatusSectionResult<WorkspaceStatusResult>;\n}\n\nexport class GetStatusUseCase {\n constructor(\n private readonly getHealthUseCase: GetHealthUseCase,\n private readonly getPathUseCase: GetPathUseCase,\n private readonly listLspUseCase: ListLspUseCase,\n private readonly listMcpUseCase: ListMcpUseCase,\n private readonly listSessionsUseCase: ListSessionsUseCase,\n private readonly sessionRepo: SessionRepository,\n ) { }\n\n async execute(input: { chatId: number }): Promise<GetStatusResult> {\n const [health, path, lsp, mcp] = await Promise.allSettled([\n this.getHealthUseCase.execute(),\n this.getPathUseCase.execute(),\n this.listLspUseCase.execute({ chatId: input.chatId }),\n this.listMcpUseCase.execute({ chatId: input.chatId }),\n ]);\n const pathResult = mapSettledResult(path);\n const [plugins, workspace] = await Promise.all([\n loadConfiguredPluginsResult(pathResult),\n loadWorkspaceStatusResult(\n input.chatId,\n pathResult,\n this.listSessionsUseCase,\n this.sessionRepo,\n ),\n ]);\n\n return {\n health: mapSettledResult(health),\n path: pathResult,\n plugins,\n workspace,\n lsp: mapSettledResult(lsp),\n mcp: mapSettledResult(mcp),\n };\n }\n}\n\nfunction mapSettledResult<TData>(result: PromiseSettledResult<TData>): StatusSectionResult<TData> {\n if (result.status === \"fulfilled\") {\n return {\n data: result.value,\n status: \"ok\",\n };\n }\n\n return {\n error: result.reason,\n status: \"error\",\n };\n}\n\nasync function loadConfiguredPluginsResult(\n path: StatusSectionResult<OpenCodePath>,\n): Promise<StatusSectionResult<ConfiguredPluginsResult>> {\n if (path.status === \"error\") {\n return {\n error: path.error,\n status: \"error\",\n };\n }\n\n try {\n return {\n data: await loadConfiguredPlugins(path.data.config),\n status: \"ok\",\n };\n } catch (error) {\n return {\n error,\n status: \"error\",\n };\n }\n}\n\nasync function loadConfiguredPlugins(configFilePath: string): Promise<ConfiguredPluginsResult> {\n const resolvedConfigFilePath = await resolveOpenCodeConfigFilePath(configFilePath);\n let content: string;\n\n try {\n content = await readFile(resolvedConfigFilePath, \"utf8\");\n } catch (error) {\n if (isMissingFileError(error)) {\n return {\n configFilePath: resolvedConfigFilePath,\n plugins: [],\n };\n }\n\n throw error;\n }\n\n const parseErrors: ParseError[] = [];\n const parsed = parse(content, parseErrors, {\n allowTrailingComma: true,\n });\n\n if (parseErrors.length > 0) {\n const errorSummary = parseErrors\n .map((error) => printParseErrorCode(error.error))\n .join(\", \");\n\n throw new Error(`Failed to parse ${resolvedConfigFilePath}: ${errorSummary}`);\n }\n\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return {\n configFilePath: resolvedConfigFilePath,\n plugins: [],\n };\n }\n\n const pluginSpecs = Array.isArray((parsed as { plugin?: unknown }).plugin)\n ? (parsed as { plugin: unknown[] }).plugin\n : [];\n const plugins = [...new Set(\n pluginSpecs\n .filter((value): value is string => typeof value === \"string\")\n .map((value) => value.trim())\n .filter((value) => value.length > 0),\n )];\n\n return {\n configFilePath: resolvedConfigFilePath,\n plugins,\n };\n}\n\nasync function loadWorkspaceStatusResult(\n chatId: number,\n path: StatusSectionResult<OpenCodePath>,\n listSessionsUseCase: ListSessionsUseCase,\n sessionRepo: SessionRepository,\n): Promise<StatusSectionResult<WorkspaceStatusResult>> {\n if (path.status === \"error\") {\n return {\n error: path.error,\n status: \"error\",\n };\n }\n\n const binding = await sessionRepo.getByChatId(chatId);\n let currentProject = resolveWorkspaceProjectPath(path.data);\n let currentSession = binding?.sessionId ?? null;\n\n try {\n const sessions = await listSessionsUseCase.execute({ chatId });\n\n currentProject = resolveWorkspaceProjectPath(path.data, sessions.currentDirectory);\n currentSession = sessions.currentSessionId\n ? formatSessionStatusLabel(\n sessions.sessions.find((session) => session.id === sessions.currentSessionId) ?? null,\n sessions.currentSessionId,\n )\n : null;\n } catch {\n // Keep path/session binding fallbacks when session listing is unavailable.\n }\n\n return {\n data: {\n currentProject,\n currentSession,\n },\n status: \"ok\",\n };\n}\n\nfunction formatSessionStatusLabel(session: OpenCodeSession | null, fallbackId: string): string {\n if (!session) {\n return fallbackId;\n }\n\n const title = session.title.trim() || session.slug || session.id;\n\n return title === session.slug\n ? title\n : `${title} (${session.slug})`;\n}\n\nfunction resolveWorkspaceProjectPath(path: OpenCodePath, preferredPath?: string | null): string {\n const candidates = [\n preferredPath,\n path.worktree,\n path.directory,\n ];\n\n for (const candidate of candidates) {\n if (isUsableWorkspacePath(candidate)) {\n return candidate;\n }\n }\n\n return preferredPath ?? path.worktree ?? path.directory;\n}\n\nfunction isUsableWorkspacePath(value: string | null | undefined): value is string {\n if (typeof value !== \"string\") {\n return false;\n }\n\n const normalized = value.trim();\n\n return normalized.length > 0\n && normalized !== \"/\"\n && normalized !== \"\\\\\"\n && isAbsolute(normalized);\n}\n\nasync function resolveOpenCodeConfigFilePath(configPath: string): Promise<string> {\n try {\n const info = await stat(configPath);\n\n return info.isDirectory()\n ? join(configPath, \"opencode.json\")\n : configPath;\n } catch (error) {\n if (isMissingFileError(error)) {\n return configPath;\n }\n\n throw error;\n }\n}\n\nfunction isMissingFileError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n}\n","import type { OpenCodeAgent } from \"../services/opencode/opencode.client.js\";\n\nexport const DEFAULT_AGENT_NAME = \"build\";\n\nexport function isSelectableAgent(agent: OpenCodeAgent): boolean {\n return !agent.hidden && agent.mode !== \"subagent\";\n}\n\nexport function getSelectableAgents(agents: OpenCodeAgent[]): OpenCodeAgent[] {\n return agents.filter((agent) => isSelectableAgent(agent));\n}\n\nexport function resolveSelectedAgent(\n agents: OpenCodeAgent[],\n requestedAgentName: string | null | undefined,\n): OpenCodeAgent | null {\n const visibleAgents = getSelectableAgents(agents);\n\n if (requestedAgentName) {\n const requestedAgent = visibleAgents.find((agent) => agent.name === requestedAgentName);\n\n if (requestedAgent) {\n return requestedAgent;\n }\n }\n\n return visibleAgents.find((agent) => agent.name === DEFAULT_AGENT_NAME) ?? null;\n}\n","import type { ChatSessionBinding } from \"../domain/session/session.entity.js\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\n\ntype MutableBindingFields = Omit<ChatSessionBinding, \"chatId\" | \"updatedAt\">;\n\nexport async function persistBindingUpdate(\n sessionRepo: SessionRepository,\n binding: ChatSessionBinding,\n updates: Partial<MutableBindingFields>,\n): Promise<ChatSessionBinding> {\n const nextBinding: ChatSessionBinding = {\n ...binding,\n ...updates,\n updatedAt: new Date().toISOString(),\n };\n\n await sessionRepo.setCurrent(nextBinding);\n return nextBinding;\n}\n\nexport async function clearStoredAgentSelection(\n sessionRepo: SessionRepository,\n binding: ChatSessionBinding,\n): Promise<ChatSessionBinding> {\n return persistBindingUpdate(sessionRepo, binding, {\n agentName: null,\n });\n}\n\nexport async function clearStoredModelSelection(\n sessionRepo: SessionRepository,\n binding: ChatSessionBinding,\n): Promise<ChatSessionBinding> {\n return persistBindingUpdate(sessionRepo, binding, {\n modelProviderId: null,\n modelId: null,\n modelVariant: null,\n });\n}\n\nexport async function clearStoredModelVariant(\n sessionRepo: SessionRepository,\n binding: ChatSessionBinding,\n): Promise<ChatSessionBinding> {\n return persistBindingUpdate(sessionRepo, binding, {\n modelVariant: null,\n });\n}\n\nexport async function clearStoredSessionContext(\n sessionRepo: SessionRepository,\n binding: ChatSessionBinding,\n): Promise<ChatSessionBinding> {\n return persistBindingUpdate(sessionRepo, binding, {\n sessionId: null,\n projectId: null,\n directory: null,\n });\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type {\n OpenCodeAgent,\n OpenCodeClientLike,\n} from \"../services/opencode/opencode.client.js\";\nimport { getSelectableAgents, resolveSelectedAgent } from \"./agent-selection.js\";\nimport { clearStoredAgentSelection } from \"./binding-update.js\";\n\nexport interface ListAgentsResult {\n agents: OpenCodeAgent[];\n currentAgentName: string | null;\n}\n\nexport class ListAgentsUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n ) { }\n\n async execute(input: { chatId: number }): Promise<ListAgentsResult> {\n let [binding, agents] = await Promise.all([\n this.sessionRepo.getByChatId(input.chatId),\n this.opencodeClient.listAgents(),\n ]);\n const visibleAgents = getSelectableAgents(agents);\n const currentAgent = resolveSelectedAgent(visibleAgents, binding?.agentName);\n\n if (binding?.agentName && currentAgent?.name !== binding.agentName) {\n binding = await clearStoredAgentSelection(this.sessionRepo, binding);\n }\n\n return {\n agents: visibleAgents,\n currentAgentName: currentAgent?.name ?? null,\n };\n }\n}\n","import type { ChatSessionBinding } from \"../domain/session/session.entity.js\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type {\n OpenCodeClientLike,\n OpenCodeLspStatus,\n OpenCodeProject,\n} from \"../services/opencode/opencode.client.js\";\nimport { clearStoredSessionContext } from \"./binding-update.js\";\n\nexport interface ListLspResult {\n currentDirectory: string;\n statuses: OpenCodeLspStatus[];\n}\n\nexport class ListLspUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n ) { }\n\n async execute(input: { chatId: number }): Promise<ListLspResult> {\n let binding = await this.sessionRepo.getByChatId(input.chatId);\n\n if (binding?.projectId && binding.directory) {\n const projects = await this.opencodeClient.listProjects();\n\n if (!hasMatchingProject(projects, binding)) {\n binding = await clearStoredSessionContext(this.sessionRepo, binding);\n }\n }\n\n const projectContext = binding?.projectId && binding.directory\n ? {\n projectId: binding.projectId,\n directory: binding.directory,\n }\n : await this.getCurrentProjectContext();\n const statuses = await this.opencodeClient.listLspStatuses(projectContext.directory);\n\n return {\n currentDirectory: projectContext.directory,\n statuses: [...statuses].sort(compareLspStatuses),\n };\n }\n\n private async getCurrentProjectContext() {\n const currentProject = await this.opencodeClient.getCurrentProject();\n\n return {\n projectId: currentProject.id,\n directory: currentProject.worktree,\n };\n }\n}\n\nfunction compareLspStatuses(left: OpenCodeLspStatus, right: OpenCodeLspStatus): number {\n return left.name.localeCompare(right.name)\n || left.root.localeCompare(right.root)\n || left.id.localeCompare(right.id);\n}\n\nfunction hasMatchingProject(projects: OpenCodeProject[], binding: ChatSessionBinding): boolean {\n return projects.some((project) =>\n project.id === binding.projectId && project.worktree === binding.directory,\n );\n}\n","import type { ChatSessionBinding } from \"../domain/session/session.entity.js\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type {\n OpenCodeClientLike,\n OpenCodeMcpStatus,\n OpenCodeProject,\n} from \"../services/opencode/opencode.client.js\";\nimport { clearStoredSessionContext } from \"./binding-update.js\";\n\nexport interface ListMcpEntry {\n name: string;\n status: OpenCodeMcpStatus;\n}\n\nexport interface ListMcpResult {\n currentDirectory: string;\n statuses: ListMcpEntry[];\n}\n\nexport class ListMcpUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n ) { }\n\n async execute(input: { chatId: number }): Promise<ListMcpResult> {\n let binding = await this.sessionRepo.getByChatId(input.chatId);\n\n if (binding?.projectId && binding.directory) {\n const projects = await this.opencodeClient.listProjects();\n\n if (!hasMatchingProject(projects, binding)) {\n binding = await clearStoredSessionContext(this.sessionRepo, binding);\n }\n }\n\n const projectContext = binding?.projectId && binding.directory\n ? {\n projectId: binding.projectId,\n directory: binding.directory,\n }\n : await this.getCurrentProjectContext();\n const statuses = await this.opencodeClient.listMcpStatuses(projectContext.directory);\n\n return {\n currentDirectory: projectContext.directory,\n statuses: Object.entries(statuses)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([name, status]) => ({ name, status })),\n };\n }\n\n private async getCurrentProjectContext() {\n const currentProject = await this.opencodeClient.getCurrentProject();\n\n return {\n projectId: currentProject.id,\n directory: currentProject.worktree,\n };\n }\n}\n\nfunction hasMatchingProject(projects: OpenCodeProject[], binding: ChatSessionBinding): boolean {\n return projects.some((project) =>\n project.id === binding.projectId && project.worktree === binding.directory,\n );\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type {\n OpenCodeClientLike,\n OpenCodeConfiguredModel,\n} from \"../services/opencode/opencode.client.js\";\nimport {\n clearStoredModelSelection,\n clearStoredModelVariant,\n} from \"./binding-update.js\";\n\nexport interface ListModelsResult {\n models: OpenCodeConfiguredModel[];\n currentModelProviderId: string | null;\n currentModelId: string | null;\n currentModelVariant: string | null;\n}\n\nexport class ListModelsUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n ) { }\n\n async execute(input: { chatId: number }): Promise<ListModelsResult> {\n let [binding, models] = await Promise.all([\n this.sessionRepo.getByChatId(input.chatId),\n this.opencodeClient.listModels(),\n ]);\n\n if (binding?.modelProviderId && binding?.modelId) {\n const selectedModel = models.find((model) =>\n model.providerID === binding?.modelProviderId && model.id === binding?.modelId,\n );\n\n if (!selectedModel) {\n binding = await clearStoredModelSelection(this.sessionRepo, binding);\n } else if (binding.modelVariant && !(binding.modelVariant in selectedModel.variants)) {\n binding = await clearStoredModelVariant(this.sessionRepo, binding);\n }\n }\n\n return {\n models,\n currentModelProviderId: binding?.modelProviderId ?? null,\n currentModelId: binding?.modelId ?? null,\n currentModelVariant: binding?.modelVariant ?? null,\n };\n }\n}\n","import type { ChatSessionBinding } from \"../domain/session/session.entity.js\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type {\n OpenCodeClientLike,\n OpenCodeProject,\n OpenCodeSession,\n} from \"../services/opencode/opencode.client.js\";\nimport { clearStoredSessionContext } from \"./binding-update.js\";\n\nexport interface ListSessionsResult {\n sessions: OpenCodeSession[];\n currentProjectId: string;\n currentDirectory: string;\n currentSessionId: string | null;\n}\n\nexport class ListSessionsUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n ) { }\n\n async execute(input: { chatId: number }): Promise<ListSessionsResult> {\n let binding = await this.sessionRepo.getByChatId(input.chatId);\n const [sessions, projects] = await Promise.all([\n this.opencodeClient.listSessions(),\n binding?.projectId && binding.directory\n ? this.opencodeClient.listProjects()\n : Promise.resolve<OpenCodeProject[] | null>(null),\n ]);\n\n if (binding?.projectId && binding.directory && !hasMatchingProject(projects, binding)) {\n binding = await clearStoredSessionContext(this.sessionRepo, binding);\n }\n\n if (binding?.sessionId && binding.projectId && !hasMatchingSession(sessions, binding)) {\n binding = await clearStoredSessionContext(this.sessionRepo, binding);\n }\n\n const projectContext = binding?.projectId && binding.directory\n ? {\n projectId: binding.projectId,\n directory: binding.directory,\n }\n : await this.getCurrentProjectContext();\n\n return {\n sessions: sessions.filter((session) => session.projectID === projectContext.projectId),\n currentProjectId: projectContext.projectId,\n currentDirectory: projectContext.directory,\n currentSessionId: binding?.projectId === projectContext.projectId ? binding.sessionId : null,\n };\n }\n\n private async getCurrentProjectContext() {\n const currentProject = await this.opencodeClient.getCurrentProject();\n\n return {\n projectId: currentProject.id,\n directory: currentProject.worktree,\n };\n }\n}\n\nfunction hasMatchingProject(\n projects: OpenCodeProject[] | null,\n binding: ChatSessionBinding,\n): boolean {\n if (!projects) {\n return true;\n }\n\n return projects.some((project) =>\n project.id === binding.projectId && project.worktree === binding.directory,\n );\n}\n\nfunction hasMatchingSession(\n sessions: OpenCodeSession[],\n binding: ChatSessionBinding,\n): boolean {\n return sessions.some((session) =>\n session.id === binding.sessionId && session.projectID === binding.projectId,\n );\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { AppLogger } from \"../infra/logger/index.js\";\nimport type { OpenCodeClientLike, OpenCodeSession } from \"../services/opencode/opencode.client.js\";\n\nexport type RenameSessionResult =\n | {\n found: false;\n }\n | {\n found: true;\n session: OpenCodeSession;\n };\n\nexport class RenameSessionUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n private readonly logger: AppLogger,\n ) { }\n\n async execute(input: { chatId: number; sessionId: string; title: string }): Promise<RenameSessionResult> {\n const title = input.title.trim();\n\n if (!title) {\n throw new Error(\"Session title cannot be empty.\");\n }\n\n const [binding, sessions] = await Promise.all([\n this.sessionRepo.getByChatId(input.chatId),\n this.opencodeClient.listSessions(),\n ]);\n const currentProjectId = binding?.projectId ?? (await this.opencodeClient.getCurrentProject()).id;\n const session = sessions.find((item) => item.id === input.sessionId && item.projectID === currentProjectId);\n\n if (!session) {\n return { found: false };\n }\n\n const renamedSession = await this.opencodeClient.renameSession(session.id, title);\n\n this.logger.info(\n {\n chatId: input.chatId,\n sessionId: renamedSession.id,\n projectId: renamedSession.projectID,\n title: renamedSession.title,\n },\n \"session renamed\",\n );\n\n return {\n found: true,\n session: renamedSession,\n };\n }\n}\n","import type { ChatSessionBinding } from \"../domain/session/session.entity.js\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { AppLogger } from \"../infra/logger/index.js\";\nimport type {\n OpenCodeClientLike,\n PromptSessionFilePartInput,\n} from \"../services/opencode/opencode.client.js\";\nimport {\n NOOP_FOREGROUND_SESSION_TRACKER,\n type ForegroundSessionTrackerLike,\n} from \"../services/session-activity/foreground-session-tracker.js\";\nimport { resolveSelectedAgent } from \"./agent-selection.js\";\nimport {\n clearStoredAgentSelection,\n clearStoredModelSelection,\n clearStoredModelVariant,\n clearStoredSessionContext,\n} from \"./binding-update.js\";\nimport { createAndBindCurrentProjectSession } from \"./session-creation.js\";\n\ntype ActiveChatBinding = ChatSessionBinding & {\n directory: string;\n projectId: string;\n sessionId: string;\n};\n\nexport class SendPromptUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n private readonly logger: AppLogger,\n private readonly foregroundSessionTracker: ForegroundSessionTrackerLike = NOOP_FOREGROUND_SESSION_TRACKER,\n ) { }\n\n async execute(input: { chatId: number; text?: string | null; files?: PromptSessionFilePartInput[] }) {\n const files = input.files ?? [];\n const promptText = buildPromptText(input.text, files);\n\n if (!promptText && files.length === 0) {\n throw new Error(\"Prompt requires text or file attachments.\");\n }\n\n let binding = await this.sessionRepo.getByChatId(input.chatId);\n\n if (binding?.projectId && binding.directory) {\n const projects = await this.opencodeClient.listProjects();\n const selectedProject = projects.find((project) =>\n project.id === binding?.projectId && project.worktree === binding.directory,\n );\n\n if (!selectedProject) {\n binding = await this.clearInvalidSessionContext(\n input.chatId,\n binding,\n \"saved project is no longer available\",\n );\n }\n }\n\n if (binding?.sessionId && binding.projectId) {\n const sessions = await this.opencodeClient.listSessions();\n const selectedSession = sessions.find((session) =>\n session.id === binding?.sessionId && session.projectID === binding.projectId,\n );\n\n if (!selectedSession) {\n binding = await this.clearInvalidSessionContext(\n input.chatId,\n binding,\n \"saved session is no longer available\",\n );\n }\n }\n\n if (!binding || !binding.sessionId || !binding.projectId || !binding.directory) {\n const createdSession = await createAndBindCurrentProjectSession(\n this.sessionRepo,\n this.opencodeClient,\n {\n chatId: input.chatId,\n binding,\n },\n );\n binding = createdSession.binding;\n this.logger.info(\n {\n chatId: input.chatId,\n sessionId: createdSession.session.id,\n projectId: createdSession.session.projectID,\n directory: createdSession.session.directory,\n },\n \"session created\",\n );\n }\n\n if (binding?.modelProviderId && binding?.modelId) {\n const availableModels = await this.opencodeClient.listModels();\n const selectedModel = availableModels.find((model) =>\n model.providerID === binding?.modelProviderId && model.id === binding?.modelId,\n );\n\n if (!selectedModel) {\n binding = await clearStoredModelSelection(this.sessionRepo, binding);\n this.logger.warn?.(\n {\n chatId: input.chatId,\n },\n \"selected model is no longer available, falling back to OpenCode default\",\n );\n } else if (binding.modelVariant && !(binding.modelVariant in selectedModel.variants)) {\n binding = await clearStoredModelVariant(this.sessionRepo, binding);\n this.logger.warn?.(\n {\n chatId: input.chatId,\n providerId: selectedModel.providerID,\n modelId: selectedModel.id,\n },\n \"selected model variant is no longer available, falling back to default variant\",\n );\n }\n }\n\n if (!binding || !binding.sessionId || !binding.projectId) {\n throw new Error(\"Failed to initialize chat session.\");\n }\n\n let activeBinding = binding as ActiveChatBinding;\n const shouldIsolateImageTurn = hasImageFiles(files);\n const model = activeBinding.modelProviderId && activeBinding.modelId\n ? {\n providerID: activeBinding.modelProviderId,\n modelID: activeBinding.modelId,\n }\n : null;\n const availableAgents = await this.opencodeClient.listAgents();\n const selectedAgent = resolveSelectedAgent(availableAgents, activeBinding.agentName);\n\n if (activeBinding.agentName && selectedAgent?.name !== activeBinding.agentName) {\n activeBinding = await clearStoredAgentSelection(this.sessionRepo, activeBinding) as ActiveChatBinding;\n this.logger.warn?.(\n {\n chatId: input.chatId,\n },\n \"selected agent is no longer available, falling back to OpenCode default\",\n );\n }\n\n const temporarySessionId = shouldIsolateImageTurn\n ? await this.createTemporaryImageSession(input.chatId, activeBinding.sessionId)\n : null;\n const executionSessionId = temporarySessionId ?? activeBinding.sessionId;\n const endForegroundSession = this.foregroundSessionTracker.begin(input.chatId, executionSessionId);\n let result;\n\n try {\n result = await this.opencodeClient.promptSession({\n sessionId: executionSessionId,\n prompt: promptText,\n ...(files.length > 0 ? { files } : {}),\n ...(selectedAgent ? { agent: selectedAgent.name } : {}),\n structured: true,\n ...(model ? { model } : {}),\n ...(activeBinding.modelVariant ? { variant: activeBinding.modelVariant } : {}),\n });\n } finally {\n endForegroundSession();\n\n if (temporarySessionId) {\n await this.cleanupTemporaryImageSession(\n input.chatId,\n activeBinding.sessionId,\n temporarySessionId,\n );\n }\n }\n\n await this.sessionRepo.touch(input.chatId);\n\n return {\n assistantReply: result,\n sessionId: activeBinding.sessionId,\n projectId: activeBinding.projectId,\n };\n }\n\n private async clearInvalidSessionContext(\n chatId: number,\n binding: ChatSessionBinding,\n reason: string,\n ) {\n const nextBinding = await clearStoredSessionContext(this.sessionRepo, binding);\n this.logger.warn?.(\n {\n chatId,\n },\n `${reason}, falling back to the current OpenCode project`,\n );\n\n return nextBinding;\n }\n\n private async createTemporaryImageSession(chatId: number, sessionId: string): Promise<string> {\n const temporarySession = await this.opencodeClient.forkSession(sessionId);\n\n if (!temporarySession.id || temporarySession.id === sessionId) {\n throw new Error(\"OpenCode did not return a distinct temporary session for the image turn.\");\n }\n\n this.logger.info?.(\n {\n chatId,\n parentSessionId: sessionId,\n sessionId: temporarySession.id,\n },\n \"created temporary image session\",\n );\n\n return temporarySession.id;\n }\n\n private async cleanupTemporaryImageSession(\n chatId: number,\n parentSessionId: string,\n sessionId: string,\n ): Promise<void> {\n try {\n const deleted = await this.opencodeClient.deleteSession(sessionId);\n\n if (!deleted) {\n this.logger.warn?.(\n {\n chatId,\n parentSessionId,\n sessionId,\n },\n \"failed to delete temporary image session\",\n );\n }\n } catch (error) {\n this.logger.warn?.(\n {\n error,\n chatId,\n parentSessionId,\n sessionId,\n },\n \"failed to delete temporary image session\",\n );\n }\n }\n}\n\nfunction buildPromptText(\n text: string | null | undefined,\n files: PromptSessionFilePartInput[],\n): string {\n const trimmedText = text?.trim() ?? \"\";\n\n if (!files.some(isImageFile)) {\n return trimmedText;\n }\n\n const promptSections = [\n [\n \"System note for this turn:\",\n \"The user attached one or more images in this message and you can inspect those images directly.\",\n \"Ignore any earlier statements in this conversation claiming that you could not view images or attachments, because those may have come from a previous model or an earlier limitation.\",\n \"If an image is blurry or unreadable, say that it is blurry or unreadable instead of saying that you cannot view images.\",\n ].join(\" \"),\n ];\n\n if (trimmedText) {\n promptSections.push(trimmedText);\n }\n\n return promptSections.join(\"\\n\\n\");\n}\n\nfunction isImageFile(file: PromptSessionFilePartInput): boolean {\n return file.mime.trim().toLowerCase().startsWith(\"image/\");\n}\n\nfunction hasImageFiles(files: PromptSessionFilePartInput[]): boolean {\n return files.some(isImageFile);\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { AppLogger } from \"../infra/logger/index.js\";\nimport type {\n OpenCodeAgent,\n OpenCodeClientLike,\n} from \"../services/opencode/opencode.client.js\";\nimport { isSelectableAgent } from \"./agent-selection.js\";\n\nexport type SwitchAgentResult =\n | {\n found: false;\n }\n | {\n found: true;\n agent: OpenCodeAgent;\n };\n\nexport class SwitchAgentUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n private readonly logger: AppLogger,\n ) { }\n\n async execute(input: { chatId: number; agentName: string }): Promise<SwitchAgentResult> {\n const [binding, agents] = await Promise.all([\n this.sessionRepo.getByChatId(input.chatId),\n this.opencodeClient.listAgents(),\n ]);\n const agent = agents.find((item) => item.name === input.agentName && isSelectableAgent(item));\n\n if (!agent) {\n return { found: false };\n }\n\n await this.sessionRepo.setCurrent({\n chatId: input.chatId,\n sessionId: binding?.sessionId ?? null,\n projectId: binding?.projectId ?? null,\n directory: binding?.directory ?? null,\n agentName: agent.name,\n modelProviderId: binding?.modelProviderId ?? null,\n modelId: binding?.modelId ?? null,\n modelVariant: binding?.modelVariant ?? null,\n language: binding?.language ?? null,\n updatedAt: new Date().toISOString(),\n });\n\n this.logger.info(\n {\n chatId: input.chatId,\n agentName: agent.name,\n },\n \"agent switched\",\n );\n\n return {\n found: true,\n agent,\n };\n }\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { AppLogger } from \"../infra/logger/index.js\";\nimport type {\n OpenCodeClientLike,\n OpenCodeConfiguredModel,\n} from \"../services/opencode/opencode.client.js\";\n\nexport type SwitchModelResult =\n | {\n found: false;\n }\n | {\n found: true;\n model: OpenCodeConfiguredModel;\n variant: string | null;\n };\n\nexport class SwitchModelUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n private readonly logger: AppLogger,\n ) { }\n\n async execute(input: {\n chatId: number;\n providerId: string;\n modelId: string;\n variant?: string | null;\n }): Promise<SwitchModelResult> {\n const [binding, models] = await Promise.all([\n this.sessionRepo.getByChatId(input.chatId),\n this.opencodeClient.listModels(),\n ]);\n const model = models.find((item) =>\n item.providerID === input.providerId && item.id === input.modelId,\n );\n\n if (!model) {\n return { found: false };\n }\n\n const variant = input.variant ?? null;\n\n if (variant && !(variant in model.variants)) {\n return { found: false };\n }\n\n await this.sessionRepo.setCurrent({\n chatId: input.chatId,\n sessionId: binding?.sessionId ?? null,\n projectId: binding?.projectId ?? null,\n directory: binding?.directory ?? null,\n agentName: binding?.agentName ?? null,\n modelProviderId: model.providerID,\n modelId: model.id,\n modelVariant: variant,\n language: binding?.language ?? null,\n updatedAt: new Date().toISOString(),\n });\n\n this.logger.info(\n {\n chatId: input.chatId,\n providerId: model.providerID,\n modelId: model.id,\n variant,\n },\n \"model switched\",\n );\n\n return {\n found: true,\n model,\n variant,\n };\n }\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { AppLogger } from \"../infra/logger/index.js\";\nimport type { OpenCodeClientLike, OpenCodeSession } from \"../services/opencode/opencode.client.js\";\n\nexport type SwitchSessionResult =\n | {\n found: false;\n }\n | {\n found: true;\n session: OpenCodeSession;\n };\n\nexport class SwitchSessionUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n private readonly logger: AppLogger,\n ) { }\n\n async execute(input: { chatId: number; sessionId: string }): Promise<SwitchSessionResult> {\n const [binding, sessions] = await Promise.all([\n this.sessionRepo.getByChatId(input.chatId),\n this.opencodeClient.listSessions(),\n ]);\n const currentProjectId = binding?.projectId ?? (await this.opencodeClient.getCurrentProject()).id;\n const session = sessions.find((item) => item.id === input.sessionId && item.projectID === currentProjectId);\n\n if (!session) {\n return { found: false };\n }\n\n await this.sessionRepo.setCurrent({\n chatId: input.chatId,\n sessionId: session.id,\n projectId: session.projectID,\n directory: session.directory,\n agentName: binding?.agentName ?? null,\n modelProviderId: binding?.modelProviderId ?? null,\n modelId: binding?.modelId ?? null,\n modelVariant: binding?.modelVariant ?? null,\n language: binding?.language ?? null,\n updatedAt: new Date().toISOString(),\n });\n\n this.logger.info(\n {\n chatId: input.chatId,\n sessionId: session.id,\n projectId: session.projectID,\n directory: session.directory,\n },\n \"session switched\",\n );\n\n return {\n found: true,\n session,\n };\n }\n}\n","import type { PromptSessionFilePartInput } from \"../services/opencode/opencode.client.js\";\nimport {\n TelegramFileDownloadError,\n type TelegramFileClientLike,\n} from \"../services/telegram/telegram.client.js\";\n\nexport interface UploadFileInput {\n expectedType: \"image\";\n filePath: string;\n filename?: string | null;\n mimeType?: string | null;\n}\n\nexport class ImageFileDownloadError extends Error {\n readonly data: { message: string };\n\n constructor(message: string) {\n super(message);\n this.name = \"ImageFileDownloadError\";\n this.data = { message };\n }\n}\n\nexport class ImageMessageUnsupportedError extends Error {\n readonly data: { message: string };\n\n constructor(message: string) {\n super(message);\n this.name = \"ImageMessageUnsupportedError\";\n this.data = { message };\n }\n}\n\nexport class UploadFileUseCase {\n constructor(private readonly telegramFileClient: TelegramFileClientLike) { }\n\n async execute(input: UploadFileInput): Promise<PromptSessionFilePartInput> {\n if (input.expectedType !== \"image\") {\n throw new ImageMessageUnsupportedError(\"Only image uploads are supported.\");\n }\n\n let download;\n\n try {\n download = await this.telegramFileClient.downloadFile({\n filePath: input.filePath,\n });\n } catch (error) {\n if (error instanceof TelegramFileDownloadError) {\n throw new ImageFileDownloadError(error.message);\n }\n\n throw error;\n }\n\n const mimeType = normalizeMimeType(input.mimeType ?? download.mimeType);\n\n if (!mimeType.startsWith(\"image/\")) {\n throw new ImageMessageUnsupportedError(`Unsupported image MIME type: ${mimeType}`);\n }\n\n const filename = resolveFilename(input.filename, input.filePath, mimeType);\n\n return {\n filename,\n mime: mimeType,\n url: buildDataUrl(download.data, mimeType),\n };\n }\n}\n\nfunction buildDataUrl(data: Uint8Array, mimeType: string): string {\n const buffer = Buffer.from(data.buffer, data.byteOffset, data.byteLength);\n\n return `data:${mimeType};base64,${buffer.toString(\"base64\")}`;\n}\n\nfunction normalizeMimeType(value: string | null): string {\n const normalized = value?.trim().toLowerCase();\n\n return normalized && normalized.length > 0\n ? normalized\n : \"application/octet-stream\";\n}\n\nfunction resolveFilename(\n explicitFilename: string | null | undefined,\n filePath: string,\n mimeType: string,\n): string {\n const preferredFilename = explicitFilename?.trim();\n\n if (preferredFilename) {\n return preferredFilename;\n }\n\n const filePathFilename = filePath.split(\"/\").at(-1)?.trim();\n\n if (filePathFilename) {\n return filePathFilename;\n }\n\n return `telegram-upload${resolveExtension(mimeType)}`;\n}\n\nfunction resolveExtension(mimeType: string): string {\n switch (mimeType) {\n case \"image/jpeg\":\n return \".jpg\";\n case \"image/png\":\n return \".png\";\n case \"image/webp\":\n return \".webp\";\n case \"image/gif\":\n return \".gif\";\n default:\n return \"\";\n }\n}\n","import type { BotDependencies } from \"../bot/types.js\";\nimport type { PermissionApprovalRepository } from \"../domain/permission/approval.repository.js\";\nimport { createOpenCodeAppLogger } from \"../infra/logger/index.js\";\nimport { FilePendingActionRepository } from \"../repositories/pending-action.repo.js\";\nimport { FilePermissionApprovalRepository } from \"../repositories/permission-approval.repo.js\";\nimport { FileSessionRepository } from \"../repositories/session.repo.js\";\nimport {\n createOpenCodeClientFromSdkClient,\n type OpenCodeClientLike,\n type OpenCodeSdkClientLike,\n} from \"../services/opencode/opencode.client.js\";\nimport { createDefaultOpencodeTbotState } from \"../services/storage/bot-state.js\";\nimport { JsonStateStore } from \"../services/storage/json-state-store.js\";\nimport { TelegramFileClient } from \"../services/telegram/telegram.client.js\";\nimport { ForegroundSessionTracker } from \"../services/session-activity/foreground-session-tracker.js\";\nimport { AbortPromptUseCase } from \"../use-cases/abort-prompt.usecase.js\";\nimport { CreateSessionUseCase } from \"../use-cases/create-session.usecase.js\";\nimport { GetHealthUseCase } from \"../use-cases/get-health.usecase.js\";\nimport { GetPathUseCase } from \"../use-cases/get-path.usecase.js\";\nimport { GetStatusUseCase } from \"../use-cases/get-status.usecase.js\";\nimport { ListAgentsUseCase } from \"../use-cases/list-agents.usecase.js\";\nimport { ListLspUseCase } from \"../use-cases/list-lsp.usecase.js\";\nimport { ListMcpUseCase } from \"../use-cases/list-mcp.usecase.js\";\nimport { ListModelsUseCase } from \"../use-cases/list-models.usecase.js\";\nimport { ListSessionsUseCase } from \"../use-cases/list-sessions.usecase.js\";\nimport { RenameSessionUseCase } from \"../use-cases/rename-session.usecase.js\";\nimport { SendPromptUseCase } from \"../use-cases/send-prompt.usecase.js\";\nimport { SwitchAgentUseCase } from \"../use-cases/switch-agent.usecase.js\";\nimport { SwitchModelUseCase } from \"../use-cases/switch-model.usecase.js\";\nimport { SwitchSessionUseCase } from \"../use-cases/switch-session.usecase.js\";\nimport { UploadFileUseCase } from \"../use-cases/upload-file.usecase.js\";\nimport type { AppConfig } from \"./config.js\";\n\nexport interface AppContainer extends BotDependencies {\n opencodeClient: OpenCodeClientLike;\n foregroundSessionTracker: ForegroundSessionTracker;\n permissionApprovalRepo: PermissionApprovalRepository;\n dispose(): Promise<void>;\n}\n\nexport function createAppContainer(\n config: AppConfig,\n client: OpenCodeSdkClientLike,\n): AppContainer {\n const logger = createOpenCodeAppLogger(client, {\n level: config.logLevel,\n });\n\n return createContainer(config, createOpenCodeClientFromSdkClient(client), logger);\n}\n\nfunction createContainer(\n config: AppConfig,\n opencodeClient: OpenCodeClientLike,\n logger: BotDependencies[\"logger\"],\n): AppContainer {\n const stateStore = new JsonStateStore({\n filePath: config.stateFilePath,\n createDefaultState: createDefaultOpencodeTbotState,\n });\n const pendingActionRepo = new FilePendingActionRepository(stateStore);\n const permissionApprovalRepo = new FilePermissionApprovalRepository(stateStore);\n const sessionRepo = new FileSessionRepository(stateStore);\n const foregroundSessionTracker = new ForegroundSessionTracker();\n const telegramFileClient = new TelegramFileClient({\n botToken: config.telegramBotToken,\n apiRoot: config.telegramApiRoot,\n });\n const uploadFileUseCase = new UploadFileUseCase(telegramFileClient);\n const abortPromptUseCase = new AbortPromptUseCase(\n sessionRepo,\n opencodeClient,\n foregroundSessionTracker,\n );\n const createSessionUseCase = new CreateSessionUseCase(sessionRepo, opencodeClient, logger);\n const getHealthUseCase = new GetHealthUseCase(opencodeClient);\n const getPathUseCase = new GetPathUseCase(opencodeClient);\n const listAgentsUseCase = new ListAgentsUseCase(sessionRepo, opencodeClient);\n const listLspUseCase = new ListLspUseCase(sessionRepo, opencodeClient);\n const listMcpUseCase = new ListMcpUseCase(sessionRepo, opencodeClient);\n const listSessionsUseCase = new ListSessionsUseCase(sessionRepo, opencodeClient);\n const getStatusUseCase = new GetStatusUseCase(\n getHealthUseCase,\n getPathUseCase,\n listLspUseCase,\n listMcpUseCase,\n listSessionsUseCase,\n sessionRepo,\n );\n const listModelsUseCase = new ListModelsUseCase(sessionRepo, opencodeClient);\n const renameSessionUseCase = new RenameSessionUseCase(sessionRepo, opencodeClient, logger);\n const sendPromptUseCase = new SendPromptUseCase(\n sessionRepo,\n opencodeClient,\n logger,\n foregroundSessionTracker,\n );\n const switchAgentUseCase = new SwitchAgentUseCase(sessionRepo, opencodeClient, logger);\n const switchModelUseCase = new SwitchModelUseCase(sessionRepo, opencodeClient, logger);\n const switchSessionUseCase = new SwitchSessionUseCase(sessionRepo, opencodeClient, logger);\n let disposed = false;\n\n return {\n abortPromptUseCase,\n createSessionUseCase,\n getHealthUseCase,\n getPathUseCase,\n getStatusUseCase,\n listAgentsUseCase,\n listLspUseCase,\n listMcpUseCase,\n logger,\n listModelsUseCase,\n listSessionsUseCase,\n pendingActionRepo,\n renameSessionUseCase,\n sessionRepo,\n sendPromptUseCase,\n switchAgentUseCase,\n switchModelUseCase,\n switchSessionUseCase,\n telegramFileClient,\n uploadFileUseCase,\n opencodeClient,\n foregroundSessionTracker,\n permissionApprovalRepo,\n async dispose() {\n if (disposed) {\n return;\n }\n\n disposed = true;\n logger.info({ filePath: config.stateFilePath }, \"disposing telegram bot container\");\n await logger.flush();\n },\n };\n}\n","import {\n loadAppConfig,\n type LoadAppConfigOptions,\n type AppConfig,\n type PluginConfigSource,\n} from \"./config.js\";\nimport {\n createAppContainer,\n type AppContainer,\n} from \"./container.js\";\nimport type { OpenCodeSdkClientLike } from \"../services/opencode/opencode.client.js\";\n\nexport function bootstrapPluginApp(\n client: OpenCodeSdkClientLike,\n configSource: PluginConfigSource | undefined = {},\n options: LoadAppConfigOptions = {},\n): { config: AppConfig; container: AppContainer } {\n const config = loadAppConfig(configSource, options);\n\n return {\n config,\n container: createAppContainer(config, client),\n };\n}\n","import type { PermissionApprovalReply } from \"../../domain/permission/approval.entity.js\";\nimport type { PermissionApprovalCallbackData } from \"../../bot/types.js\";\nimport type { OpenCodePermissionRequest } from \"../opencode/opencode.client.js\";\n\nexport function buildPermissionApprovalMessage(\n request: OpenCodePermissionRequest,\n): string {\n const patterns = request.patterns.length > 0\n ? request.patterns.map((pattern) => `- ${pattern}`).join(\"\\n\")\n : \"- (none)\";\n\n return [\n \"*Permission Request*\",\n \"\",\n `Permission: \\`${escapeMarkdownV2(request.permission)}\\``,\n `Session: \\`${escapeMarkdownV2(request.sessionID)}\\``,\n \"\",\n \"Patterns:\",\n patterns,\n ].join(\"\\n\");\n}\n\nexport function buildPermissionApprovalResolvedMessage(\n requestId: string,\n reply: PermissionApprovalReply,\n): string {\n return [\n \"Permission request resolved.\",\n \"\",\n `Request: ${requestId}`,\n `Reply: ${reply}`,\n ].join(\"\\n\");\n}\n\nexport function buildPermissionApprovalKeyboard(requestId: string) {\n return {\n inline_keyboard: [[\n {\n text: \"Allow once\",\n callback_data: buildPermissionApprovalCallbackData(\"once\", requestId),\n },\n {\n text: \"Always allow\",\n callback_data: buildPermissionApprovalCallbackData(\"always\", requestId),\n },\n {\n text: \"Reject\",\n callback_data: buildPermissionApprovalCallbackData(\"reject\", requestId),\n },\n ]],\n };\n}\n\nexport function buildPermissionApprovalCallbackData(\n reply: PermissionApprovalReply,\n requestId: string,\n): PermissionApprovalCallbackData {\n return `permission:${reply}:${requestId}`;\n}\n\nexport function parsePermissionApprovalCallbackData(data: string): {\n reply: PermissionApprovalReply;\n requestId: string;\n} | null {\n if (!data.startsWith(\"permission:\")) {\n return null;\n }\n\n const [, reply, requestId] = data.split(\":\", 3);\n\n if (!requestId || !isPermissionApprovalReply(reply)) {\n return null;\n }\n\n return {\n reply,\n requestId,\n };\n}\n\nfunction isPermissionApprovalReply(value: string): value is PermissionApprovalReply {\n return value === \"once\" || value === \"always\" || value === \"reject\";\n}\n\nfunction escapeMarkdownV2(value: string): string {\n return value.replaceAll(/([_*\\[\\]()~`>#+\\-=|{}.!\\\\])/g, \"\\\\$1\");\n}\n","import type { PendingPermissionApproval, PermissionApprovalReply } from \"../domain/permission/approval.entity.js\";\nimport type { OpenCodePermissionRequest } from \"../services/opencode/opencode.client.js\";\nimport {\n buildPermissionApprovalKeyboard,\n buildPermissionApprovalMessage,\n buildPermissionApprovalResolvedMessage,\n} from \"../services/permission/telegram-approval.js\";\nimport type { TelegramBotRuntime } from \"./runtime.js\";\n\ntype TelegramBotPluginEvent =\n | {\n type: \"permission.asked\";\n properties: OpenCodePermissionRequest;\n }\n | {\n type: \"permission.replied\";\n properties: {\n sessionID: string;\n requestID: string;\n reply: PermissionApprovalReply;\n };\n }\n | {\n type: \"session.error\";\n properties: {\n sessionID?: string;\n error?: {\n name?: string;\n data?: {\n message?: string;\n };\n };\n };\n }\n | {\n type: \"session.idle\";\n properties: {\n sessionID: string;\n };\n };\n\nexport async function handleTelegramBotPluginEvent(\n runtime: TelegramBotRuntime,\n event: TelegramBotPluginEvent,\n): Promise<void> {\n switch (event.type) {\n case \"permission.asked\":\n await handlePermissionAsked(runtime, event.properties);\n return;\n case \"permission.replied\":\n await handlePermissionReplied(runtime, event.properties.requestID, event.properties.reply);\n return;\n case \"session.error\":\n await handleSessionError(runtime, event.properties.sessionID, event.properties.error);\n return;\n case \"session.idle\":\n await handleSessionIdle(runtime, event.properties.sessionID);\n return;\n default:\n return;\n }\n}\n\nasync function handlePermissionAsked(\n runtime: TelegramBotRuntime,\n request: OpenCodePermissionRequest,\n): Promise<void> {\n const bindings = await runtime.container.sessionRepo.listBySessionId(request.sessionID);\n const chatIds = new Set([\n ...bindings.map((binding) => binding.chatId),\n ...runtime.container.foregroundSessionTracker.listChatIds(request.sessionID),\n ]);\n const approvals = await runtime.container.permissionApprovalRepo.listByRequestId(request.id);\n const approvedChatIds = new Set(approvals.map((approval) => approval.chatId));\n\n for (const chatId of chatIds) {\n if (approvedChatIds.has(chatId)) {\n continue;\n }\n\n try {\n const message = await runtime.bot.api.sendMessage(\n chatId,\n buildPermissionApprovalMessage(request),\n {\n parse_mode: \"MarkdownV2\",\n reply_markup: buildPermissionApprovalKeyboard(request.id),\n },\n );\n\n await runtime.container.permissionApprovalRepo.set({\n requestId: request.id,\n sessionId: request.sessionID,\n chatId,\n messageId: message.message_id,\n status: \"pending\",\n updatedAt: new Date().toISOString(),\n });\n } catch (error) {\n runtime.container.logger.error(\n {\n error,\n chatId,\n requestId: request.id,\n },\n \"failed to deliver permission request to Telegram\",\n );\n }\n }\n}\n\nasync function handlePermissionReplied(\n runtime: TelegramBotRuntime,\n requestId: string,\n reply: PermissionApprovalReply,\n): Promise<void> {\n const approvals = await runtime.container.permissionApprovalRepo.listByRequestId(requestId);\n\n await Promise.all(\n approvals.map(async (approval) => {\n try {\n await runtime.bot.api.editMessageText(\n approval.chatId,\n approval.messageId,\n buildPermissionApprovalResolvedMessage(requestId, reply),\n );\n } catch (error) {\n runtime.container.logger.warn(\n {\n error,\n chatId: approval.chatId,\n requestId,\n },\n \"failed to update Telegram permission message\",\n );\n }\n\n await runtime.container.permissionApprovalRepo.set(toResolvedApproval(approval, reply));\n }),\n );\n}\n\nasync function handleSessionError(\n runtime: TelegramBotRuntime,\n sessionId: string | undefined,\n error: {\n name?: string;\n data?: {\n message?: string;\n };\n } | undefined,\n): Promise<void> {\n if (!sessionId) {\n runtime.container.logger.error({ error }, \"session error received without a session id\");\n return;\n }\n\n const wasForeground = runtime.container.foregroundSessionTracker.clear(sessionId);\n\n if (wasForeground) {\n runtime.container.logger.warn({ error, sessionId }, \"session error suppressed for foreground Telegram session\");\n return;\n }\n\n const message = error?.data?.message?.trim()\n || error?.name?.trim()\n || \"Unknown session error.\";\n\n await notifyBoundChats(runtime, sessionId, `Session failed.\\n\\nSession: ${sessionId}\\nError: ${message}`);\n}\n\nasync function handleSessionIdle(\n runtime: TelegramBotRuntime,\n sessionId: string,\n): Promise<void> {\n const wasForeground = runtime.container.foregroundSessionTracker.clear(sessionId);\n\n if (wasForeground) {\n runtime.container.logger.info({ sessionId }, \"session idle notification suppressed for foreground Telegram session\");\n return;\n }\n\n await notifyBoundChats(runtime, sessionId, `Session finished.\\n\\nSession: ${sessionId}`);\n}\n\nasync function notifyBoundChats(\n runtime: TelegramBotRuntime,\n sessionId: string,\n text: string,\n): Promise<void> {\n const bindings = await runtime.container.sessionRepo.listBySessionId(sessionId);\n const chatIds = [...new Set(bindings.map((binding) => binding.chatId))];\n\n await Promise.all(\n chatIds.map(async (chatId) => {\n try {\n await runtime.bot.api.sendMessage(chatId, text);\n } catch (error) {\n runtime.container.logger.warn(\n { error, chatId, sessionId },\n \"failed to notify Telegram chat about session event\",\n );\n }\n }),\n );\n}\n\nfunction toResolvedApproval(\n approval: PendingPermissionApproval,\n reply: PermissionApprovalReply,\n): PendingPermissionApproval {\n return {\n ...approval,\n status: reply,\n updatedAt: new Date().toISOString(),\n };\n}\n","export type BotLanguage = \"en\" | \"zh-CN\" | \"ja\";\n\nexport interface BotCopy {\n locale: BotLanguage;\n commands: {\n start: string;\n status: string;\n new: string;\n agents: string;\n sessions: string;\n cancel: string;\n model: string;\n language: string;\n };\n start: {\n lines: string[];\n };\n systemStatus: {\n title: string;\n };\n common: {\n notSelected: string;\n openCodeDefault: string;\n previousPage: string;\n nextPage: string;\n page(currentPage: number, totalPages: number): string;\n };\n status: {\n processing: string;\n alreadyProcessing: string;\n };\n prompt: {\n emptyResponse: string;\n };\n replyMetrics: {\n durationLabel: string;\n tokensLabel: string;\n totalLabel: string;\n inputLabel: string;\n outputLabel: string;\n reasoningLabel: string;\n cacheReadLabel: string;\n cacheWriteLabel: string;\n notAvailable: string;\n };\n abort: {\n noSession: string;\n notRunning: string;\n aborted: string;\n };\n errors: {\n unexpected: string;\n providerAuth: string;\n requestAborted: string;\n promptTimeout: string;\n structuredOutput: string;\n voiceUnsupported: string;\n imageDownload: string;\n imageUnsupported: string;\n outputLength: string;\n contextOverflow: string;\n providerRequest: string;\n notFound: string;\n badRequest: string;\n causeLabel: string;\n retryableLabel: string;\n statusCodeLabel: string;\n };\n health: {\n title: string;\n status(healthy: boolean): string;\n version(version: string): string;\n };\n path: {\n title: string;\n home(path: string): string;\n state(path: string): string;\n config(path: string): string;\n worktree(path: string): string;\n directory(path: string): string;\n };\n sessions: {\n none: string;\n title: string;\n actionTitle: string;\n chooseAction: string;\n currentProject(worktree: string): string;\n currentSession(session: string): string;\n selectedSession(session: string): string;\n switched: string;\n created: string;\n renamed: string;\n renameCancelled: string;\n renameEmpty: string;\n renameExpired: string;\n renamePendingInput: string;\n renamePrompt(session: string): string;\n switchAction: string;\n renameAction: string;\n backToList: string;\n expired: string;\n };\n lsp: {\n none: string;\n title: string;\n currentProject(worktree: string): string;\n connected: string;\n error: string;\n };\n mcp: {\n none: string;\n title: string;\n currentProject(worktree: string): string;\n connected: string;\n disabled: string;\n needsAuth: string;\n failed(error: string): string;\n needsClientRegistration(error: string): string;\n };\n agents: {\n none: string;\n title: string;\n current(agent: string): string;\n switched: string;\n expired: string;\n };\n models: {\n none: string;\n title: string;\n configuredOnly: string;\n current(model: string): string;\n switched: string;\n currentReasoningLevel(variant: string): string;\n reasoningLevel(variant: string): string;\n noReasoningLevels: string;\n reasoningLevelsTitle: string;\n model(model: string): string;\n modelNumber(modelIndex: number): string;\n expired: string;\n reasoningLevelExpired: string;\n defaultReasoningLevel: string;\n };\n language: {\n title: string;\n choose: string;\n current(label: string): string;\n switched: string;\n expired: string;\n labels: Record<BotLanguage, string>;\n };\n}\n\nexport const DEFAULT_BOT_LANGUAGE: BotLanguage = \"en\";\nexport const SUPPORTED_BOT_LANGUAGES: readonly BotLanguage[] = [\"en\", \"zh-CN\", \"ja\"];\n\nconst EN_BOT_COPY: BotCopy = {\n locale: \"en\",\n commands: {\n start: \"Show welcome and quick start\",\n status: \"Show system status\",\n new: \"Create a new session\",\n agents: \"Show and switch agents\",\n sessions: \"Show and switch sessions\",\n cancel: \"Cancel rename or abort running request\",\n model: \"Show and switch models\",\n language: \"Show and switch language\",\n },\n start: {\n lines: [\n \"# Welcome to opencode-tbot\",\n \"\",\n \"Talk to your OpenCode server from Telegram.\",\n \"\",\n \"## Quick start\",\n \"1. Run `/status` to confirm the server is ready.\",\n \"2. Run `/new [title]` to create a fresh session.\",\n \"\",\n \"Send a text or image message directly.\",\n ],\n },\n systemStatus: {\n title: \"System Status\",\n },\n common: {\n notSelected: \"Not selected\",\n openCodeDefault: \"Not selected (using OpenCode default)\",\n previousPage: \"Previous\",\n nextPage: \"Next\",\n page(currentPage: number, totalPages: number) {\n return `Page ${currentPage}/${totalPages}`;\n },\n },\n status: {\n processing: \"Processing...\",\n alreadyProcessing: \"Another request is still running. Wait for it to finish before sending a new prompt.\",\n },\n prompt: {\n emptyResponse: \"OpenCode returned empty response.\",\n },\n replyMetrics: {\n durationLabel: \"Duration\",\n tokensLabel: \"Tokens\",\n totalLabel: \"total\",\n inputLabel: \"input\",\n outputLabel: \"output\",\n reasoningLabel: \"reasoning\",\n cacheReadLabel: \"cache.read\",\n cacheWriteLabel: \"cache.write\",\n notAvailable: \"n/a\",\n },\n abort: {\n noSession: \"No active session is bound to this chat yet.\",\n notRunning: \"No request is currently running for the current session.\",\n aborted: \"Abort signal sent to the current session.\",\n },\n errors: {\n unexpected: \"Unexpected error.\",\n providerAuth: \"Provider authentication failed.\",\n requestAborted: \"Request was aborted.\",\n promptTimeout: \"OpenCode request timed out.\",\n structuredOutput: \"Structured output validation failed.\",\n voiceUnsupported: \"Voice messages are not supported. Send text or an image instead.\",\n imageDownload: \"Failed to download the Telegram image file.\",\n imageUnsupported: \"Image file is too large or unsupported.\",\n outputLength: \"Reply hit the model output limit.\",\n contextOverflow: \"Conversation exceeded the model context window.\",\n providerRequest: \"Provider request failed.\",\n notFound: \"Requested resource was not found.\",\n badRequest: \"Request was rejected by OpenCode.\",\n causeLabel: \"Cause\",\n retryableLabel: \"retryable\",\n statusCodeLabel: \"status\",\n },\n health: {\n title: \"Server Health\",\n status(healthy: boolean) {\n return `Status: ${healthy ? \"healthy\" : \"unhealthy\"}`;\n },\n version(version: string) {\n return `Version: ${version}`;\n },\n },\n path: {\n title: \"Current Paths\",\n home(path: string) {\n return `Home: ${path}`;\n },\n state(path: string) {\n return `State: ${path}`;\n },\n config(path: string) {\n return `Config: ${path}`;\n },\n worktree(path: string) {\n return `Worktree: ${path}`;\n },\n directory(path: string) {\n return `Current working directory: ${path}`;\n },\n },\n sessions: {\n none: \"No sessions available in the current project.\",\n title: \"Session List\",\n actionTitle: \"Session Actions\",\n chooseAction: \"Choose an action for this session.\",\n currentProject(worktree: string) {\n return `Current project: ${worktree}`;\n },\n currentSession(session: string) {\n return `Current session: ${session}`;\n },\n selectedSession(session: string) {\n return `Selected session: ${session}`;\n },\n switched: \"Session switched.\",\n created: \"Session created.\",\n renamed: \"Session renamed.\",\n renameCancelled: \"Session rename cancelled.\",\n renameEmpty: \"Session name cannot be empty. Send a new name or /cancel.\",\n renameExpired: \"The session is no longer available. Run /sessions again.\",\n renamePendingInput: \"Waiting for the new session name. Send plain text or /cancel.\",\n renamePrompt(session: string) {\n return [\n `Rename session: ${session}`,\n \"Send the new session name as your next text message.\",\n \"Send /cancel to cancel.\",\n ].join(\"\\n\");\n },\n switchAction: \"Switch\",\n renameAction: \"Rename\",\n backToList: \"Back to list\",\n expired: \"The session is no longer available. Run /sessions again.\",\n },\n lsp: {\n none: \"No LSP servers detected for the current project.\",\n title: \"LSP Servers\",\n currentProject(worktree: string) {\n return `Current project: ${worktree}`;\n },\n connected: \"connected\",\n error: \"error\",\n },\n mcp: {\n none: \"No MCP servers configured for the current project.\",\n title: \"MCP Servers\",\n currentProject(worktree: string) {\n return `Current project: ${worktree}`;\n },\n connected: \"connected\",\n disabled: \"disabled\",\n needsAuth: \"needs auth\",\n failed(error: string) {\n return `failed: ${error}`;\n },\n needsClientRegistration(error: string) {\n return `needs client registration: ${error}`;\n },\n },\n agents: {\n none: \"No agents available.\",\n title: \"Agent List\",\n current(agent: string) {\n return `Current agent: ${agent}`;\n },\n switched: \"Agent switched.\",\n expired: \"The agent is no longer available. Run /agents again.\",\n },\n models: {\n none: \"No models available.\",\n title: \"Model List\",\n configuredOnly: \"Only models currently available in OpenCode and connected providers are shown.\",\n current(model: string) {\n return `Current model: ${model}`;\n },\n switched: \"Model switched.\",\n currentReasoningLevel(variant: string) {\n return `Current reasoning level: ${variant}`;\n },\n reasoningLevel(variant: string) {\n return `Reasoning level: ${variant}`;\n },\n noReasoningLevels: \"This model has no selectable reasoning levels.\",\n reasoningLevelsTitle: \"Reasoning Levels\",\n model(model: string) {\n return `Model: ${model}`;\n },\n modelNumber(modelIndex: number) {\n return `Model number: ${modelIndex}`;\n },\n expired: \"The model is no longer available. Run /model again.\",\n reasoningLevelExpired: \"The reasoning level is no longer available. Run /model again.\",\n defaultReasoningLevel: \"default\",\n },\n language: {\n title: \"Language\",\n choose: \"Choose the display language for bot menus and replies.\",\n current(label: string) {\n return `Current language: ${label}`;\n },\n switched: \"Language switched.\",\n expired: \"The language option is no longer available. Run /language again.\",\n labels: {\n en: \"English\",\n \"zh-CN\": \"简体中文\",\n ja: \"日本語\",\n },\n },\n};\n\nconst ZH_CN_BOT_COPY: BotCopy = {\n locale: \"zh-CN\",\n commands: {\n start: \"查看欢迎信息与快速开始\",\n status: \"查看系统状态\",\n new: \"新建会话\",\n agents: \"查看并切换 Agent\",\n sessions: \"查看并切换会话\",\n cancel: \"取消重命名或中止当前请求\",\n model: \"查看并切换模型\",\n language: \"查看并切换语言\",\n },\n start: {\n lines: [\n \"# 欢迎使用 opencode-tbot\",\n \"\",\n \"通过 Telegram 直接和 OpenCode 服务对话。\",\n \"\",\n \"## 快速开始\",\n \"1. 先运行 `/status` 确认服务状态正常。\",\n \"2. 运行 `/new [title]` 创建一个新会话。\",\n \"\",\n \"直接发送文本或图片消息即可。\",\n ],\n },\n systemStatus: {\n title: \"系统状态\",\n },\n common: {\n notSelected: \"未选择\",\n openCodeDefault: \"未选择(使用 OpenCode 默认值)\",\n previousPage: \"上一页\",\n nextPage: \"下一页\",\n page(currentPage: number, totalPages: number) {\n return `第 ${currentPage}/${totalPages} 页`;\n },\n },\n status: {\n processing: \"处理中...\",\n alreadyProcessing: \"另一个请求仍在运行。请等待其完成后再发送新的提示词。\",\n },\n prompt: {\n emptyResponse: \"OpenCode 返回了空响应。\",\n },\n replyMetrics: {\n durationLabel: \"耗时\",\n tokensLabel: \"令牌数\",\n totalLabel: \"总计\",\n inputLabel: \"输入\",\n outputLabel: \"输出\",\n reasoningLabel: \"推理\",\n cacheReadLabel: \"缓存读取\",\n cacheWriteLabel: \"缓存写入\",\n notAvailable: \"不可用\",\n },\n abort: {\n noSession: \"当前聊天还没有绑定活动会话。\",\n notRunning: \"当前会话没有正在运行的请求。\",\n aborted: \"已向当前会话发送中止信号。\",\n },\n errors: {\n unexpected: \"发生未知错误。\",\n providerAuth: \"Provider 认证失败。\",\n requestAborted: \"请求已中止。\",\n promptTimeout: \"OpenCode 响应超时。\",\n structuredOutput: \"结构化输出校验失败。\",\n voiceUnsupported: \"暂不支持语音消息,请改发文本或图片。\",\n imageDownload: \"下载 Telegram 图片文件失败。\",\n imageUnsupported: \"图片文件过大或不受支持。\",\n outputLength: \"回复触发了模型输出长度上限。\",\n contextOverflow: \"会话已超过模型上下文窗口。\",\n providerRequest: \"Provider 请求失败。\",\n notFound: \"请求的资源不存在。\",\n badRequest: \"OpenCode 拒绝了该请求。\",\n causeLabel: \"原因\",\n retryableLabel: \"可重试\",\n statusCodeLabel: \"状态码\",\n },\n health: {\n title: \"服务健康状态\",\n status(healthy: boolean) {\n return `状态: ${healthy ? \"健康\" : \"异常\"}`;\n },\n version(version: string) {\n return `版本: ${version}`;\n },\n },\n path: {\n title: \"当前路径\",\n home(path: string) {\n return `主目录: ${path}`;\n },\n state(path: string) {\n return `状态目录: ${path}`;\n },\n config(path: string) {\n return `配置文件: ${path}`;\n },\n worktree(path: string) {\n return `工作树: ${path}`;\n },\n directory(path: string) {\n return `当前工作目录: ${path}`;\n },\n },\n sessions: {\n none: \"当前项目下没有可用会话。\",\n title: \"会话列表\",\n actionTitle: \"会话操作\",\n chooseAction: \"请选择该会话的操作。\",\n currentProject(worktree: string) {\n return `当前项目: ${worktree}`;\n },\n currentSession(session: string) {\n return `当前会话: ${session}`;\n },\n selectedSession(session: string) {\n return `已选会话: ${session}`;\n },\n switched: \"会话已切换。\",\n created: \"会话已创建。\",\n renamed: \"会话已重命名。\",\n renameCancelled: \"已取消会话重命名。\",\n renameEmpty: \"会话名称不能为空。请重新发送名称或发送 /cancel。\",\n renameExpired: \"该会话已不可用。请重新运行 /sessions。\",\n renamePendingInput: \"当前正在等待新的会话名称。请发送纯文本或 /cancel。\",\n renamePrompt(session: string) {\n return [\n `重命名会话: ${session}`,\n \"请发送新的会话名称。\",\n \"发送 /cancel 取消。\",\n ].join(\"\\n\");\n },\n switchAction: \"切换\",\n renameAction: \"重命名\",\n backToList: \"返回列表\",\n expired: \"该会话已不可用。请重新运行 /sessions。\",\n },\n lsp: {\n none: \"当前项目未检测到 LSP。\",\n title: \"LSP 服务\",\n currentProject(worktree: string) {\n return `当前项目: ${worktree}`;\n },\n connected: \"已连接\",\n error: \"异常\",\n },\n mcp: {\n none: \"当前项目未配置 MCP。\",\n title: \"MCP 服务\",\n currentProject(worktree: string) {\n return `当前项目: ${worktree}`;\n },\n connected: \"已连接\",\n disabled: \"已禁用\",\n needsAuth: \"需要认证\",\n failed(error: string) {\n return `失败: ${error}`;\n },\n needsClientRegistration(error: string) {\n return `需要客户端注册: ${error}`;\n },\n },\n agents: {\n none: \"没有可用 Agent。\",\n title: \"Agent 列表\",\n current(agent: string) {\n return `当前 Agent: ${agent}`;\n },\n switched: \"Agent 已切换。\",\n expired: \"该 Agent 已不可用。请重新运行 /agents。\",\n },\n models: {\n none: \"没有可用模型。\",\n title: \"模型列表\",\n configuredOnly: \"仅显示当前在 OpenCode 和已连接 provider 中实际可用的模型。\",\n current(model: string) {\n return `当前模型: ${model}`;\n },\n switched: \"模型已切换。\",\n currentReasoningLevel(variant: string) {\n return `当前推理级别: ${variant}`;\n },\n reasoningLevel(variant: string) {\n return `推理级别: ${variant}`;\n },\n noReasoningLevels: \"该模型没有可选的推理级别。\",\n reasoningLevelsTitle: \"推理级别\",\n model(model: string) {\n return `模型: ${model}`;\n },\n modelNumber(modelIndex: number) {\n return `模型编号: ${modelIndex}`;\n },\n expired: \"该模型已不可用。请重新运行 /model。\",\n reasoningLevelExpired: \"该推理级别已不可用。请重新运行 /model。\",\n defaultReasoningLevel: \"默认\",\n },\n language: {\n title: \"语言\",\n choose: \"选择 Bot 菜单和回复的显示语言。\",\n current(label: string) {\n return `当前语言: ${label}`;\n },\n switched: \"语言已切换。\",\n expired: \"该语言选项已不可用。请重新运行 /language。\",\n labels: {\n en: \"English\",\n \"zh-CN\": \"简体中文\",\n ja: \"日本語\",\n },\n },\n};\n\nconst JA_BOT_COPY: BotCopy = {\n locale: \"ja\",\n commands: {\n start: \"ようこそとクイックスタートを表示\",\n status: \"システム状態を表示\",\n new: \"新しいセッションを作成\",\n agents: \"エージェントを表示して切り替え\",\n sessions: \"セッションを表示して切り替え\",\n cancel: \"名前変更を取り消すか実行中のリクエストを中止\",\n model: \"モデルを表示して切り替え\",\n language: \"言語を表示して切り替え\",\n },\n start: {\n lines: [\n \"# opencode-tbot へようこそ\",\n \"\",\n \"Telegram から OpenCode サーバーとやり取りできます。\",\n \"\",\n \"## クイックスタート\",\n \"1. `/status` を実行してサーバーの準備完了を確認します。\",\n \"2. `/new [title]` を実行して新しいセッションを作成します。\",\n \"\",\n \"そのままテキストまたは画像メッセージを送信できます。\",\n ],\n },\n systemStatus: {\n title: \"システム状態\",\n },\n common: {\n notSelected: \"未選択\",\n openCodeDefault: \"未選択(OpenCode のデフォルトを使用)\",\n previousPage: \"前へ\",\n nextPage: \"次へ\",\n page(currentPage: number, totalPages: number) {\n return `ページ ${currentPage}/${totalPages}`;\n },\n },\n status: {\n processing: \"処理中...\",\n alreadyProcessing: \"別のリクエストがまだ実行中です。完了するまで新しいプロンプトを送信しないでください。\",\n },\n prompt: {\n emptyResponse: \"OpenCode から空の応答が返されました。\",\n },\n replyMetrics: {\n durationLabel: \"所要時間\",\n tokensLabel: \"トークン\",\n totalLabel: \"合計\",\n inputLabel: \"入力\",\n outputLabel: \"出力\",\n reasoningLabel: \"推論\",\n cacheReadLabel: \"キャッシュ読込\",\n cacheWriteLabel: \"キャッシュ書込\",\n notAvailable: \"該当なし\",\n },\n abort: {\n noSession: \"このチャットにはまだアクティブなセッションが紐付いていません。\",\n notRunning: \"現在のセッションで実行中のリクエストはありません。\",\n aborted: \"現在のセッションに中止シグナルを送信しました。\",\n },\n errors: {\n unexpected: \"予期しないエラーが発生しました。\",\n providerAuth: \"Provider の認証に失敗しました。\",\n requestAborted: \"リクエストは中止されました。\",\n promptTimeout: \"OpenCode リクエストがタイムアウトしました。\",\n structuredOutput: \"構造化出力の検証に失敗しました。\",\n voiceUnsupported: \"音声メッセージには対応していません。代わりにテキストまたは画像を送信してください。\",\n imageDownload: \"Telegram の画像ファイルをダウンロードできませんでした。\",\n imageUnsupported: \"画像ファイルが大きすぎるか、サポートされていません。\",\n outputLength: \"返信がモデルの出力上限に達しました。\",\n contextOverflow: \"会話がモデルのコンテキスト上限を超えました。\",\n providerRequest: \"Provider へのリクエストに失敗しました。\",\n notFound: \"要求されたリソースが見つかりませんでした。\",\n badRequest: \"リクエストは OpenCode に拒否されました。\",\n causeLabel: \"原因\",\n retryableLabel: \"再試行可能\",\n statusCodeLabel: \"ステータス\",\n },\n health: {\n title: \"サーバー状態\",\n status(healthy: boolean) {\n return `状態: ${healthy ? \"正常\" : \"異常\"}`;\n },\n version(version: string) {\n return `バージョン: ${version}`;\n },\n },\n path: {\n title: \"現在のパス\",\n home(path: string) {\n return `ホーム: ${path}`;\n },\n state(path: string) {\n return `状態: ${path}`;\n },\n config(path: string) {\n return `設定: ${path}`;\n },\n worktree(path: string) {\n return `ワークツリー: ${path}`;\n },\n directory(path: string) {\n return `現在の作業ディレクトリ: ${path}`;\n },\n },\n sessions: {\n none: \"現在のプロジェクトで利用できるセッションはありません。\",\n title: \"セッション一覧\",\n actionTitle: \"セッション操作\",\n chooseAction: \"このセッションに対する操作を選択してください。\",\n currentProject(worktree: string) {\n return `現在のプロジェクト: ${worktree}`;\n },\n currentSession(session: string) {\n return `現在のセッション: ${session}`;\n },\n selectedSession(session: string) {\n return `選択中のセッション: ${session}`;\n },\n switched: \"セッションを切り替えました。\",\n created: \"セッションを作成しました。\",\n renamed: \"セッション名を変更しました。\",\n renameCancelled: \"セッション名の変更を取り消しました。\",\n renameEmpty: \"セッション名は空にできません。新しい名前を送信するか /cancel を実行してください。\",\n renameExpired: \"このセッションはもう利用できません。/sessions を再実行してください。\",\n renamePendingInput: \"新しいセッション名の入力待ちです。プレーンテキストを送るか /cancel を実行してください。\",\n renamePrompt(session: string) {\n return [\n `セッション名を変更: ${session}`,\n \"次のテキストメッセージで新しいセッション名を送信してください。\",\n \"/cancel で取り消します。\",\n ].join(\"\\n\");\n },\n switchAction: \"切り替え\",\n renameAction: \"名前変更\",\n backToList: \"一覧に戻る\",\n expired: \"このセッションはもう利用できません。/sessions を再実行してください。\",\n },\n lsp: {\n none: \"現在のプロジェクトで LSP サーバーは検出されませんでした。\",\n title: \"LSP サーバー\",\n currentProject(worktree: string) {\n return `現在のプロジェクト: ${worktree}`;\n },\n connected: \"接続済み\",\n error: \"エラー\",\n },\n mcp: {\n none: \"現在のプロジェクトで MCP サーバーは設定されていません。\",\n title: \"MCP サーバー\",\n currentProject(worktree: string) {\n return `現在のプロジェクト: ${worktree}`;\n },\n connected: \"接続済み\",\n disabled: \"無効\",\n needsAuth: \"認証が必要\",\n failed(error: string) {\n return `失敗: ${error}`;\n },\n needsClientRegistration(error: string) {\n return `クライアント登録が必要: ${error}`;\n },\n },\n agents: {\n none: \"利用可能なエージェントはありません。\",\n title: \"エージェント一覧\",\n current(agent: string) {\n return `現在のエージェント: ${agent}`;\n },\n switched: \"エージェントを切り替えました。\",\n expired: \"このエージェントはもう利用できません。/agents を再実行してください。\",\n },\n models: {\n none: \"利用可能なモデルはありません。\",\n title: \"モデル一覧\",\n configuredOnly: \"OpenCode と接続済み provider で現在利用可能なモデルのみ表示します。\",\n current(model: string) {\n return `現在のモデル: ${model}`;\n },\n switched: \"モデルを切り替えました。\",\n currentReasoningLevel(variant: string) {\n return `現在の推論レベル: ${variant}`;\n },\n reasoningLevel(variant: string) {\n return `推論レベル: ${variant}`;\n },\n noReasoningLevels: \"このモデルには選択可能な推論レベルがありません。\",\n reasoningLevelsTitle: \"推論レベル\",\n model(model: string) {\n return `モデル: ${model}`;\n },\n modelNumber(modelIndex: number) {\n return `モデル番号: ${modelIndex}`;\n },\n expired: \"このモデルはもう利用できません。/model を再実行してください。\",\n reasoningLevelExpired: \"この推論レベルはもう利用できません。/model を再実行してください。\",\n defaultReasoningLevel: \"デフォルト\",\n },\n language: {\n title: \"言語\",\n choose: \"Bot のメニューと返信に使う表示言語を選択してください。\",\n current(label: string) {\n return `現在の言語: ${label}`;\n },\n switched: \"言語を切り替えました。\",\n expired: \"この言語オプションはもう利用できません。/language を再実行してください。\",\n labels: {\n en: \"English\",\n \"zh-CN\": \"简体中文\",\n ja: \"日本語\",\n },\n },\n};\n\nexport const BOT_COPY = EN_BOT_COPY;\n\nexport function isBotLanguage(value: string): value is BotLanguage {\n return SUPPORTED_BOT_LANGUAGES.includes(value as BotLanguage);\n}\n\nexport function normalizeBotLanguage(value: string | null | undefined): BotLanguage {\n if (!value) {\n return DEFAULT_BOT_LANGUAGE;\n }\n\n const normalized = value.trim().toLowerCase();\n\n if (normalized === \"zh-cn\" || normalized === \"zh-hans\" || normalized === \"zh\") {\n return \"zh-CN\";\n }\n\n if (normalized === \"ja\" || normalized === \"ja-jp\" || normalized === \"ja_jp\") {\n return \"ja\";\n }\n\n return \"en\";\n}\n\nexport function getBotCopy(language: BotLanguage | null | undefined = DEFAULT_BOT_LANGUAGE): BotCopy {\n const normalized = normalizeBotLanguage(language);\n\n if (normalized === \"zh-CN\") {\n return ZH_CN_BOT_COPY;\n }\n\n if (normalized === \"ja\") {\n return JA_BOT_COPY;\n }\n\n return EN_BOT_COPY;\n}\n\nexport function getLanguageLabel(language: BotLanguage, copy: BotCopy = BOT_COPY): string {\n return copy.language.labels[language];\n}\n","import type { BotCommand } from \"grammy/types\";\nimport { getBotCopy, type BotLanguage } from \"../copy.js\";\n\nexport function getTelegramCommands(language: BotLanguage = \"en\"): BotCommand[] {\n const copy = getBotCopy(language);\n\n return [\n {\n command: \"start\",\n description: copy.commands.start,\n },\n {\n command: \"status\",\n description: copy.commands.status,\n },\n {\n command: \"new\",\n description: copy.commands.new,\n },\n {\n command: \"agents\",\n description: copy.commands.agents,\n },\n {\n command: \"sessions\",\n description: copy.commands.sessions,\n },\n {\n command: \"cancel\",\n description: copy.commands.cancel,\n },\n {\n command: \"model\",\n description: copy.commands.model,\n },\n {\n command: \"language\",\n description: copy.commands.language,\n },\n ];\n}\n\nexport const TELEGRAM_COMMANDS: BotCommand[] = getTelegramCommands();\n","import type { Bot } from \"grammy\";\nimport type { BotCommandScope } from \"grammy/types\";\nimport type { AppLogger } from \"../../infra/logger/index.js\";\nimport { type BotLanguage } from \"../copy.js\";\nimport { getTelegramCommands, TELEGRAM_COMMANDS } from \"./command-list.js\";\n\nexport const TELEGRAM_COMMAND_SYNC_SCOPES: ReadonlyArray<BotCommandScope> = [\n { type: \"default\" },\n { type: \"all_private_chats\" },\n];\n\nexport async function syncTelegramCommands(bot: Bot, logger: AppLogger): Promise<void> {\n await Promise.all(\n TELEGRAM_COMMAND_SYNC_SCOPES.map((scope) =>\n bot.api.setMyCommands(TELEGRAM_COMMANDS, { scope }),\n ),\n );\n\n logger.info(\n {\n commands: TELEGRAM_COMMANDS.map((command) => command.command),\n scopes: TELEGRAM_COMMAND_SYNC_SCOPES.map((scope) => scope.type),\n },\n \"telegram commands synced\",\n );\n}\n\nexport async function syncTelegramCommandsForChat(\n api: Pick<Bot[\"api\"], \"setMyCommands\">,\n chatId: number,\n language: BotLanguage,\n): Promise<void> {\n await api.setMyCommands(\n getTelegramCommands(language),\n {\n scope: {\n type: \"chat\",\n chat_id: chatId,\n },\n },\n );\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport {\n DEFAULT_BOT_LANGUAGE,\n getBotCopy,\n normalizeBotLanguage,\n type BotCopy,\n type BotLanguage,\n} from \"./copy.js\";\n\nexport async function getChatLanguage(\n sessionRepo: SessionRepository,\n chatId: number | null | undefined,\n): Promise<BotLanguage> {\n if (!chatId) {\n return DEFAULT_BOT_LANGUAGE;\n }\n\n const binding = await sessionRepo.getByChatId(chatId);\n\n return normalizeBotLanguage(binding?.language);\n}\n\nexport async function getChatCopy(\n sessionRepo: SessionRepository,\n chatId: number | null | undefined,\n): Promise<BotCopy> {\n return getBotCopy(await getChatLanguage(sessionRepo, chatId));\n}\n\nexport async function setChatLanguage(\n sessionRepo: SessionRepository,\n chatId: number,\n language: BotLanguage,\n): Promise<void> {\n const binding = await sessionRepo.getByChatId(chatId);\n\n await sessionRepo.setCurrent({\n chatId,\n sessionId: binding?.sessionId ?? null,\n projectId: binding?.projectId ?? null,\n directory: binding?.directory ?? null,\n agentName: binding?.agentName ?? null,\n modelProviderId: binding?.modelProviderId ?? null,\n modelId: binding?.modelId ?? null,\n modelVariant: binding?.modelVariant ?? null,\n language,\n updatedAt: new Date().toISOString(),\n });\n}\n","import { InlineKeyboard } from \"grammy\";\nimport {\n BOT_COPY,\n getLanguageLabel,\n SUPPORTED_BOT_LANGUAGES,\n type BotCopy,\n type BotLanguage,\n} from \"../copy.js\";\nimport type {\n OpenCodeAgent,\n OpenCodeConfiguredModel,\n OpenCodeSession,\n} from \"../../services/opencode/opencode.client.js\";\n\nexport const MODELS_PAGE_SIZE = 10;\nexport const AGENTS_PAGE_SIZE = 10;\nexport const SESSIONS_PAGE_SIZE = 10;\nconst NUMBERED_BUTTONS_PER_ROW = 5;\n\nexport interface ModelsPage {\n items: OpenCodeConfiguredModel[];\n page: number;\n startIndex: number;\n totalPages: number;\n}\n\nexport interface AgentsPage {\n items: OpenCodeAgent[];\n page: number;\n startIndex: number;\n totalPages: number;\n}\n\nexport interface SessionsPage {\n items: OpenCodeSession[];\n page: number;\n startIndex: number;\n totalPages: number;\n}\n\nexport function buildModelsKeyboard(\n models: OpenCodeConfiguredModel[],\n requestedPage: number,\n copy: BotCopy = BOT_COPY,\n) {\n const page = getModelsPage(models, requestedPage);\n const keyboard = buildNumberedKeyboard(\n page.items,\n page.startIndex,\n (_, index) => `model:pick:${page.startIndex + index + 1}`,\n );\n\n appendPaginationButtons(keyboard, page.page, page.totalPages, \"model:page\", copy);\n\n return { keyboard, page };\n}\n\nexport function buildAgentsKeyboard(\n agents: OpenCodeAgent[],\n requestedPage: number,\n copy: BotCopy = BOT_COPY,\n) {\n const page = getAgentsPage(agents, requestedPage);\n const keyboard = buildNumberedKeyboard(\n page.items,\n page.startIndex,\n (_, index) => `agents:select:${page.startIndex + index + 1}`,\n );\n\n appendPaginationButtons(keyboard, page.page, page.totalPages, \"agents:page\", copy);\n\n return { keyboard, page };\n}\n\nexport function buildSessionsKeyboard(\n sessions: OpenCodeSession[],\n requestedPage: number,\n copy: BotCopy = BOT_COPY,\n) {\n const page = getSessionsPage(sessions, requestedPage);\n const keyboard = buildNumberedKeyboard(\n page.items,\n page.startIndex,\n (session) => `sessions:pick:${page.page}:${session.id}`,\n );\n\n appendPaginationButtons(keyboard, page.page, page.totalPages, \"sessions:page\", copy);\n\n return { keyboard, page };\n}\n\nexport function buildSessionActionKeyboard(\n sessionId: string,\n page: number,\n copy: BotCopy = BOT_COPY,\n) {\n return new InlineKeyboard()\n .text(copy.sessions.switchAction, `sessions:switch:${page}:${sessionId}`)\n .text(copy.sessions.renameAction, `sessions:rename:${page}:${sessionId}`)\n .row()\n .text(copy.sessions.backToList, `sessions:back:${page}`);\n}\n\nexport function buildModelVariantsKeyboard(variants: string[], modelIndex: number) {\n return buildNumberedKeyboard(\n variants,\n 0,\n (_, index) => `model:variant:${modelIndex}:${index + 1}`,\n );\n}\n\nexport function buildLanguageKeyboard(currentLanguage: BotLanguage, copy: BotCopy = BOT_COPY) {\n const keyboard = new InlineKeyboard();\n\n SUPPORTED_BOT_LANGUAGES.forEach((language, index) => {\n const label = currentLanguage === language\n ? `[${getLanguageLabel(language, copy)}]`\n : getLanguageLabel(language, copy);\n\n keyboard.text(label, `language:select:${language}`);\n\n if (index !== SUPPORTED_BOT_LANGUAGES.length - 1) {\n keyboard.row();\n }\n });\n\n return keyboard;\n}\n\nexport function getModelsPage(models: OpenCodeConfiguredModel[], requestedPage: number): ModelsPage {\n return getPagedItems(models, requestedPage, MODELS_PAGE_SIZE);\n}\n\nexport function getAgentsPage(agents: OpenCodeAgent[], requestedPage: number): AgentsPage {\n return getPagedItems(agents, requestedPage, AGENTS_PAGE_SIZE);\n}\n\nexport function getSessionsPage(sessions: OpenCodeSession[], requestedPage: number): SessionsPage {\n return getPagedItems(sessions, requestedPage, SESSIONS_PAGE_SIZE);\n}\n\nfunction buildNumberedKeyboard<T>(\n items: T[],\n startIndex: number,\n buildCallbackData: (item: T, index: number) => string,\n) {\n const keyboard = new InlineKeyboard();\n\n items.forEach((item, index) => {\n const displayIndex = startIndex + index + 1;\n\n keyboard.text(`${displayIndex}`, buildCallbackData(item, index));\n\n if (\n index !== items.length - 1\n && (index + 1) % NUMBERED_BUTTONS_PER_ROW === 0\n ) {\n keyboard.row();\n }\n });\n\n return keyboard;\n}\n\nfunction appendPaginationButtons(\n keyboard: InlineKeyboard,\n page: number,\n totalPages: number,\n prefix: \"agents:page\" | \"sessions:page\" | \"model:page\",\n copy: BotCopy,\n) {\n if (totalPages <= 1) {\n return;\n }\n\n if (page > 0) {\n keyboard.text(copy.common.previousPage, `${prefix}:${page - 1}`);\n }\n\n if (page < totalPages - 1) {\n keyboard.text(copy.common.nextPage, `${prefix}:${page + 1}`);\n }\n}\n\nfunction getPagedItems<T>(items: T[], requestedPage: number, pageSize: number) {\n const totalPages = Math.max(1, Math.ceil(items.length / pageSize));\n const page = clampPage(requestedPage, totalPages);\n const startIndex = page * pageSize;\n\n return {\n items: items.slice(startIndex, startIndex + pageSize),\n page,\n startIndex,\n totalPages,\n };\n}\n\nfunction clampPage(page: number, totalPages: number): number {\n if (!Number.isInteger(page) || page < 0) {\n return 0;\n }\n\n return Math.min(page, totalPages - 1);\n}\n","import { BOT_COPY, type BotCopy } from \"../copy.js\";\n\ninterface PresentedError {\n cause: string | null;\n message: string;\n}\n\nexport function presentError(error: unknown, copy: BotCopy = BOT_COPY): string {\n const presented = normalizeError(error, copy);\n\n return presented.cause\n ? `${presented.message}\\n${copy.errors.causeLabel}: ${presented.cause}`\n : presented.message;\n}\n\nfunction normalizeError(error: unknown, copy: BotCopy): PresentedError {\n if (isNamedError(error, \"ProviderAuthError\")) {\n return {\n message: copy.errors.providerAuth,\n cause: extractMessage(error.data) ?? null,\n };\n }\n\n if (isNamedError(error, \"MessageAbortedError\")) {\n return {\n message: copy.errors.requestAborted,\n cause: extractMessage(error.data) ?? null,\n };\n }\n\n if (isNamedError(error, \"OpenCodePromptTimeoutError\")) {\n return {\n message: copy.errors.promptTimeout,\n cause: null,\n };\n }\n\n if (isNamedError(error, \"StructuredOutputError\")) {\n return {\n message: copy.errors.structuredOutput,\n cause: joinNonEmptyParts([\n extractMessage(error.data),\n extractRetries(error.data),\n ]),\n };\n }\n\n if (isNamedError(error, \"ImageFileDownloadError\")) {\n return {\n message: copy.errors.imageDownload,\n cause: extractMessage(error.data) ?? null,\n };\n }\n\n if (isNamedError(error, \"ImageMessageUnsupportedError\")) {\n return {\n message: copy.errors.imageUnsupported,\n cause: extractMessage(error.data) ?? null,\n };\n }\n\n if (isNamedError(error, \"MessageOutputLengthError\")) {\n return {\n message: copy.errors.outputLength,\n cause: extractMessage(error.data) ?? null,\n };\n }\n\n if (isNamedError(error, \"ContextOverflowError\")) {\n return {\n message: copy.errors.contextOverflow,\n cause: extractMessage(error.data) ?? null,\n };\n }\n\n if (isNamedError(error, \"APIError\")) {\n return {\n message: copy.errors.providerRequest,\n cause: joinNonEmptyParts([\n extractMessage(error.data),\n extractStatusCode(error.data, copy),\n extractRetryable(error.data, copy),\n ]),\n };\n }\n\n if (isNamedError(error, \"NotFoundError\")) {\n return {\n message: copy.errors.notFound,\n cause: extractMessage(error.data) ?? null,\n };\n }\n\n if (isBadRequestError(error)) {\n return {\n message: copy.errors.badRequest,\n cause: extractBadRequestCause(error),\n };\n }\n\n if (error instanceof Error) {\n return {\n message: error.name === \"AbortError\" ? copy.errors.requestAborted : copy.errors.unexpected,\n cause: error.message || null,\n };\n }\n\n return {\n message: copy.errors.unexpected,\n cause: extractMessage(error) ?? stringifyUnknown(error),\n };\n}\n\nfunction isBadRequestError(error: unknown): error is { data?: unknown; errors?: unknown[]; success: false } {\n return !!error\n && typeof error === \"object\"\n && \"success\" in error\n && (error as { success?: unknown }).success === false;\n}\n\nfunction isNamedError<TName extends string>(\n error: unknown,\n name: TName,\n): error is { data?: unknown; name: TName } {\n return !!error\n && typeof error === \"object\"\n && \"name\" in error\n && (error as { name?: unknown }).name === name;\n}\n\nfunction extractBadRequestCause(error: { data?: unknown; errors?: unknown[] }): string | null {\n const directMessage = extractMessage(error.data);\n\n if (directMessage) {\n return directMessage;\n }\n\n const firstValidationError = Array.isArray(error.errors) && error.errors.length > 0\n ? stringifyUnknown(error.errors[0])\n : null;\n\n return firstValidationError;\n}\n\nfunction extractMessage(value: unknown): string | null {\n if (!value) {\n return null;\n }\n\n if (typeof value === \"string\") {\n return value.trim() || null;\n }\n\n if (typeof value === \"object\" && \"message\" in value) {\n const message = (value as { message?: unknown }).message;\n\n return typeof message === \"string\" && message.trim().length > 0\n ? message.trim()\n : null;\n }\n\n return null;\n}\n\nfunction extractRetries(value: unknown): string | null {\n if (!value || typeof value !== \"object\" || !(\"retries\" in value)) {\n return null;\n }\n\n const retries = (value as { retries?: unknown }).retries;\n\n return typeof retries === \"number\" && Number.isFinite(retries)\n ? `retries: ${Math.round(retries)}`\n : null;\n}\n\nfunction extractRetryable(value: unknown, copy: BotCopy): string | null {\n if (!value || typeof value !== \"object\" || !(\"isRetryable\" in value)) {\n return null;\n }\n\n const isRetryable = (value as { isRetryable?: unknown }).isRetryable;\n\n return typeof isRetryable === \"boolean\"\n ? `${copy.errors.retryableLabel}: ${isRetryable ? \"yes\" : \"no\"}`\n : null;\n}\n\nfunction extractStatusCode(value: unknown, copy: BotCopy): string | null {\n if (!value || typeof value !== \"object\" || !(\"statusCode\" in value)) {\n return null;\n }\n\n const statusCode = (value as { statusCode?: unknown }).statusCode;\n\n return typeof statusCode === \"number\" && Number.isFinite(statusCode)\n ? `${copy.errors.statusCodeLabel}: ${Math.round(statusCode)}`\n : null;\n}\n\nfunction joinNonEmptyParts(parts: Array<string | null | undefined>): string | null {\n const filtered = parts\n .map((part) => part?.trim())\n .filter((part): part is string => !!part);\n\n return filtered.length > 0 ? filtered.join(\" | \") : null;\n}\n\nfunction stringifyUnknown(value: unknown): string | null {\n if (value === null || value === undefined) {\n return null;\n }\n\n if (typeof value === \"string\") {\n return value.trim() || null;\n }\n\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n\n try {\n const text = JSON.stringify(value);\n\n return text && text !== \"{}\" ? text : null;\n } catch {\n return null;\n }\n}\n","import type {\n OpenCodeAgent,\n OpenCodeConfiguredModel,\n OpenCodeHealth,\n OpenCodeLspStatus,\n OpenCodeMcpStatus,\n OpenCodePath,\n OpenCodeSession,\n} from \"../../services/opencode/opencode.client.js\";\nimport { OPENCODE_TBOT_VERSION } from \"../../app/package-info.js\";\nimport type { GetStatusResult } from \"../../use-cases/get-status.usecase.js\";\nimport {\n BOT_COPY,\n getLanguageLabel,\n type BotCopy,\n type BotLanguage,\n} from \"../copy.js\";\nimport { presentError } from \"./error.presenter.js\";\nimport {\n getAgentsPage,\n getModelsPage,\n getSessionsPage,\n} from \"./keyboard.presenter.js\";\n\ninterface ModelsMessageInput {\n currentModelId: string | null;\n currentModelProviderId: string | null;\n currentModelVariant: string | null;\n models: OpenCodeConfiguredModel[];\n page: number;\n}\n\ninterface AgentsMessageInput {\n agents: OpenCodeAgent[];\n currentAgentName: string | null;\n page: number;\n}\n\ninterface SessionsMessageInput {\n currentDirectory: string;\n currentSessionId: string | null;\n page: number;\n sessions: OpenCodeSession[];\n}\n\ninterface McpMessageInput {\n currentDirectory: string;\n statuses: Array<{\n name: string;\n status: OpenCodeMcpStatus;\n }>;\n}\n\ninterface LspMessageInput {\n currentDirectory: string;\n statuses: OpenCodeLspStatus[];\n}\n\ninterface StatusLayoutCopy {\n connectivityLabel: string;\n currentProjectLabel: string;\n currentSessionLabel: string;\n defaultSessionValue: string;\n detailsLabel: string;\n errorStatus: string;\n lspTitle: string;\n mcpFailedStatus: string;\n mcpNotesLabel: string;\n mcpRegistrationRequiredStatus: string;\n mcpTitle: string;\n noPluginsMessage: string;\n noneStatus: string;\n openCodeVersionLabel: string;\n overviewTitle: string;\n pluginsTitle: string;\n statusLabel: string;\n tbotVersionLabel: string;\n workspaceTitle: string;\n}\n\ninterface StatusDetailLine {\n codeValue?: boolean;\n label: string;\n value: string;\n}\n\nconst VARIANT_ORDER = [\"minimal\", \"none\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\n\nexport function presentHealthMessage(health: OpenCodeHealth, copy: BotCopy = BOT_COPY): string {\n return [\n copy.health.title,\n copy.health.status(health.healthy),\n copy.health.version(health.version),\n ].join(\"\\n\");\n}\n\nexport function presentPathMessage(path: OpenCodePath, copy: BotCopy = BOT_COPY): string {\n return copy.path.directory(path.directory);\n}\n\nexport function presentStatusMessage(input: GetStatusResult, copy: BotCopy = BOT_COPY): string {\n const layout = getStatusLayoutCopy(copy);\n const sections = [\n presentStatusPlainSection(layout.overviewTitle, presentStatusPlainOverviewLines(input, copy, layout)),\n presentStatusPlainSection(layout.workspaceTitle, presentStatusPlainWorkspaceLines(input, copy, layout)),\n presentStatusPlainSection(layout.pluginsTitle, presentStatusPlainPluginLines(input, copy, layout)),\n presentStatusPlainSection(layout.mcpTitle, presentStatusPlainMcpLines(input, copy, layout)),\n presentStatusPlainSection(layout.lspTitle, presentStatusPlainLspLines(input, copy, layout)),\n ];\n\n return presentStatusSections(sections);\n}\n\nexport function presentStatusMarkdownMessage(input: GetStatusResult, copy: BotCopy = BOT_COPY): string {\n const layout = getStatusLayoutCopy(copy);\n const sections = [\n presentStatusMarkdownSection(layout.overviewTitle, presentStatusMarkdownOverviewLines(input, copy, layout)),\n presentStatusMarkdownSection(layout.workspaceTitle, presentStatusMarkdownWorkspaceLines(input, copy, layout)),\n presentStatusMarkdownSection(layout.pluginsTitle, presentStatusMarkdownPluginLines(input, copy, layout)),\n presentStatusMarkdownSection(layout.mcpTitle, presentStatusMarkdownMcpLines(input, copy, layout)),\n presentStatusMarkdownSection(layout.lspTitle, presentStatusMarkdownLspLines(input, copy, layout)),\n ];\n\n return presentStatusSections(sections);\n}\n\nexport function presentMcpMessage(input: McpMessageInput, copy: BotCopy = BOT_COPY): string {\n const lines = [\n copy.mcp.title,\n copy.mcp.currentProject(input.currentDirectory),\n \"\",\n ];\n\n if (input.statuses.length === 0) {\n lines.push(copy.mcp.none);\n return lines.join(\"\\n\");\n }\n\n lines.push(...input.statuses.map(({ name, status }) => `${name}: ${formatMcpStatus(status, copy)}`));\n\n return lines.join(\"\\n\");\n}\n\nexport function presentLspMessage(input: LspMessageInput, copy: BotCopy = BOT_COPY): string {\n const lines = [\n copy.lsp.title,\n copy.lsp.currentProject(input.currentDirectory),\n \"\",\n ];\n\n if (input.statuses.length === 0) {\n lines.push(copy.lsp.none);\n return lines.join(\"\\n\");\n }\n\n lines.push(...input.statuses.map((status) => `${status.name}: ${formatLspStatus(status, copy)}`));\n\n return lines.join(\"\\n\");\n}\n\nfunction presentStatusSections(sections: string[]): string {\n return sections.flatMap((section, index) => index === 0 ? [section] : [\"\", section]).join(\"\\n\");\n}\n\nfunction presentStatusPlainSection(title: string, lines: string[]): string {\n return [\n title,\n ...lines,\n ].join(\"\\n\");\n}\n\nfunction presentStatusMarkdownSection(title: string, lines: string[]): string {\n return [\n `## ${title}`,\n ...lines,\n ].join(\"\\n\");\n}\n\nfunction presentStatusPlainOverviewLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n const lines = [\n presentPlainStatusBullet(\n layout.connectivityLabel,\n input.health.status === \"error\"\n ? layout.errorStatus\n : formatHealthBadge(input.health.data.healthy, layout),\n ),\n ];\n\n if (input.health.status === \"error\") {\n return [\n ...lines,\n ...presentStatusPlainErrorDetailLines(input.health.error, copy, layout),\n presentPlainStatusBullet(layout.tbotVersionLabel, OPENCODE_TBOT_VERSION),\n ];\n }\n\n return [\n ...lines,\n presentPlainStatusBullet(layout.openCodeVersionLabel, input.health.data.version),\n presentPlainStatusBullet(layout.tbotVersionLabel, OPENCODE_TBOT_VERSION),\n ];\n}\n\nfunction presentStatusMarkdownOverviewLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n const lines = [\n presentMarkdownStatusBullet(\n layout.connectivityLabel,\n input.health.status === \"error\"\n ? layout.errorStatus\n : formatHealthBadge(input.health.data.healthy, layout),\n ),\n ];\n\n if (input.health.status === \"error\") {\n return [\n ...lines,\n ...presentStatusMarkdownErrorDetailLines(input.health.error, copy, layout),\n presentMarkdownStatusBullet(layout.tbotVersionLabel, OPENCODE_TBOT_VERSION),\n ];\n }\n\n return [\n ...lines,\n presentMarkdownStatusBullet(layout.openCodeVersionLabel, input.health.data.version),\n presentMarkdownStatusBullet(layout.tbotVersionLabel, OPENCODE_TBOT_VERSION),\n ];\n}\n\nfunction presentStatusPlainWorkspaceLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.workspace.status === \"error\") {\n return presentStatusPlainErrorLines(input.workspace.error, copy, layout);\n }\n\n return [\n presentPlainStatusBullet(layout.currentProjectLabel, input.workspace.data.currentProject),\n presentPlainStatusBullet(layout.currentSessionLabel, input.workspace.data.currentSession ?? layout.defaultSessionValue),\n ];\n}\n\nfunction presentStatusMarkdownWorkspaceLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.workspace.status === \"error\") {\n return presentStatusMarkdownErrorLines(input.workspace.error, copy, layout);\n }\n\n return [\n presentMarkdownStatusBullet(layout.currentProjectLabel, input.workspace.data.currentProject, {\n codeValue: true,\n }),\n presentMarkdownStatusBullet(layout.currentSessionLabel, input.workspace.data.currentSession ?? layout.defaultSessionValue),\n ];\n}\n\nfunction presentStatusPlainPluginLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.plugins.status === \"error\") {\n return presentStatusPlainErrorLines(input.plugins.error, copy, layout);\n }\n\n if (input.plugins.data.plugins.length === 0) {\n return [\n ...presentPlainEmptyStatusLines(layout.noPluginsMessage, layout),\n ];\n }\n\n return input.plugins.data.plugins.map((plugin) => `- ${plugin}`);\n}\n\nfunction presentStatusMarkdownPluginLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.plugins.status === \"error\") {\n return presentStatusMarkdownErrorLines(input.plugins.error, copy, layout);\n }\n\n if (input.plugins.data.plugins.length === 0) {\n return [\n ...presentMarkdownEmptyStatusLines(layout.noPluginsMessage, layout),\n ];\n }\n\n return input.plugins.data.plugins.map((plugin) => `- \\`${plugin}\\``);\n}\n\nfunction presentStatusPlainLspLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.lsp.status === \"error\") {\n return presentStatusPlainErrorLines(input.lsp.error, copy, layout);\n }\n\n if (input.lsp.data.statuses.length === 0) {\n return presentPlainEmptyStatusLines(copy.lsp.none, layout);\n }\n\n return input.lsp.data.statuses.flatMap((status) => presentPlainStatusGroup(status.name, [\n {\n label: layout.statusLabel,\n value: formatLspStatusBadge(status),\n },\n ]));\n}\n\nfunction presentStatusMarkdownLspLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.lsp.status === \"error\") {\n return presentStatusMarkdownErrorLines(input.lsp.error, copy, layout);\n }\n\n if (input.lsp.data.statuses.length === 0) {\n return presentMarkdownEmptyStatusLines(copy.lsp.none, layout);\n }\n\n return input.lsp.data.statuses.flatMap((status) => presentMarkdownStatusGroup(status.name, [\n {\n label: layout.statusLabel,\n value: formatLspStatusBadge(status),\n },\n ]));\n}\n\nfunction presentStatusPlainMcpLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.mcp.status === \"error\") {\n return presentStatusPlainErrorLines(input.mcp.error, copy, layout);\n }\n\n if (input.mcp.data.statuses.length === 0) {\n return presentPlainEmptyStatusLines(copy.mcp.none, layout);\n }\n\n return input.mcp.data.statuses.flatMap(({ name, status }) =>\n presentPlainStatusGroup(name, getMcpStatusDetailLines(status, copy, layout)),\n );\n}\n\nfunction presentStatusMarkdownMcpLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.mcp.status === \"error\") {\n return presentStatusMarkdownErrorLines(input.mcp.error, copy, layout);\n }\n\n if (input.mcp.data.statuses.length === 0) {\n return presentMarkdownEmptyStatusLines(copy.mcp.none, layout);\n }\n\n return input.mcp.data.statuses.flatMap(({ name, status }) =>\n presentMarkdownStatusGroup(name, getMcpStatusDetailLines(status, copy, layout), {\n codeName: true,\n }),\n );\n}\n\nfunction presentStatusPlainErrorLines(error: unknown, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n return [\n presentPlainStatusBullet(layout.statusLabel, layout.errorStatus),\n ...presentStatusPlainErrorDetailLines(error, copy, layout),\n ];\n}\n\nfunction presentStatusPlainErrorDetailLines(error: unknown, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n const detailLines = splitStatusLines(presentError(error, copy));\n\n return detailLines.map((line) => presentPlainStatusBullet(layout.detailsLabel, line));\n}\n\nfunction presentStatusMarkdownErrorLines(error: unknown, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n return [\n presentMarkdownStatusBullet(layout.statusLabel, layout.errorStatus),\n ...presentStatusMarkdownErrorDetailLines(error, copy, layout),\n ];\n}\n\nfunction presentStatusMarkdownErrorDetailLines(error: unknown, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n const detailLines = splitStatusLines(presentError(error, copy));\n\n return detailLines.map((line) => presentMarkdownStatusBullet(layout.detailsLabel, line));\n}\n\nfunction presentPlainEmptyStatusLines(message: string, layout: StatusLayoutCopy): string[] {\n return [\n presentPlainStatusBullet(layout.statusLabel, layout.noneStatus),\n presentPlainStatusBullet(layout.detailsLabel, message),\n ];\n}\n\nfunction presentMarkdownEmptyStatusLines(message: string, layout: StatusLayoutCopy): string[] {\n return [\n presentMarkdownStatusBullet(layout.statusLabel, layout.noneStatus),\n presentMarkdownStatusBullet(layout.detailsLabel, message),\n ];\n}\n\nfunction presentPlainStatusGroup(name: string, details: StatusDetailLine[]): string[] {\n return [\n `- ${normalizeStatusInlineValue(name)}`,\n ...details.map((detail) => ` - ${detail.label}: ${formatStatusValue(detail.value)}`),\n ];\n}\n\nfunction presentMarkdownStatusGroup(\n name: string,\n details: StatusDetailLine[],\n options: {\n codeName?: boolean;\n } = {},\n): string[] {\n const renderedName = options.codeName\n ? `\\`${normalizeStatusInlineValue(name)}\\``\n : `**${normalizeStatusInlineValue(name)}**`;\n\n return [\n `- ${renderedName}`,\n ...details.map((detail) => detail.codeValue\n ? ` - **${detail.label}:** \\`${formatStatusValue(detail.value)}\\``\n : ` - **${detail.label}:** ${formatStatusValue(detail.value)}`),\n ];\n}\n\nfunction presentPlainStatusBullet(label: string, value: string): string {\n return `- ${label}: ${formatStatusValue(value)}`;\n}\n\nfunction presentMarkdownStatusBullet(\n label: string,\n value: string,\n options: {\n codeValue?: boolean;\n } = {},\n): string {\n return options.codeValue\n ? `- **${label}:** \\`${formatStatusValue(value)}\\``\n : `- **${label}:** ${formatStatusValue(value)}`;\n}\n\nfunction splitStatusLines(text: string): string[] {\n return text\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n}\n\nfunction formatHealthBadge(healthy: boolean, layout: StatusLayoutCopy): string {\n return healthy ? \"\\uD83D\\uDFE2\" : layout.errorStatus;\n}\n\nfunction formatLspStatusBadge(status: OpenCodeLspStatus): string {\n switch (status.status) {\n case \"connected\":\n return \"\\uD83D\\uDFE2\";\n case \"error\":\n return \"\\uD83D\\uDD34\";\n }\n\n const exhaustiveStatus: never = status.status;\n\n return exhaustiveStatus;\n}\n\nfunction formatMcpStatusBadge(status: OpenCodeMcpStatus, layout: StatusLayoutCopy): string {\n switch (status.status) {\n case \"connected\":\n return \"\\uD83D\\uDFE2\";\n case \"disabled\":\n return \"\\u26AA\";\n case \"needs_auth\":\n return \"\\uD83D\\uDFE1\";\n case \"failed\":\n return layout.mcpFailedStatus;\n case \"needs_client_registration\":\n return layout.mcpRegistrationRequiredStatus;\n }\n\n const exhaustiveStatus: never = status;\n\n return exhaustiveStatus;\n}\n\nfunction getMcpStatusDetailLines(\n status: OpenCodeMcpStatus,\n copy: BotCopy,\n layout: StatusLayoutCopy,\n): StatusDetailLine[] {\n const notes = formatMcpStatusNotes(status, copy, layout);\n\n return notes\n ? [\n {\n label: layout.statusLabel,\n value: formatMcpStatusBadge(status, layout),\n },\n {\n label: layout.mcpNotesLabel,\n value: notes,\n },\n ]\n : [\n {\n label: layout.statusLabel,\n value: formatMcpStatusBadge(status, layout),\n },\n ];\n}\n\nfunction formatMcpStatusNotes(status: OpenCodeMcpStatus, copy: BotCopy, layout: StatusLayoutCopy): string | null {\n switch (status.status) {\n case \"connected\":\n return null;\n case \"disabled\":\n return null;\n case \"needs_auth\":\n return copy.mcp.needsAuth;\n case \"failed\":\n return status.error;\n case \"needs_client_registration\":\n return status.error;\n }\n\n const exhaustiveStatus: never = status;\n\n return exhaustiveStatus;\n}\n\nfunction formatStatusValue(value: string): string {\n const normalized = value\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n .join(\" / \");\n\n return normalized.length > 0 ? normalized : \"-\";\n}\n\nfunction normalizeStatusInlineValue(value: string): string {\n return formatStatusValue(value);\n}\n\nfunction getStatusLayoutCopy(copy: BotCopy): StatusLayoutCopy {\n if (copy.locale === \"en\") {\n return {\n connectivityLabel: \"Connectivity\",\n currentProjectLabel: \"Current Project\",\n currentSessionLabel: \"Current Session\",\n defaultSessionValue: \"OpenCode Default\",\n detailsLabel: \"Details\",\n errorStatus: \"\\uD83D\\uDD34\",\n lspTitle: \"\\uD83E\\uDDE0 LSP\",\n mcpFailedStatus: \"\\uD83D\\uDD34\",\n mcpNotesLabel: \"Notes\",\n mcpRegistrationRequiredStatus: \"\\uD83D\\uDFE1\",\n mcpTitle: \"\\uD83D\\uDD0C MCP\",\n noPluginsMessage: \"No plugins configured in the OpenCode config.\",\n noneStatus: \"\\u26AA\",\n openCodeVersionLabel: \"OpenCode Version\",\n overviewTitle: \"\\uD83D\\uDDA5\\uFE0F Overview\",\n pluginsTitle: \"\\uD83E\\uDDE9 Plugins\",\n statusLabel: \"Status\",\n tbotVersionLabel: \"opencode-tbot Version\",\n workspaceTitle: \"\\uD83D\\uDCC1 Workspace\",\n };\n }\n\n if (copy.locale === \"ja\") {\n return {\n connectivityLabel: \"接続性\",\n currentProjectLabel: \"現在のプロジェクト\",\n currentSessionLabel: \"現在のセッション\",\n defaultSessionValue: \"OpenCode のデフォルト\",\n detailsLabel: \"詳細\",\n errorStatus: \"\\uD83D\\uDD34\",\n lspTitle: \"\\uD83E\\uDDE0 LSP\",\n mcpFailedStatus: \"\\uD83D\\uDD34\",\n mcpNotesLabel: \"補足\",\n mcpRegistrationRequiredStatus: \"\\uD83D\\uDFE1\",\n mcpTitle: \"\\uD83D\\uDD0C MCP\",\n noPluginsMessage: \"現在の OpenCode 設定にはプラグインが設定されていません。\",\n noneStatus: \"\\u26AA\",\n openCodeVersionLabel: \"OpenCode バージョン\",\n overviewTitle: \"\\uD83D\\uDDA5\\uFE0F 概要\",\n pluginsTitle: \"\\uD83E\\uDDE9 プラグイン\",\n statusLabel: \"状態\",\n tbotVersionLabel: \"opencode-tbot バージョン\",\n workspaceTitle: \"\\uD83D\\uDCC1 ワークスペース\",\n };\n }\n\n return {\n connectivityLabel: \"\\u8FDE\\u901A\\u6027\",\n currentProjectLabel: \"\\u5F53\\u524D\\u9879\\u76EE\",\n currentSessionLabel: \"\\u5F53\\u524D\\u4F1A\\u8BDD\",\n defaultSessionValue: \"OpenCode \\u9ED8\\u8BA4\",\n detailsLabel: \"\\u8BE6\\u60C5\",\n errorStatus: \"\\uD83D\\uDD34\",\n lspTitle: \"\\uD83E\\uDDE0 LSP\",\n mcpFailedStatus: \"\\uD83D\\uDD34\",\n mcpNotesLabel: \"\\u8BF4\\u660E\",\n mcpRegistrationRequiredStatus: \"\\uD83D\\uDFE1\",\n mcpTitle: \"\\uD83D\\uDD0C MCP\",\n noPluginsMessage: \"\\u5F53\\u524D OpenCode \\u914D\\u7F6E\\u4E2D\\u672A\\u914D\\u7F6E\\u63D2\\u4EF6\\u3002\",\n noneStatus: \"\\u26AA\",\n openCodeVersionLabel: \"OpenCode\\u7248\\u672C\",\n overviewTitle: \"\\uD83D\\uDDA5\\uFE0F \\u6982\\u89C8\",\n pluginsTitle: \"\\uD83E\\uDDE9 \\u63D2\\u4EF6\",\n statusLabel: \"\\u72B6\\u6001\",\n tbotVersionLabel: \"opencode-tbot\\u7248\\u672C\",\n workspaceTitle: \"\\uD83D\\uDCC1 \\u5DE5\\u4F5C\\u533A\",\n };\n}\n\nexport function presentSessionsMessage(input: SessionsMessageInput, copy: BotCopy = BOT_COPY): string {\n if (input.sessions.length === 0) {\n return copy.sessions.none;\n }\n\n const page = getSessionsPage(input.sessions, input.page);\n const currentSession = input.currentSessionId\n ? input.sessions.find((session) => session.id === input.currentSessionId) ?? null\n : null;\n const lines = [\n copy.sessions.title,\n copy.sessions.currentProject(input.currentDirectory),\n copy.sessions.currentSession(currentSession ? formatSessionLabel(currentSession) : copy.common.notSelected),\n copy.common.page(page.page + 1, page.totalPages),\n \"\",\n ...page.items.map((session, index) => `${page.startIndex + index + 1}. ${formatSessionLabel(session)}`),\n ];\n\n return lines.join(\"\\n\");\n}\n\nexport function presentSessionSwitchMessage(session: OpenCodeSession, copy: BotCopy = BOT_COPY): string {\n return [\n copy.sessions.switched,\n copy.sessions.currentSession(formatSessionLabel(session)),\n ].join(\"\\n\");\n}\n\nexport function presentSessionCreatedMessage(session: OpenCodeSession, copy: BotCopy = BOT_COPY): string {\n return [\n copy.sessions.created,\n copy.sessions.currentSession(formatSessionLabel(session)),\n ].join(\"\\n\");\n}\n\nexport function presentSessionActionsMessage(\n input: { currentDirectory: string; session: OpenCodeSession },\n copy: BotCopy = BOT_COPY,\n): string {\n return [\n copy.sessions.actionTitle,\n copy.sessions.currentProject(input.currentDirectory),\n copy.sessions.selectedSession(formatSessionLabel(input.session)),\n \"\",\n copy.sessions.chooseAction,\n ].join(\"\\n\");\n}\n\nexport function presentSessionRenamePromptMessage(session: OpenCodeSession, copy: BotCopy = BOT_COPY): string {\n return copy.sessions.renamePrompt(formatSessionLabel(session));\n}\n\nexport function presentSessionRenamedMessage(session: OpenCodeSession, copy: BotCopy = BOT_COPY): string {\n return [\n copy.sessions.renamed,\n copy.sessions.currentSession(formatSessionLabel(session)),\n ].join(\"\\n\");\n}\n\nexport function presentAgentsMessage(input: AgentsMessageInput, copy: BotCopy = BOT_COPY): string {\n if (input.agents.length === 0) {\n return copy.agents.none;\n }\n\n const page = getAgentsPage(input.agents, input.page);\n const currentAgent = input.currentAgentName\n ? input.agents.find((agent) => agent.name === input.currentAgentName) ?? null\n : null;\n const lines = [\n copy.agents.title,\n copy.agents.current(currentAgent ? formatAgentLabel(currentAgent) : copy.common.openCodeDefault),\n copy.common.page(page.page + 1, page.totalPages),\n \"\",\n ...page.items.map((agent, index) => `${page.startIndex + index + 1}. ${formatAgentLabel(agent)}`),\n ];\n\n return lines.join(\"\\n\");\n}\n\nexport function presentAgentSwitchMessage(agent: OpenCodeAgent, copy: BotCopy = BOT_COPY): string {\n return [\n copy.agents.switched,\n copy.agents.current(formatAgentLabel(agent)),\n ].join(\"\\n\");\n}\n\nexport function presentModelsMessage(input: ModelsMessageInput, copy: BotCopy = BOT_COPY): string {\n if (input.models.length === 0) {\n return copy.models.none;\n }\n\n const page = getModelsPage(input.models, input.page);\n const currentModel = input.currentModelId && input.currentModelProviderId\n ? input.models.find((model) =>\n model.id === input.currentModelId && model.providerID === input.currentModelProviderId,\n ) ?? null\n : null;\n const currentModelLine = currentModel\n ? formatModelSelection(currentModel, input.currentModelVariant, copy)\n : copy.common.openCodeDefault;\n const lines = [\n copy.models.title,\n copy.models.configuredOnly,\n copy.models.current(currentModelLine),\n copy.common.page(page.page + 1, page.totalPages),\n \"\",\n ...page.items.map((model, index) => formatModelListLine(model, page.startIndex + index + 1)),\n ];\n\n return lines.join(\"\\n\");\n}\n\nexport function presentModelVariantsMessage(\n model: OpenCodeConfiguredModel,\n modelIndex: number,\n copy: BotCopy = BOT_COPY,\n): string {\n const variants = getModelVariants(model);\n\n if (variants.length === 0) {\n return [\n copy.models.noReasoningLevels,\n copy.models.model(formatModelLabel(model)),\n ].join(\"\\n\");\n }\n\n const lines = [\n copy.models.reasoningLevelsTitle,\n copy.models.model(formatModelLabel(model)),\n copy.models.modelNumber(modelIndex),\n \"\",\n ...variants.map((variant, index) => `${index + 1}. ${variant}`),\n ];\n\n return lines.join(\"\\n\");\n}\n\nexport function presentModelSwitchMessage(\n model: OpenCodeConfiguredModel,\n variant: string | null,\n copy: BotCopy = BOT_COPY,\n): string {\n return [\n copy.models.switched,\n copy.models.current(formatModelLabel(model)),\n copy.models.currentReasoningLevel(variant ?? copy.models.defaultReasoningLevel),\n ].join(\"\\n\");\n}\n\nexport function presentLanguageMessage(currentLanguage: BotLanguage, copy: BotCopy = BOT_COPY): string {\n return [\n copy.language.title,\n copy.language.current(getLanguageLabel(currentLanguage, copy)),\n \"\",\n copy.language.choose,\n ].join(\"\\n\");\n}\n\nexport function presentLanguageSwitchMessage(currentLanguage: BotLanguage, copy: BotCopy = BOT_COPY): string {\n return [\n copy.language.switched,\n copy.language.current(getLanguageLabel(currentLanguage, copy)),\n ].join(\"\\n\");\n}\n\nexport function getModelVariants(model: OpenCodeConfiguredModel): string[] {\n return Object.keys(model.variants)\n .sort((left, right) => compareVariantNames(left, right));\n}\n\nfunction compareVariantNames(left: string, right: string): number {\n const leftIndex = VARIANT_ORDER.indexOf(left);\n const rightIndex = VARIANT_ORDER.indexOf(right);\n\n if (leftIndex === -1 && rightIndex === -1) {\n return left.localeCompare(right);\n }\n\n if (leftIndex === -1) {\n return 1;\n }\n\n if (rightIndex === -1) {\n return -1;\n }\n\n return leftIndex - rightIndex;\n}\n\nfunction formatAgentLabel(agent: OpenCodeAgent): string {\n return agent.name;\n}\n\nfunction formatModelListLine(model: OpenCodeConfiguredModel, displayIndex: number): string {\n return `${displayIndex}. ${formatModelLabel(model)}`;\n}\n\nfunction formatModelSelection(\n model: OpenCodeConfiguredModel,\n variant: string | null,\n copy: BotCopy,\n): string {\n return `${formatModelLabel(model)} | ${copy.models.reasoningLevel(variant ?? copy.models.defaultReasoningLevel)}`;\n}\n\nfunction formatModelLabel(model: OpenCodeConfiguredModel): string {\n return `${model.providerName} / ${model.name}`;\n}\n\nfunction formatSessionLabel(session: OpenCodeSession): string {\n const title = session.title.trim() || session.slug || session.id;\n\n return title === session.slug\n ? title\n : `${title} [${session.slug}]`;\n}\n\nfunction formatMcpStatus(status: OpenCodeMcpStatus, copy: BotCopy): string {\n switch (status.status) {\n case \"connected\":\n return copy.mcp.connected;\n case \"disabled\":\n return copy.mcp.disabled;\n case \"needs_auth\":\n return copy.mcp.needsAuth;\n case \"failed\":\n return copy.mcp.failed(status.error);\n case \"needs_client_registration\":\n return copy.mcp.needsClientRegistration(status.error);\n }\n\n const exhaustiveStatus: never = status;\n\n return exhaustiveStatus;\n}\n\nfunction formatLspStatus(status: OpenCodeLspStatus, copy: BotCopy): string {\n switch (status.status) {\n case \"connected\":\n return copy.lsp.connected;\n case \"error\":\n return copy.lsp.error;\n }\n\n const exhaustiveStatus: never = status.status;\n\n return exhaustiveStatus;\n}\n","import type { Bot } from \"grammy\";\nimport { getChatCopy } from \"../i18n.js\";\nimport type { BotDependencies } from \"../types.js\";\nimport { buildAgentsKeyboard } from \"../presenters/keyboard.presenter.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport { presentAgentsMessage } from \"../presenters/message.presenter.js\";\n\ninterface AgentsCommandContext {\n chat: { id: number };\n reply(text: string, other?: { reply_markup?: unknown }): Promise<unknown>;\n}\n\nexport async function handleAgentsCommand(ctx: AgentsCommandContext, dependencies: BotDependencies) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n const result = await dependencies.listAgentsUseCase.execute({\n chatId: ctx.chat.id,\n });\n\n if (result.agents.length === 0) {\n await ctx.reply(copy.agents.none);\n return;\n }\n\n const { keyboard, page } = buildAgentsKeyboard(result.agents, 0, copy);\n\n await ctx.reply(\n presentAgentsMessage({\n agents: result.agents,\n currentAgentName: result.currentAgentName,\n page: page.page,\n }, copy),\n {\n reply_markup: keyboard,\n },\n );\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to list agents\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport function registerAgentsCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command([\"agents\", \"agent\"], async (ctx) => {\n await handleAgentsCommand(ctx, dependencies);\n });\n}\n","import type { OpenCodeSession } from \"../services/opencode/opencode.client.js\";\nimport { getChatCopy } from \"./i18n.js\";\nimport {\n buildSessionActionKeyboard,\n buildSessionsKeyboard,\n} from \"./presenters/keyboard.presenter.js\";\nimport {\n presentSessionActionsMessage,\n presentSessionsMessage,\n} from \"./presenters/message.presenter.js\";\nimport type { BotDependencies } from \"./types.js\";\n\ninterface SessionsMessageApi {\n editMessageText(\n chatId: number,\n messageId: number,\n text: string,\n other?: { reply_markup?: unknown },\n ): Promise<unknown>;\n}\n\ninterface SessionsListView {\n copy: Awaited<ReturnType<typeof getChatCopy>>;\n currentDirectory: string;\n currentSessionId: string | null;\n keyboard?: unknown;\n page: number;\n sessions: OpenCodeSession[];\n text: string;\n}\n\nexport async function buildSessionsListView(\n chatId: number,\n requestedPage: number,\n dependencies: BotDependencies,\n): Promise<SessionsListView> {\n const copy = await getChatCopy(dependencies.sessionRepo, chatId);\n const result = await dependencies.listSessionsUseCase.execute({\n chatId,\n });\n\n if (result.sessions.length === 0) {\n return {\n copy,\n currentDirectory: result.currentDirectory,\n currentSessionId: result.currentSessionId,\n page: 0,\n sessions: [],\n text: copy.sessions.none,\n };\n }\n\n const { keyboard, page } = buildSessionsKeyboard(result.sessions, requestedPage, copy);\n\n return {\n copy,\n currentDirectory: result.currentDirectory,\n currentSessionId: result.currentSessionId,\n keyboard,\n page: page.page,\n sessions: result.sessions,\n text: presentSessionsMessage({\n currentDirectory: result.currentDirectory,\n currentSessionId: result.currentSessionId,\n page: page.page,\n sessions: result.sessions,\n }, copy),\n };\n}\n\nexport async function buildSessionActionView(\n chatId: number,\n requestedPage: number,\n sessionId: string,\n dependencies: BotDependencies,\n) {\n const listView = await buildSessionsListView(chatId, requestedPage, dependencies);\n const session = listView.sessions.find((item) => item.id === sessionId);\n\n if (!session) {\n return {\n copy: listView.copy,\n found: false as const,\n };\n }\n\n return {\n copy: listView.copy,\n found: true as const,\n keyboard: buildSessionActionKeyboard(session.id, listView.page, listView.copy),\n page: listView.page,\n session,\n text: presentSessionActionsMessage({\n currentDirectory: listView.currentDirectory,\n session,\n }, listView.copy),\n };\n}\n\nexport async function restoreSessionsListMessage(\n api: SessionsMessageApi,\n chatId: number,\n messageId: number,\n requestedPage: number,\n dependencies: BotDependencies,\n) {\n const listView = await buildSessionsListView(chatId, requestedPage, dependencies);\n\n if (listView.keyboard) {\n await api.editMessageText(chatId, messageId, listView.text, {\n reply_markup: listView.keyboard,\n });\n } else {\n await api.editMessageText(chatId, messageId, listView.text);\n }\n\n return listView;\n}\n","import type { PendingChatAction, SessionRenamePendingAction } from \"../domain/pending-action/pending-action.entity.js\";\nimport { getChatCopy } from \"./i18n.js\";\nimport { restoreSessionsListMessage } from \"./sessions-menu.js\";\nimport { presentError } from \"./presenters/error.presenter.js\";\nimport { presentSessionRenamedMessage } from \"./presenters/message.presenter.js\";\nimport type { BotDependencies } from \"./types.js\";\n\ninterface SessionRenameApi {\n editMessageText(\n chatId: number,\n messageId: number,\n text: string,\n other?: { reply_markup?: unknown },\n ): Promise<unknown>;\n}\n\ninterface SessionRenameReplyContext {\n api: SessionRenameApi;\n chat: { id: number };\n reply(text: string): Promise<unknown>;\n}\n\ninterface SessionRenameTextContext extends SessionRenameReplyContext {\n message: { text?: string };\n}\n\nexport async function getPendingSessionRenameAction(\n dependencies: BotDependencies,\n chatId: number,\n): Promise<SessionRenamePendingAction | null> {\n const action = await dependencies.pendingActionRepo.getByChatId(chatId);\n\n return isSessionRenamePendingAction(action) ? action : null;\n}\n\nexport async function replyIfSessionRenamePending(\n ctx: Pick<SessionRenameReplyContext, \"chat\" | \"reply\">,\n dependencies: BotDependencies,\n): Promise<boolean> {\n const pendingAction = await getPendingSessionRenameAction(dependencies, ctx.chat.id);\n\n if (!pendingAction) {\n return false;\n }\n\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n await ctx.reply(copy.sessions.renamePendingInput);\n\n return true;\n}\n\nexport async function handlePendingSessionRenameText(\n ctx: SessionRenameTextContext,\n dependencies: BotDependencies,\n): Promise<boolean> {\n const pendingAction = await getPendingSessionRenameAction(dependencies, ctx.chat.id);\n\n if (!pendingAction) {\n return false;\n }\n\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n const title = ctx.message.text?.trim() ?? \"\";\n\n if (title.startsWith(\"/\")) {\n await ctx.reply(copy.sessions.renamePendingInput);\n return true;\n }\n\n if (!title) {\n await ctx.reply(copy.sessions.renameEmpty);\n return true;\n }\n\n try {\n const result = await dependencies.renameSessionUseCase.execute({\n chatId: ctx.chat.id,\n sessionId: pendingAction.sessionId,\n title,\n });\n\n await dependencies.pendingActionRepo.clear(ctx.chat.id);\n\n await bestEffortRestoreSessionsList(ctx.api, pendingAction, dependencies);\n\n if (!result.found) {\n await ctx.reply(copy.sessions.renameExpired);\n return true;\n }\n\n await ctx.reply(presentSessionRenamedMessage(result.session, copy));\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to rename session\");\n await ctx.reply(presentError(error, copy));\n }\n\n return true;\n}\n\nexport async function cancelPendingSessionRename(\n ctx: SessionRenameReplyContext,\n dependencies: BotDependencies,\n): Promise<boolean> {\n const pendingAction = await getPendingSessionRenameAction(dependencies, ctx.chat.id);\n\n if (!pendingAction) {\n return false;\n }\n\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n await dependencies.pendingActionRepo.clear(ctx.chat.id);\n await bestEffortRestoreSessionsList(ctx.api, pendingAction, dependencies);\n await ctx.reply(copy.sessions.renameCancelled);\n\n return true;\n}\n\nasync function bestEffortRestoreSessionsList(\n api: SessionRenameApi,\n pendingAction: SessionRenamePendingAction,\n dependencies: BotDependencies,\n) {\n try {\n await restoreSessionsListMessage(\n api,\n pendingAction.chatId,\n pendingAction.menuMessageId,\n pendingAction.returnPage,\n dependencies,\n );\n } catch (error) {\n dependencies.logger.warn?.({ error }, \"failed to restore sessions list message\");\n }\n}\n\nfunction isSessionRenamePendingAction(action: PendingChatAction | null): action is SessionRenamePendingAction {\n return action?.kind === \"session_rename\";\n}\n","import type { Bot } from \"grammy\";\nimport { getChatCopy } from \"../i18n.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport { cancelPendingSessionRename } from \"../session-rename.js\";\nimport type { BotDependencies } from \"../types.js\";\n\ninterface CancelCommandContext {\n api: {\n editMessageText(\n chatId: number,\n messageId: number,\n text: string,\n other?: { reply_markup?: unknown },\n ): Promise<unknown>;\n };\n chat: { id: number };\n reply(text: string): Promise<unknown>;\n}\n\nexport async function handleCancelCommand(ctx: CancelCommandContext, dependencies: BotDependencies) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n if (await cancelPendingSessionRename(ctx, dependencies)) {\n return;\n }\n\n const result = await dependencies.abortPromptUseCase.execute({\n chatId: ctx.chat.id,\n });\n\n if (result.status === \"no_session\") {\n await ctx.reply(copy.abort.noSession);\n return;\n }\n\n if (result.status === \"not_running\") {\n await ctx.reply(copy.abort.notRunning);\n return;\n }\n\n await ctx.reply(copy.abort.aborted);\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to cancel current action\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport function registerCancelCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command(\"cancel\", async (ctx) => {\n await handleCancelCommand(ctx, dependencies);\n });\n}\n","import type { Bot } from \"grammy\";\nimport { isBotLanguage } from \"../copy.js\";\nimport { getChatCopy, getChatLanguage, setChatLanguage } from \"../i18n.js\";\nimport { buildLanguageKeyboard } from \"../presenters/keyboard.presenter.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport {\n presentLanguageMessage,\n presentLanguageSwitchMessage,\n} from \"../presenters/message.presenter.js\";\nimport { syncTelegramCommandsForChat } from \"./sync-commands.js\";\nimport type { BotDependencies } from \"../types.js\";\n\ninterface LanguageCommandContext {\n chat: { id: number };\n api: Pick<Bot[\"api\"], \"setMyCommands\">;\n reply(text: string, other?: { reply_markup?: unknown }): Promise<unknown>;\n}\n\nexport async function handleLanguageCommand(ctx: LanguageCommandContext, dependencies: BotDependencies) {\n const language = await getChatLanguage(dependencies.sessionRepo, ctx.chat.id);\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n await syncTelegramCommandsForChat(ctx.api, ctx.chat.id, language);\n await ctx.reply(\n presentLanguageMessage(language, copy),\n {\n reply_markup: buildLanguageKeyboard(language, copy),\n },\n );\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to show language options\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport async function switchLanguageForChat(\n api: Pick<Bot[\"api\"], \"setMyCommands\">,\n chatId: number,\n language: string,\n dependencies: BotDependencies,\n): Promise<\n | { found: false; copy: Awaited<ReturnType<typeof getChatCopy>> }\n | {\n found: true;\n copy: Awaited<ReturnType<typeof getChatCopy>>;\n language: Awaited<ReturnType<typeof getChatLanguage>>;\n }\n> {\n const currentCopy = await getChatCopy(dependencies.sessionRepo, chatId);\n\n if (!isBotLanguage(language)) {\n return {\n found: false,\n copy: currentCopy,\n };\n }\n\n await setChatLanguage(dependencies.sessionRepo, chatId, language);\n await syncTelegramCommandsForChat(api, chatId, language);\n\n return {\n found: true,\n copy: await getChatCopy(dependencies.sessionRepo, chatId),\n language,\n };\n}\n\nexport async function presentLanguageSwitchForChat(\n chatId: number,\n api: Pick<Bot[\"api\"], \"setMyCommands\">,\n language: string,\n dependencies: BotDependencies,\n) {\n const result = await switchLanguageForChat(api, chatId, language, dependencies);\n\n if (!result.found) {\n return {\n found: false as const,\n copy: result.copy,\n text: result.copy.language.expired,\n keyboard: buildLanguageKeyboard(await getChatLanguage(dependencies.sessionRepo, chatId), result.copy),\n };\n }\n\n return {\n found: true as const,\n copy: result.copy,\n text: presentLanguageSwitchMessage(result.language, result.copy),\n keyboard: buildLanguageKeyboard(result.language, result.copy),\n };\n}\n\nexport function registerLanguageCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command(\"language\", async (ctx) => {\n await handleLanguageCommand(ctx, dependencies);\n });\n}\n","import type { Bot } from \"grammy\";\nimport { getChatCopy } from \"../i18n.js\";\nimport type { BotDependencies } from \"../types.js\";\nimport { buildModelsKeyboard } from \"../presenters/keyboard.presenter.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport { presentModelsMessage } from \"../presenters/message.presenter.js\";\n\ninterface ModelsCommandContext {\n chat: { id: number };\n reply(text: string, other?: { reply_markup?: unknown }): Promise<unknown>;\n}\n\nexport async function handleModelsCommand(ctx: ModelsCommandContext, dependencies: BotDependencies) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n const result = await dependencies.listModelsUseCase.execute({\n chatId: ctx.chat.id,\n });\n\n if (result.models.length === 0) {\n await ctx.reply(copy.models.none);\n return;\n }\n\n const { keyboard, page } = buildModelsKeyboard(result.models, 0, copy);\n\n await ctx.reply(\n presentModelsMessage({\n currentModelId: result.currentModelId,\n currentModelProviderId: result.currentModelProviderId,\n currentModelVariant: result.currentModelVariant,\n models: result.models,\n page: page.page,\n }, copy),\n {\n reply_markup: keyboard,\n },\n );\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to list models\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport function registerModelsCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command([\"model\", \"models\"], async (ctx) => {\n await handleModelsCommand(ctx, dependencies);\n });\n}\n","import type { Bot } from \"grammy\";\nimport { getChatCopy } from \"../i18n.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport { presentSessionCreatedMessage } from \"../presenters/message.presenter.js\";\nimport type { BotDependencies } from \"../types.js\";\n\ninterface NewCommandContext {\n chat: { id: number };\n message?: { text?: string };\n match?: string;\n reply(text: string): Promise<unknown>;\n}\n\nexport async function handleNewCommand(ctx: NewCommandContext, dependencies: BotDependencies) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n const title = extractSessionTitle(ctx);\n const result = await dependencies.createSessionUseCase.execute({\n chatId: ctx.chat.id,\n title,\n });\n\n await ctx.reply(presentSessionCreatedMessage(result.session, copy));\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to create new session\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport function registerNewCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command(\"new\", async (ctx) => {\n await handleNewCommand(ctx, dependencies);\n });\n}\n\nfunction extractSessionTitle(ctx: NewCommandContext): string | null {\n if (typeof ctx.match === \"string\") {\n const title = ctx.match.trim();\n\n return title ? title : null;\n }\n\n const messageText = ctx.message?.text?.trim();\n\n if (!messageText) {\n return null;\n }\n\n const match = messageText.match(/^\\/new(?:@\\S+)?(?:\\s+([\\s\\S]*))?$/i);\n const title = match?.[1]?.trim();\n\n return title ? title : null;\n}\n","import { BOT_COPY, type BotCopy } from \"../../bot/copy.js\";\nimport type {\n PromptSessionMetrics,\n PromptSessionResult,\n} from \"../opencode/opencode.client.js\";\n\nconst MAX_TELEGRAM_MESSAGE_LENGTH = 4096;\nconst TRUNCATED_SUFFIX = \"...\";\nconst MARKDOWN_SPECIAL_CHARACTERS = /([_*\\[\\]()~`>#+\\-=|{}.!\\\\])/g;\n\nexport interface TelegramReplyPayload {\n options?: {\n parse_mode: \"MarkdownV2\";\n };\n text: string;\n}\n\nexport interface TelegramPromptReply {\n fallback: TelegramReplyPayload;\n preferred: TelegramReplyPayload;\n}\n\nexport interface TelegramStaticReply {\n fallback: TelegramReplyPayload;\n preferred: TelegramReplyPayload;\n}\n\nexport function buildTelegramPromptReply(\n result: PromptSessionResult,\n copy: BotCopy = BOT_COPY,\n): TelegramPromptReply {\n const renderedMarkdown = result.bodyMd ? renderMarkdownToTelegramMarkdownV2(result.bodyMd) : null;\n const footerPlain = formatPlainMetricsFooter(result.metrics, copy);\n const plainBody = normalizePlainBody(result, renderedMarkdown !== null, copy);\n const fallback = {\n text: joinBodyAndFooter(\n truncatePlainBody(plainBody, footerPlain),\n footerPlain,\n ),\n };\n\n if (!renderedMarkdown) {\n return {\n preferred: fallback,\n fallback,\n };\n }\n\n const footerMarkdown = formatMarkdownMetricsFooter(result.metrics, copy);\n const markdownText = joinBodyAndFooter(renderedMarkdown, footerMarkdown);\n\n if (markdownText.length > MAX_TELEGRAM_MESSAGE_LENGTH) {\n return {\n preferred: fallback,\n fallback,\n };\n }\n\n return {\n preferred: {\n text: markdownText,\n options: {\n parse_mode: \"MarkdownV2\",\n },\n },\n fallback,\n };\n}\n\nexport function buildTelegramStaticReply(markdown: string): TelegramStaticReply {\n const renderedMarkdown = renderMarkdownToTelegramMarkdownV2(markdown);\n const plainText = truncateStaticText(stripMarkdownToPlainText(markdown) || markdown.trim());\n const fallback = {\n text: plainText,\n };\n\n if (!renderedMarkdown || renderedMarkdown.length > MAX_TELEGRAM_MESSAGE_LENGTH) {\n return {\n preferred: fallback,\n fallback,\n };\n }\n\n return {\n preferred: {\n text: renderedMarkdown,\n options: {\n parse_mode: \"MarkdownV2\",\n },\n },\n fallback,\n };\n}\n\nexport function renderMarkdownToTelegramMarkdownV2(markdown: string): string | null {\n const normalizedMarkdown = preprocessMarkdownForTelegram(markdown).trim();\n\n if (!normalizedMarkdown) {\n return null;\n }\n\n const lines = normalizedMarkdown.split(\"\\n\");\n const rendered: string[] = [];\n let inCodeBlock = false;\n let codeBlockLanguage = \"\";\n\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index];\n const fenceMatch = line.match(/^```([A-Za-z0-9_-]+)?\\s*$/);\n\n if (fenceMatch) {\n if (inCodeBlock) {\n rendered.push(\"```\");\n inCodeBlock = false;\n codeBlockLanguage = \"\";\n } else {\n codeBlockLanguage = fenceMatch[1] ?? \"\";\n rendered.push(`\\`\\`\\`${codeBlockLanguage}`);\n inCodeBlock = true;\n }\n\n continue;\n }\n\n if (inCodeBlock) {\n rendered.push(escapeCodeContent(line));\n continue;\n }\n\n if (line.includes(\"![\")) {\n return null;\n }\n\n const tableBlock = consumeMarkdownTable(lines, index);\n\n if (tableBlock) {\n rendered.push(renderTableAsTelegramCodeBlock(tableBlock.rows));\n index = tableBlock.nextIndex - 1;\n continue;\n }\n\n if (/<[/A-Za-z][^>]*>/.test(line)) {\n return null;\n }\n\n if (!line.trim()) {\n rendered.push(\"\");\n continue;\n }\n\n const headingMatch = line.match(/^\\s{0,3}(#{1,6})\\s+(.+)$/);\n\n if (headingMatch) {\n rendered.push(`*${escapeMarkdownText(headingMatch[2].trim())}*`);\n continue;\n }\n\n const blockquoteMatch = line.match(/^\\s*>\\s?(.*)$/);\n\n if (blockquoteMatch) {\n const quote = renderInlineMarkdown(blockquoteMatch[1]);\n\n if (quote === null) {\n return null;\n }\n\n rendered.push(`>${quote ? ` ${quote}` : \"\"}`);\n continue;\n }\n\n const unorderedListMatch = line.match(/^(\\s*)[-+*]\\s+(.+)$/);\n\n if (unorderedListMatch) {\n const item = renderInlineMarkdown(unorderedListMatch[2]);\n\n if (item === null) {\n return null;\n }\n\n rendered.push(`${unorderedListMatch[1]}\\\\- ${item}`);\n continue;\n }\n\n const orderedListMatch = line.match(/^(\\s*)(\\d+)[.)]\\s+(.+)$/);\n\n if (orderedListMatch) {\n const item = renderInlineMarkdown(orderedListMatch[3]);\n\n if (item === null) {\n return null;\n }\n\n rendered.push(`${orderedListMatch[1]}${orderedListMatch[2]}\\\\. ${item}`);\n continue;\n }\n\n const paragraph = renderInlineMarkdown(line);\n\n if (paragraph === null) {\n return null;\n }\n\n rendered.push(paragraph);\n }\n\n if (inCodeBlock) {\n return null;\n }\n\n return rendered.join(\"\\n\");\n}\n\nexport function stripMarkdownToPlainText(markdown: string): string {\n const normalizedMarkdown = preprocessMarkdownForTelegram(markdown);\n const lines = normalizedMarkdown.split(\"\\n\");\n const rendered: string[] = [];\n\n for (let index = 0; index < lines.length; index += 1) {\n const tableBlock = consumeMarkdownTable(lines, index);\n\n if (tableBlock) {\n rendered.push(renderTableAsPlainText(tableBlock.rows));\n index = tableBlock.nextIndex - 1;\n continue;\n }\n\n rendered.push(lines[index]);\n }\n\n return rendered\n .join(\"\\n\")\n .replace(/```[A-Za-z0-9_-]*\\n?/g, \"\")\n .replace(/```/g, \"\")\n .replace(/^#{1,6}\\s+/gm, \"\")\n .replace(/^\\s*>\\s?/gm, \"\")\n .replace(/^(\\s*)[-+*]\\s+/gm, \"$1- \")\n .replace(/^(\\s*)(\\d+)[.)]\\s+/gm, \"$1$2. \")\n .replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, \"$1 ($2)\")\n .replace(/(\\*\\*|__)(.*?)\\1/g, \"$2\")\n .replace(/(\\*|_)(.*?)\\1/g, \"$2\")\n .replace(/`([^`]+)`/g, \"$1\")\n .trim();\n}\n\nfunction normalizePlainBody(\n result: PromptSessionResult,\n preferStructured: boolean,\n copy: BotCopy,\n): string {\n const fromStructured = result.bodyMd ? stripMarkdownToPlainText(result.bodyMd) : \"\";\n const fromFallback = result.fallbackText.trim();\n const body = preferStructured\n ? fromStructured || fromFallback\n : fromFallback || fromStructured;\n\n return body.trim() || result.fallbackText || copy.prompt.emptyResponse;\n}\n\nfunction truncatePlainBody(body: string, footer: string): string {\n const reservedLength = footer.length + 2;\n const maxBodyLength = Math.max(0, MAX_TELEGRAM_MESSAGE_LENGTH - reservedLength);\n\n if (body.length <= maxBodyLength) {\n return body;\n }\n\n return `${body.slice(0, Math.max(0, maxBodyLength - TRUNCATED_SUFFIX.length))}${TRUNCATED_SUFFIX}`;\n}\n\nfunction truncateStaticText(text: string): string {\n if (text.length <= MAX_TELEGRAM_MESSAGE_LENGTH) {\n return text;\n }\n\n return `${text.slice(0, Math.max(0, MAX_TELEGRAM_MESSAGE_LENGTH - TRUNCATED_SUFFIX.length))}${TRUNCATED_SUFFIX}`;\n}\n\nfunction joinBodyAndFooter(body: string, footer: string): string {\n return `${body}\\n\\n${footer}`;\n}\n\nfunction preprocessMarkdownForTelegram(markdown: string): string {\n const normalizedMarkdown = markdown.replace(/\\r\\n?/g, \"\\n\");\n const lines = normalizedMarkdown.split(\"\\n\");\n const processed: string[] = [];\n let activeFence: \"markdown\" | \"plain\" | null = null;\n\n for (const line of lines) {\n const fenceMatch = line.match(/^```([A-Za-z0-9_-]+)?\\s*$/);\n\n if (fenceMatch) {\n const language = (fenceMatch[1] ?? \"\").toLowerCase();\n\n if (activeFence === \"markdown\") {\n activeFence = null;\n continue;\n }\n\n if (activeFence === \"plain\") {\n processed.push(line);\n activeFence = null;\n continue;\n }\n\n if (language === \"md\" || language === \"markdown\") {\n activeFence = \"markdown\";\n continue;\n }\n\n activeFence = \"plain\";\n processed.push(line);\n continue;\n }\n\n processed.push(line);\n }\n\n return processed.join(\"\\n\");\n}\n\nfunction formatPlainMetricsFooter(metrics: PromptSessionMetrics, copy: BotCopy): string {\n return `${copy.replyMetrics.durationLabel}: ${formatDuration(metrics.durationMs, copy)} | ${copy.replyMetrics.tokensLabel}: ${formatMetricValue(metrics.tokens.total, copy)}`;\n}\n\nfunction formatMarkdownMetricsFooter(metrics: PromptSessionMetrics, copy: BotCopy): string {\n return escapeMarkdownText(formatPlainMetricsFooter(metrics, copy));\n}\n\nfunction formatDuration(durationMs: number | null, copy: BotCopy): string {\n if (durationMs === null || !Number.isFinite(durationMs)) {\n return copy.replyMetrics.notAvailable;\n }\n\n if (durationMs < 1000) {\n return `${Math.round(durationMs)} ms`;\n }\n\n return `${(durationMs / 1000).toFixed(1)} s`;\n}\n\nfunction formatMetricValue(value: number | null, copy: BotCopy): string {\n if (value === null || !Number.isFinite(value)) {\n return copy.replyMetrics.notAvailable;\n }\n\n return `${Math.round(value)}`;\n}\n\ninterface ConsumedTable {\n nextIndex: number;\n rows: string[][];\n}\n\nfunction consumeMarkdownTable(lines: string[], startIndex: number): ConsumedTable | null {\n if (startIndex + 1 >= lines.length) {\n return null;\n }\n\n const headerCells = parseMarkdownTableRow(lines[startIndex]);\n const separatorCells = parseMarkdownTableSeparator(lines[startIndex + 1]);\n\n if (!headerCells || !separatorCells || headerCells.length !== separatorCells.length) {\n return null;\n }\n\n const rows = [headerCells];\n let index = startIndex + 2;\n\n while (index < lines.length) {\n const rowCells = parseMarkdownTableRow(lines[index]);\n\n if (!rowCells || rowCells.length !== headerCells.length) {\n break;\n }\n\n rows.push(rowCells);\n index += 1;\n }\n\n return {\n rows,\n nextIndex: index,\n };\n}\n\nfunction parseMarkdownTableRow(line: string): string[] | null {\n const trimmed = line.trim();\n\n if (!trimmed.includes(\"|\")) {\n return null;\n }\n\n const cells = splitMarkdownTableCells(trimmed)\n .map((cell) => normalizeTableCell(cell));\n\n return cells.length >= 2 ? cells : null;\n}\n\nfunction parseMarkdownTableSeparator(line: string): string[] | null {\n const cells = splitMarkdownTableCells(line.trim());\n\n if (cells.length < 2) {\n return null;\n }\n\n return cells.every((cell) => /^:?-{3,}:?$/.test(cell.trim()))\n ? cells\n : null;\n}\n\nfunction splitMarkdownTableCells(line: string): string[] {\n const content = line\n .replace(/^\\|/, \"\")\n .replace(/\\|$/, \"\");\n const cells: string[] = [];\n let current = \"\";\n let escaped = false;\n\n for (const char of content) {\n if (escaped) {\n current += char;\n escaped = false;\n continue;\n }\n\n if (char === \"\\\\\") {\n escaped = true;\n current += char;\n continue;\n }\n\n if (char === \"|\") {\n cells.push(current);\n current = \"\";\n continue;\n }\n\n current += char;\n }\n\n cells.push(current);\n return cells;\n}\n\nfunction normalizeTableCell(cell: string): string {\n return cell\n .trim()\n .replace(/\\\\\\|/g, \"|\")\n .replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, \"$1 ($2)\")\n .replace(/(\\*\\*|__)(.*?)\\1/g, \"$2\")\n .replace(/(\\*|_)(.*?)\\1/g, \"$2\")\n .replace(/`([^`]+)`/g, \"$1\");\n}\n\nfunction renderTableAsTelegramCodeBlock(rows: string[][]): string {\n return [\n \"```\",\n ...buildAlignedTableLines(rows).map((line) => escapeCodeContent(line)),\n \"```\",\n ].join(\"\\n\");\n}\n\nfunction renderTableAsPlainText(rows: string[][]): string {\n return buildAlignedTableLines(rows).join(\"\\n\");\n}\n\nfunction buildAlignedTableLines(rows: string[][]): string[] {\n const columnWidths = calculateTableColumnWidths(rows);\n const header = formatTableRow(rows[0], columnWidths);\n const separator = columnWidths.map((width) => \"-\".repeat(Math.max(3, width))).join(\"-+-\");\n const body = rows.slice(1).map((row) => formatTableRow(row, columnWidths));\n\n return [header, separator, ...body];\n}\n\nfunction calculateTableColumnWidths(rows: string[][]): number[] {\n return rows[0].map((_, columnIndex) =>\n rows.reduce((maxWidth, row) => Math.max(maxWidth, getDisplayWidth(row[columnIndex] ?? \"\")), 0),\n );\n}\n\nfunction formatTableRow(row: string[], columnWidths: number[]): string {\n return row\n .map((cell, index) => padDisplayWidth(cell, columnWidths[index] ?? 0))\n .join(\" | \");\n}\n\nfunction padDisplayWidth(value: string, targetWidth: number): string {\n const padding = Math.max(0, targetWidth - getDisplayWidth(value));\n\n return `${value}${\" \".repeat(padding)}`;\n}\n\nfunction getDisplayWidth(value: string): number {\n let width = 0;\n\n for (const char of value) {\n width += isWideCharacter(char.codePointAt(0) ?? 0) ? 2 : 1;\n }\n\n return width;\n}\n\nfunction isWideCharacter(codePoint: number): boolean {\n return codePoint >= 0x1100 && (\n codePoint <= 0x115f ||\n codePoint === 0x2329 ||\n codePoint === 0x232a ||\n (codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) ||\n (codePoint >= 0xac00 && codePoint <= 0xd7a3) ||\n (codePoint >= 0xf900 && codePoint <= 0xfaff) ||\n (codePoint >= 0xfe10 && codePoint <= 0xfe19) ||\n (codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||\n (codePoint >= 0xff00 && codePoint <= 0xff60) ||\n (codePoint >= 0xffe0 && codePoint <= 0xffe6)\n );\n}\n\nfunction renderInlineMarkdown(input: string): string | null {\n let output = \"\";\n let cursor = 0;\n\n while (cursor < input.length) {\n if (input.startsWith(\"![\", cursor)) {\n return null;\n }\n\n if (input.startsWith(\"**\", cursor) || input.startsWith(\"__\", cursor)) {\n const delimiter = input.slice(cursor, cursor + 2);\n const closingIndex = input.indexOf(delimiter, cursor + 2);\n\n if (closingIndex > cursor + 2) {\n output += `*${escapeMarkdownText(input.slice(cursor + 2, closingIndex))}*`;\n cursor = closingIndex + 2;\n continue;\n }\n }\n\n if (input[cursor] === \"*\" || input[cursor] === \"_\") {\n const delimiter = input[cursor];\n const closingIndex = input.indexOf(delimiter, cursor + 1);\n\n if (closingIndex > cursor + 1) {\n output += `_${escapeMarkdownText(input.slice(cursor + 1, closingIndex))}_`;\n cursor = closingIndex + 1;\n continue;\n }\n }\n\n if (input[cursor] === \"`\") {\n const closingIndex = input.indexOf(\"`\", cursor + 1);\n\n if (closingIndex === -1) {\n return null;\n }\n\n output += `\\`${escapeCodeContent(input.slice(cursor + 1, closingIndex))}\\``;\n cursor = closingIndex + 1;\n continue;\n }\n\n if (input[cursor] === \"[\") {\n const linkMatch = input.slice(cursor).match(/^\\[([^\\]]+)\\]\\(([^)\\s]+)\\)/);\n\n if (!linkMatch) {\n return null;\n }\n\n output += `[${escapeMarkdownText(linkMatch[1])}](${escapeLinkDestination(linkMatch[2])})`;\n cursor += linkMatch[0].length;\n continue;\n }\n\n output += escapeMarkdownText(input[cursor]);\n cursor += 1;\n }\n\n return output;\n}\n\nfunction escapeMarkdownText(text: string): string {\n return text.replace(MARKDOWN_SPECIAL_CHARACTERS, \"\\\\$1\");\n}\n\nfunction escapeCodeContent(text: string): string {\n return text\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/`/g, \"\\\\`\");\n}\n\nfunction escapeLinkDestination(url: string): string {\n return url\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\\)/g, \"\\\\)\")\n .replace(/\\(/g, \"\\\\(\");\n}\n","import type { Bot } from \"grammy\";\nimport { getChatCopy } from \"../i18n.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport {\n presentStatusMarkdownMessage,\n presentStatusMessage,\n} from \"../presenters/message.presenter.js\";\nimport { renderMarkdownToTelegramMarkdownV2 } from \"../../services/telegram/telegram-format.js\";\nimport type { BotDependencies } from \"../types.js\";\n\ninterface StatusCommandContext {\n chat?: { id: number };\n reply(\n text: string,\n options?: {\n parse_mode: \"MarkdownV2\";\n },\n ): Promise<unknown>;\n}\n\nexport async function handleStatusCommand(ctx: StatusCommandContext, dependencies: BotDependencies) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat?.id);\n\n try {\n const result = await dependencies.getStatusUseCase.execute({\n chatId: ctx.chat?.id ?? 0,\n });\n\n const renderedMarkdown = renderMarkdownToTelegramMarkdownV2(presentStatusMarkdownMessage(result, copy));\n\n if (renderedMarkdown) {\n await ctx.reply(renderedMarkdown, { parse_mode: \"MarkdownV2\" });\n return;\n }\n\n await ctx.reply(presentStatusMessage(result, copy));\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to fetch system status\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport function registerStatusCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command(\"status\", async (ctx) => {\n await handleStatusCommand(ctx, dependencies);\n });\n}\n","import type { Bot } from \"grammy\";\nimport { getChatCopy } from \"../i18n.js\";\nimport type { BotDependencies } from \"../types.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport { buildSessionsListView } from \"../sessions-menu.js\";\n\ninterface SessionsCommandContext {\n chat: { id: number };\n reply(text: string, other?: { reply_markup?: unknown }): Promise<unknown>;\n}\n\nexport async function handleSessionsCommand(ctx: SessionsCommandContext, dependencies: BotDependencies) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n await dependencies.pendingActionRepo.clear(ctx.chat.id);\n const view = await buildSessionsListView(ctx.chat.id, 0, dependencies);\n\n await ctx.reply(view.text, view.keyboard ? { reply_markup: view.keyboard } : undefined);\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to list sessions\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport function registerSessionsCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command(\"sessions\", async (ctx) => {\n await handleSessionsCommand(ctx, dependencies);\n });\n}\n","import { BOT_COPY, type BotCopy } from \"../copy.js\";\n\nexport function presentStartMarkdownMessage(copy: BotCopy = BOT_COPY): string {\n return copy.start.lines.join(\"\\n\");\n}\n","import type { Bot } from \"grammy\";\nimport { buildTelegramStaticReply } from \"../../services/telegram/telegram-format.js\";\nimport { getChatCopy } from \"../i18n.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport { presentStartMarkdownMessage } from \"../presenters/static.presenter.js\";\nimport type { BotDependencies } from \"../types.js\";\n\ninterface StartCommandContext {\n chat?: { id: number };\n reply(\n text: string,\n other?: {\n parse_mode: \"MarkdownV2\";\n },\n ): Promise<unknown>;\n}\n\nexport async function handleStartCommand(ctx: StartCommandContext, dependencies: BotDependencies) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat?.id);\n const reply = buildTelegramStaticReply(presentStartMarkdownMessage(copy));\n\n try {\n await ctx.reply(reply.preferred.text, reply.preferred.options);\n } catch (error) {\n if (reply.preferred.options) {\n dependencies.logger.error({ error }, \"failed to send start markdown reply, falling back to plain text\");\n await ctx.reply(reply.fallback.text);\n return;\n }\n\n dependencies.logger.error({ error }, \"failed to show start message\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport function registerStartCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command(\"start\", async (ctx) => {\n await handleStartCommand(ctx, dependencies);\n });\n}\n","import type { Bot } from \"grammy\";\nimport type { PermissionApprovalRepository } from \"../../domain/permission/approval.repository.js\";\nimport { getChatCopy, getChatLanguage } from \"../i18n.js\";\nimport { presentLanguageSwitchForChat } from \"../commands/language.js\";\nimport type {\n AgentsCallbackData,\n BotDependencies,\n LanguageCallbackData,\n ModelCallbackData,\n PermissionApprovalCallbackData,\n SessionsCallbackData,\n} from \"../types.js\";\nimport {\n buildAgentsKeyboard,\n buildLanguageKeyboard,\n buildModelsKeyboard,\n buildModelVariantsKeyboard,\n} from \"../presenters/keyboard.presenter.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport {\n getModelVariants,\n presentAgentsMessage,\n presentAgentSwitchMessage,\n presentLanguageMessage,\n presentModelsMessage,\n presentModelSwitchMessage,\n presentModelVariantsMessage,\n presentSessionRenamePromptMessage,\n presentSessionSwitchMessage,\n} from \"../presenters/message.presenter.js\";\nimport {\n buildSessionActionView,\n buildSessionsListView,\n} from \"../sessions-menu.js\";\nimport type { OpenCodeClientLike } from \"../../services/opencode/opencode.client.js\";\nimport {\n buildPermissionApprovalResolvedMessage,\n parsePermissionApprovalCallbackData,\n} from \"../../services/permission/telegram-approval.js\";\n\nconst AGENTS_PAGE_PREFIX = \"agents:page:\";\nconst AGENTS_SELECT_PREFIX = \"agents:select:\";\nconst SESSIONS_PAGE_PREFIX = \"sessions:page:\";\nconst SESSIONS_PICK_PREFIX = \"sessions:pick:\";\nconst SESSIONS_SWITCH_PREFIX = \"sessions:switch:\";\nconst SESSIONS_RENAME_PREFIX = \"sessions:rename:\";\nconst SESSIONS_BACK_PREFIX = \"sessions:back:\";\nconst MODEL_PAGE_PREFIX = \"model:page:\";\nconst MODEL_PICK_PREFIX = \"model:pick:\";\nconst MODEL_VARIANT_PREFIX = \"model:variant:\";\nconst LANGUAGE_SELECT_PREFIX = \"language:select:\";\nconst PERMISSION_PREFIX = \"permission:\";\n\ntype PermissionApprovalDependencies = BotDependencies & {\n opencodeClient: OpenCodeClientLike;\n permissionApprovalRepo: PermissionApprovalRepository;\n};\n\ninterface CallbackContext {\n chat?: { id: number };\n callbackQuery: { data: string; message?: { message_id: number } };\n answerCallbackQuery(): Promise<unknown>;\n editMessageText(text: string, other?: { reply_markup?: unknown }): Promise<unknown>;\n api?: Pick<Bot[\"api\"], \"setMyCommands\">;\n}\n\nexport async function handleAgentsCallback(ctx: CallbackContext, dependencies: BotDependencies) {\n const data = ctx.callbackQuery.data as AgentsCallbackData;\n\n if (!data.startsWith(\"agents:\")) {\n return;\n }\n\n await ctx.answerCallbackQuery();\n\n if (!ctx.chat) {\n return;\n }\n\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n if (data.startsWith(AGENTS_PAGE_PREFIX)) {\n const requestedPage = Number(data.slice(AGENTS_PAGE_PREFIX.length));\n const result = await dependencies.listAgentsUseCase.execute({\n chatId: ctx.chat.id,\n });\n\n if (result.agents.length === 0) {\n await ctx.editMessageText(copy.agents.none);\n return;\n }\n\n const { keyboard, page } = buildAgentsKeyboard(result.agents, requestedPage, copy);\n\n await ctx.editMessageText(\n presentAgentsMessage({\n agents: result.agents,\n currentAgentName: result.currentAgentName,\n page: page.page,\n }, copy),\n {\n reply_markup: keyboard,\n },\n );\n\n return;\n }\n\n if (data.startsWith(AGENTS_SELECT_PREFIX)) {\n const agentIndex = Number(data.slice(AGENTS_SELECT_PREFIX.length));\n const result = await dependencies.listAgentsUseCase.execute({\n chatId: ctx.chat.id,\n });\n const agent = result.agents[agentIndex - 1];\n\n if (!agent) {\n await ctx.editMessageText(copy.agents.expired);\n return;\n }\n\n const switchResult = await dependencies.switchAgentUseCase.execute({\n chatId: ctx.chat.id,\n agentName: agent.name,\n });\n\n if (!switchResult.found) {\n await ctx.editMessageText(copy.agents.expired);\n return;\n }\n\n await ctx.editMessageText(presentAgentSwitchMessage(switchResult.agent, copy));\n }\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to handle agent callback\");\n await ctx.editMessageText(presentError(error, copy));\n }\n}\n\nexport async function handleModelsCallback(ctx: CallbackContext, dependencies: BotDependencies) {\n const data = ctx.callbackQuery.data as ModelCallbackData;\n\n if (!data.startsWith(\"model:\")) {\n return;\n }\n\n await ctx.answerCallbackQuery();\n\n if (!ctx.chat) {\n return;\n }\n\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n if (data.startsWith(MODEL_PAGE_PREFIX)) {\n const requestedPage = Number(data.slice(MODEL_PAGE_PREFIX.length));\n const result = await dependencies.listModelsUseCase.execute({\n chatId: ctx.chat.id,\n });\n\n if (result.models.length === 0) {\n await ctx.editMessageText(copy.models.none);\n return;\n }\n\n const { keyboard, page } = buildModelsKeyboard(result.models, requestedPage, copy);\n\n await ctx.editMessageText(\n presentModelsMessage({\n currentModelId: result.currentModelId,\n currentModelProviderId: result.currentModelProviderId,\n currentModelVariant: result.currentModelVariant,\n models: result.models,\n page: page.page,\n }, copy),\n {\n reply_markup: keyboard,\n },\n );\n\n return;\n }\n\n if (data.startsWith(MODEL_PICK_PREFIX)) {\n const modelIndex = Number(data.slice(MODEL_PICK_PREFIX.length));\n const result = await dependencies.listModelsUseCase.execute({\n chatId: ctx.chat.id,\n });\n const model = result.models[modelIndex - 1];\n\n if (!model) {\n await ctx.editMessageText(copy.models.expired);\n return;\n }\n\n const variants = getModelVariants(model);\n\n if (variants.length === 0) {\n const switchResult = await dependencies.switchModelUseCase.execute({\n chatId: ctx.chat.id,\n providerId: model.providerID,\n modelId: model.id,\n });\n\n if (!switchResult.found) {\n await ctx.editMessageText(copy.models.expired);\n return;\n }\n\n await ctx.editMessageText(presentModelSwitchMessage(switchResult.model, switchResult.variant, copy));\n return;\n }\n\n await ctx.editMessageText(\n presentModelVariantsMessage(model, modelIndex, copy),\n {\n reply_markup: buildModelVariantsKeyboard(variants, modelIndex),\n },\n );\n\n return;\n }\n\n if (data.startsWith(MODEL_VARIANT_PREFIX)) {\n const suffix = data.slice(MODEL_VARIANT_PREFIX.length);\n const [modelIndexRaw, variantIndexRaw] = suffix.split(\":\");\n const modelIndex = Number(modelIndexRaw);\n const variantIndex = Number(variantIndexRaw);\n const result = await dependencies.listModelsUseCase.execute({\n chatId: ctx.chat.id,\n });\n const model = result.models[modelIndex - 1];\n\n if (!model) {\n await ctx.editMessageText(copy.models.expired);\n return;\n }\n\n const variants = getModelVariants(model);\n const variant = variants[variantIndex - 1];\n\n if (!variant) {\n await ctx.editMessageText(copy.models.reasoningLevelExpired);\n return;\n }\n\n const switchResult = await dependencies.switchModelUseCase.execute({\n chatId: ctx.chat.id,\n providerId: model.providerID,\n modelId: model.id,\n variant,\n });\n\n if (!switchResult.found) {\n await ctx.editMessageText(copy.models.expired);\n return;\n }\n\n await ctx.editMessageText(presentModelSwitchMessage(switchResult.model, switchResult.variant, copy));\n }\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to handle model callback\");\n await ctx.editMessageText(presentError(error, copy));\n }\n}\n\nexport async function handleSessionsCallback(ctx: CallbackContext, dependencies: BotDependencies) {\n const data = ctx.callbackQuery.data as SessionsCallbackData;\n\n if (!data.startsWith(\"sessions:\")) {\n return;\n }\n\n await ctx.answerCallbackQuery();\n\n if (!ctx.chat) {\n return;\n }\n\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n if (data.startsWith(SESSIONS_PAGE_PREFIX)) {\n const requestedPage = Number(data.slice(SESSIONS_PAGE_PREFIX.length));\n const view = await buildSessionsListView(ctx.chat.id, requestedPage, dependencies);\n\n await ctx.editMessageText(view.text, view.keyboard ? { reply_markup: view.keyboard } : undefined);\n\n return;\n }\n\n if (data.startsWith(SESSIONS_BACK_PREFIX)) {\n const requestedPage = Number(data.slice(SESSIONS_BACK_PREFIX.length));\n const view = await buildSessionsListView(ctx.chat.id, requestedPage, dependencies);\n\n await ctx.editMessageText(view.text, view.keyboard ? { reply_markup: view.keyboard } : undefined);\n return;\n }\n\n if (data.startsWith(SESSIONS_PICK_PREFIX)) {\n const target = parseSessionActionTarget(data, SESSIONS_PICK_PREFIX);\n\n if (!target) {\n await ctx.editMessageText(copy.sessions.expired);\n return;\n }\n\n const view = await buildSessionActionView(\n ctx.chat.id,\n target.page,\n target.sessionId,\n dependencies,\n );\n\n if (!view.found) {\n await ctx.editMessageText(copy.sessions.expired);\n return;\n }\n\n await ctx.editMessageText(view.text, {\n reply_markup: view.keyboard,\n });\n return;\n }\n\n if (data.startsWith(SESSIONS_SWITCH_PREFIX)) {\n const target = parseSessionActionTarget(data, SESSIONS_SWITCH_PREFIX);\n\n if (!target) {\n await ctx.editMessageText(copy.sessions.expired);\n return;\n }\n\n const result = await dependencies.switchSessionUseCase.execute({\n chatId: ctx.chat.id,\n sessionId: target.sessionId,\n });\n\n if (!result.found) {\n await ctx.editMessageText(copy.sessions.expired);\n return;\n }\n\n await ctx.editMessageText(presentSessionSwitchMessage(result.session, copy));\n return;\n }\n\n if (data.startsWith(SESSIONS_RENAME_PREFIX)) {\n const target = parseSessionActionTarget(data, SESSIONS_RENAME_PREFIX);\n\n if (!target) {\n await ctx.editMessageText(copy.sessions.renameExpired);\n return;\n }\n\n const view = await buildSessionActionView(\n ctx.chat.id,\n target.page,\n target.sessionId,\n dependencies,\n );\n\n if (!view.found) {\n await ctx.editMessageText(copy.sessions.renameExpired);\n return;\n }\n\n const menuMessageId = ctx.callbackQuery.message?.message_id;\n\n if (typeof menuMessageId !== \"number\" || !Number.isInteger(menuMessageId)) {\n await ctx.editMessageText(copy.sessions.renameExpired);\n return;\n }\n\n await dependencies.pendingActionRepo.set({\n chatId: ctx.chat.id,\n kind: \"session_rename\",\n sessionId: view.session.id,\n menuMessageId,\n returnPage: view.page,\n updatedAt: new Date().toISOString(),\n });\n\n await ctx.editMessageText(presentSessionRenamePromptMessage(view.session, copy));\n }\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to handle session callback\");\n await ctx.editMessageText(presentError(error, copy));\n }\n}\n\nexport async function handleLanguageCallback(ctx: CallbackContext, dependencies: BotDependencies) {\n const data = ctx.callbackQuery.data as LanguageCallbackData;\n\n if (!data.startsWith(\"language:\")) {\n return;\n }\n\n await ctx.answerCallbackQuery();\n\n if (!ctx.chat || !ctx.api) {\n return;\n }\n\n const currentCopy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n if (!data.startsWith(LANGUAGE_SELECT_PREFIX)) {\n await ctx.editMessageText(\n presentLanguageMessage(await getChatLanguage(dependencies.sessionRepo, ctx.chat.id), currentCopy),\n {\n reply_markup: buildLanguageKeyboard(\n await getChatLanguage(dependencies.sessionRepo, ctx.chat.id),\n currentCopy,\n ),\n },\n );\n return;\n }\n\n const selectedLanguage = data.slice(LANGUAGE_SELECT_PREFIX.length);\n const result = await presentLanguageSwitchForChat(ctx.chat.id, ctx.api, selectedLanguage, dependencies);\n\n await ctx.editMessageText(result.text, {\n reply_markup: result.keyboard,\n });\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to handle language callback\");\n await ctx.editMessageText(presentError(error, currentCopy));\n }\n}\n\nexport async function handlePermissionApprovalCallback(\n ctx: CallbackContext,\n dependencies: PermissionApprovalDependencies,\n) {\n const data = ctx.callbackQuery.data as PermissionApprovalCallbackData;\n\n if (!data.startsWith(PERMISSION_PREFIX)) {\n return;\n }\n\n await ctx.answerCallbackQuery();\n\n if (!ctx.chat) {\n return;\n }\n\n const parsed = parsePermissionApprovalCallbackData(data);\n\n if (!parsed) {\n return;\n }\n\n try {\n await dependencies.opencodeClient.replyToPermission(parsed.requestId, parsed.reply);\n\n const approvals = await dependencies.permissionApprovalRepo.listByRequestId(parsed.requestId);\n const approval = approvals.find((item) => item.chatId === ctx.chat?.id);\n\n if (approval) {\n await dependencies.permissionApprovalRepo.set({\n ...approval,\n status: parsed.reply,\n updatedAt: new Date().toISOString(),\n });\n }\n\n await ctx.editMessageText(buildPermissionApprovalResolvedMessage(parsed.requestId, parsed.reply));\n } catch (error) {\n dependencies.logger.error({ error, requestId: parsed.requestId }, \"failed to reply to permission request\");\n await ctx.editMessageText(\"Failed to reply to the permission request.\");\n }\n}\n\nexport function registerCallbackHandler(bot: Bot, dependencies: PermissionApprovalDependencies) {\n bot.callbackQuery(/^agents:/, async (ctx) => {\n await handleAgentsCallback(ctx, dependencies);\n });\n\n bot.callbackQuery(/^sessions:/, async (ctx) => {\n await handleSessionsCallback(ctx, dependencies);\n });\n\n bot.callbackQuery(/^model:/, async (ctx) => {\n await handleModelsCallback(ctx, dependencies);\n });\n\n bot.callbackQuery(/^language:/, async (ctx) => {\n await handleLanguageCallback(ctx, dependencies);\n });\n\n bot.callbackQuery(/^permission:/, async (ctx) => {\n await handlePermissionApprovalCallback(ctx, dependencies);\n });\n}\n\nfunction parseSessionActionTarget(data: string, prefix: string) {\n const suffix = data.slice(prefix.length);\n const separatorIndex = suffix.indexOf(\":\");\n\n if (separatorIndex === -1) {\n return null;\n }\n\n const page = Number(suffix.slice(0, separatorIndex));\n const sessionId = suffix.slice(separatorIndex + 1).trim();\n\n if (!Number.isInteger(page) || page < 0 || !sessionId) {\n return null;\n }\n\n return {\n page,\n sessionId,\n };\n}\n","import { getChatCopy } from \"../i18n.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport { buildTelegramPromptReply } from \"../../services/telegram/telegram-format.js\";\nimport type { BotDependencies } from \"../types.js\";\nimport type { PromptSessionFilePartInput } from \"../../services/opencode/opencode.client.js\";\n\ninterface SentMessage {\n message_id: number;\n}\n\nexport interface PromptExecutionContext {\n chat: { id: number };\n api: {\n deleteMessage(chatId: number, messageId: number): Promise<unknown>;\n };\n reply(text: string, other?: { parse_mode?: \"MarkdownV2\" }): Promise<SentMessage>;\n}\n\nexport interface PromptExecutionInput {\n files?: PromptSessionFilePartInput[];\n text?: string | null;\n}\n\nconst activePromptChats = new Set<number>();\n\nexport async function executePromptRequest(\n ctx: PromptExecutionContext,\n dependencies: BotDependencies,\n resolvePrompt: () => Promise<PromptExecutionInput>,\n) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n if (activePromptChats.has(ctx.chat.id)) {\n await ctx.reply(copy.status.alreadyProcessing);\n return;\n }\n\n let processingMessage: SentMessage | null = null;\n let sentTerminalReply = false;\n\n try {\n activePromptChats.add(ctx.chat.id);\n processingMessage = await ctx.reply(copy.status.processing);\n\n const promptInput = await resolvePrompt();\n const result = await dependencies.sendPromptUseCase.execute({\n chatId: ctx.chat.id,\n text: promptInput.text,\n files: promptInput.files,\n });\n const promptReply = normalizePromptReplyForDisplay(result.assistantReply, copy, dependencies);\n const telegramReply = buildTelegramPromptReply(promptReply, copy);\n\n try {\n await ctx.reply(telegramReply.preferred.text, telegramReply.preferred.options);\n } catch (replyError) {\n dependencies.logger.warn?.(\n { error: replyError },\n \"failed to send preferred telegram reply, falling back to plain text\",\n );\n await ctx.reply(telegramReply.fallback.text, telegramReply.fallback.options);\n }\n sentTerminalReply = true;\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to handle prompt request\");\n await ctx.reply(presentError(error, copy));\n sentTerminalReply = true;\n } finally {\n activePromptChats.delete(ctx.chat.id);\n if (processingMessage && sentTerminalReply) {\n try {\n await ctx.api.deleteMessage(ctx.chat.id, processingMessage.message_id);\n } catch (error) {\n dependencies.logger.warn?.({ error }, \"failed to delete processing message\");\n }\n }\n }\n}\n\nfunction normalizePromptReplyForDisplay(\n promptReply: Awaited<ReturnType<BotDependencies[\"sendPromptUseCase\"][\"execute\"]>>[\"assistantReply\"],\n copy: Awaited<ReturnType<typeof getChatCopy>>,\n dependencies: BotDependencies,\n) {\n if (!promptReply.assistantError) {\n return promptReply;\n }\n\n if (isRecoverableStructuredOutputError(promptReply)) {\n dependencies.logger.warn?.(\n {\n error: promptReply.assistantError,\n },\n \"structured output validation failed, falling back to assistant text reply\",\n );\n\n return {\n ...promptReply,\n assistantError: null,\n };\n }\n\n return {\n ...promptReply,\n bodyMd: null,\n fallbackText: presentError(promptReply.assistantError, copy),\n };\n}\n\nfunction isRecoverableStructuredOutputError(\n promptReply: Awaited<ReturnType<BotDependencies[\"sendPromptUseCase\"][\"execute\"]>>[\"assistantReply\"],\n): boolean {\n if (promptReply.assistantError?.name !== \"StructuredOutputError\") {\n return false;\n }\n\n if (promptReply.bodyMd?.trim()) {\n return true;\n }\n\n return promptReply.parts.some((part) =>\n part.type === \"text\" && typeof part.text === \"string\" && part.text.trim().length > 0\n );\n}\n","import type { Bot } from \"grammy\";\nimport type { BotDependencies } from \"../types.js\";\nimport { executePromptRequest, type PromptExecutionContext } from \"./prompt.handler.js\";\nimport { replyIfSessionRenamePending } from \"../session-rename.js\";\nimport { ImageMessageUnsupportedError } from \"../../use-cases/upload-file.usecase.js\";\n\nconst TELEGRAM_MAX_DOWNLOAD_BYTES = 20 * 1024 * 1024;\n\ninterface TelegramPhotoSize {\n file_id: string;\n file_size?: number;\n}\n\ninterface TelegramDocument {\n file_id: string;\n file_name?: string;\n file_size?: number;\n mime_type?: string;\n}\n\ninterface FileContext extends PromptExecutionContext {\n getFile(): Promise<{ file_path?: string }>;\n message: {\n caption?: string;\n document?: TelegramDocument;\n photo?: TelegramPhotoSize[];\n };\n}\n\nexport async function handleImageMessage(ctx: FileContext, dependencies: BotDependencies) {\n const image = resolveTelegramImage(ctx.message);\n\n if (!image) {\n return;\n }\n\n if (await replyIfSessionRenamePending(ctx, dependencies)) {\n return;\n }\n\n await executePromptRequest(ctx, dependencies, async () => {\n if (typeof image.fileSize === \"number\" && image.fileSize > TELEGRAM_MAX_DOWNLOAD_BYTES) {\n throw new ImageMessageUnsupportedError(\n `Image file size ${image.fileSize} exceeds the Telegram download limit of ${TELEGRAM_MAX_DOWNLOAD_BYTES} bytes.`,\n );\n }\n\n const file = await ctx.getFile();\n const filePath = file.file_path?.trim();\n\n if (!filePath) {\n throw new ImageMessageUnsupportedError(\"Telegram did not provide a downloadable image file path.\");\n }\n\n const promptFile = await dependencies.uploadFileUseCase.execute({\n expectedType: \"image\",\n filePath,\n filename: image.filename,\n mimeType: image.mimeType,\n });\n\n return {\n files: [promptFile],\n text: ctx.message.caption?.trim() || null,\n };\n });\n}\n\nexport function registerFileHandler(bot: Bot, dependencies: BotDependencies) {\n bot.on(\"message:photo\", async (ctx) => {\n await handleImageMessage(ctx, dependencies);\n });\n\n bot.on(\"message:document\", async (ctx) => {\n await handleImageMessage(ctx, dependencies);\n });\n}\n\nfunction resolveTelegramImage(message: FileContext[\"message\"]) {\n const document = message.document;\n\n if (document && isImageDocument(document)) {\n return {\n fileId: document.file_id,\n fileSize: document.file_size,\n filename: document.file_name ?? null,\n mimeType: document.mime_type ?? null,\n };\n }\n\n const photo = pickLargestPhoto(message.photo);\n\n if (!photo) {\n return null;\n }\n\n return {\n fileId: photo.file_id,\n fileSize: photo.file_size,\n filename: null,\n mimeType: \"image/jpeg\",\n };\n}\n\nfunction isImageDocument(document: TelegramDocument): boolean {\n const mimeType = document.mime_type?.trim().toLowerCase();\n const filename = document.file_name?.trim().toLowerCase() ?? \"\";\n\n return !!mimeType?.startsWith(\"image/\")\n || /\\.(png|jpe?g|gif|webp|bmp|svg)$/i.test(filename);\n}\n\nfunction pickLargestPhoto(photos: TelegramPhotoSize[] | undefined): TelegramPhotoSize | null {\n if (!photos || photos.length === 0) {\n return null;\n }\n\n return photos.reduce<TelegramPhotoSize>((largest, current) => {\n const currentSize = current.file_size ?? 0;\n const largestSize = largest.file_size ?? 0;\n\n return currentSize >= largestSize ? current : largest;\n }, photos[0]);\n}\n","import type { Bot } from \"grammy\";\nimport type { BotDependencies } from \"../types.js\";\nimport { executePromptRequest, type PromptExecutionContext } from \"./prompt.handler.js\";\nimport { handlePendingSessionRenameText } from \"../session-rename.js\";\n\ninterface MessageContext extends PromptExecutionContext {\n api: PromptExecutionContext[\"api\"] & {\n editMessageText(\n chatId: number,\n messageId: number,\n text: string,\n other?: { reply_markup?: unknown },\n ): Promise<unknown>;\n };\n message: { text?: string };\n}\n\nexport async function handleTextMessage(ctx: MessageContext, dependencies: BotDependencies) {\n const text = ctx.message.text?.trim();\n\n if (await handlePendingSessionRenameText(ctx, dependencies)) return;\n if (!text) return;\n if (text.startsWith(\"/\")) return;\n await executePromptRequest(ctx, dependencies, async () => ({ text }));\n}\n\nexport function registerMessageHandler(bot: Bot, dependencies: BotDependencies) {\n bot.on(\"message:text\", async (ctx) => {\n await handleTextMessage(ctx, dependencies);\n });\n}\n","import type { Bot } from \"grammy\";\nimport { getChatCopy } from \"../i18n.js\";\nimport type { BotDependencies } from \"../types.js\";\nimport { replyIfSessionRenamePending } from \"../session-rename.js\";\n\ninterface VoiceContext {\n chat: { id: number };\n message: { voice?: unknown };\n reply(text: string): Promise<unknown>;\n}\n\nexport async function handleVoiceMessage(ctx: VoiceContext, dependencies: BotDependencies) {\n if (!ctx.message.voice) {\n return;\n }\n\n if (await replyIfSessionRenamePending(ctx, dependencies)) {\n return;\n }\n\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n await ctx.reply(copy.errors.voiceUnsupported);\n}\n\nexport function registerVoiceHandler(bot: Bot, dependencies: BotDependencies) {\n bot.on(\"message:voice\", async (ctx) => {\n await handleVoiceMessage(ctx, dependencies);\n });\n}\n","import type { MiddlewareFn } from \"grammy\";\n\nexport function createAuthMiddleware(allowedChatIds: readonly number[]): MiddlewareFn {\n return async (ctx, next) => {\n if (allowedChatIds.length === 0) {\n return next();\n }\n\n const chatId = ctx.chat?.id;\n\n if (!chatId || !allowedChatIds.includes(chatId)) {\n await ctx.reply(\"Unauthorized chat.\");\n return;\n }\n\n return next();\n };\n}\n","import type { MiddlewareFn } from \"grammy\";\nimport type { AppLogger } from \"../../infra/logger/index.js\";\nimport { createRedactedPreview } from \"../../infra/utils/redact.js\";\n\ninterface LoggingContextLike {\n chat?: { id?: number };\n from?: { id?: number };\n msg?: { text?: string } | null;\n update: { update_id: number };\n}\n\nexport function buildIncomingUpdateLogFields(ctx: LoggingContextLike) {\n const messageText = ctx.msg && \"text\" in ctx.msg\n ? ctx.msg.text\n : undefined;\n\n return {\n updateId: ctx.update.update_id,\n chatId: ctx.chat?.id,\n fromId: ctx.from?.id,\n hasText: typeof messageText === \"string\" && messageText.length > 0,\n textLength: typeof messageText === \"string\" ? messageText.length : 0,\n textPreview: typeof messageText === \"string\" && messageText.length > 0\n ? createRedactedPreview(messageText)\n : undefined,\n };\n}\n\nexport function createLoggingMiddleware(logger: Pick<AppLogger, \"info\">): MiddlewareFn {\n return async (ctx, next) => {\n logger.info(\n buildIncomingUpdateLogFields(ctx),\n \"incoming update\",\n );\n\n return next();\n };\n}\n","import type { Bot } from \"grammy\";\nimport type { PermissionApprovalRepository } from \"../domain/permission/approval.repository.js\";\nimport type { OpenCodeClientLike } from \"../services/opencode/opencode.client.js\";\nimport { registerAgentsCommand } from \"./commands/agents.js\";\nimport { registerCancelCommand } from \"./commands/cancel.js\";\nimport { registerLanguageCommand } from \"./commands/language.js\";\nimport { registerModelsCommand } from \"./commands/models.js\";\nimport { registerNewCommand } from \"./commands/new.js\";\nimport { registerStatusCommand } from \"./commands/status.js\";\nimport type { BotDependencies } from \"./types.js\";\nimport { registerSessionsCommand } from \"./commands/sessions.js\";\nimport { registerStartCommand } from \"./commands/start.js\";\nimport { registerCallbackHandler } from \"./handlers/callback.handler.js\";\nimport { registerFileHandler } from \"./handlers/file.handler.js\";\nimport { registerMessageHandler } from \"./handlers/message.handler.js\";\nimport { registerVoiceHandler } from \"./handlers/voice.handler.js\";\nimport { createAuthMiddleware } from \"./middlewares/auth.js\";\nimport { createLoggingMiddleware } from \"./middlewares/logging.js\";\n\nexport interface RegisterBotOptions {\n telegramAllowedChatIds: number[];\n}\n\nexport function registerBot(\n bot: Bot,\n container: BotDependencies & {\n opencodeClient: OpenCodeClientLike;\n permissionApprovalRepo: PermissionApprovalRepository;\n },\n options: RegisterBotOptions,\n) {\n bot.use(createLoggingMiddleware(container.logger));\n bot.use(createAuthMiddleware(options.telegramAllowedChatIds));\n\n registerStartCommand(bot, container);\n registerStatusCommand(bot, container);\n registerNewCommand(bot, container);\n registerAgentsCommand(bot, container);\n registerSessionsCommand(bot, container);\n registerCancelCommand(bot, container);\n registerModelsCommand(bot, container);\n registerLanguageCommand(bot, container);\n registerCallbackHandler(bot, container);\n registerFileHandler(bot, container);\n registerMessageHandler(bot, container);\n registerVoiceHandler(bot, container);\n}\n","import { run } from \"@grammyjs/runner\";\nimport { Bot } from \"grammy\";\nimport { syncTelegramCommands } from \"../bot/commands/sync-commands.js\";\nimport { registerBot } from \"../bot/index.js\";\nimport type { AppConfig } from \"./config.js\";\nimport type { AppContainer } from \"./container.js\";\n\nexport interface StartTelegramBotRuntimeInput {\n config: AppConfig;\n container: AppContainer;\n syncCommands?: boolean;\n}\n\nexport interface TelegramBotRuntime {\n bot: Bot;\n container: AppContainer;\n stop(): void;\n dispose(): Promise<void>;\n}\n\nexport async function startTelegramBotRuntime(\n input: StartTelegramBotRuntimeInput,\n): Promise<TelegramBotRuntime> {\n const bot = new Bot(input.config.telegramBotToken, {\n client: {\n apiRoot: input.config.telegramApiRoot,\n },\n });\n\n registerBot(bot, input.container, {\n telegramAllowedChatIds: input.config.telegramAllowedChatIds,\n });\n\n bot.catch((error) => {\n input.container.logger.error(\n {\n error: error.error,\n update: error.ctx.update,\n },\n \"telegram bot update failed\",\n );\n });\n\n input.container.logger.info(\"bot starting...\");\n\n if (input.syncCommands ?? true) {\n await syncTelegramCommands(bot, input.container.logger);\n }\n\n const runner = run(bot);\n let stopped = false;\n let disposed = false;\n const stop = () => {\n if (stopped) {\n return;\n }\n\n stopped = true;\n runner.stop();\n };\n const dispose = async () => {\n if (disposed) {\n return;\n }\n\n disposed = true;\n stop();\n await input.container.dispose();\n };\n\n return {\n bot,\n container: input.container,\n stop,\n dispose,\n };\n}\n","import type { Hooks, Plugin, PluginInput } from \"@opencode-ai/plugin\";\nimport { bootstrapPluginApp } from \"./app/bootstrap.js\";\nimport { handleTelegramBotPluginEvent } from \"./app/plugin-events.js\";\nimport {\n preparePluginConfiguration,\n type PreparedPluginConfiguration,\n} from \"./app/plugin-config.js\";\nimport {\n startTelegramBotRuntime,\n type StartTelegramBotRuntimeInput,\n type TelegramBotRuntime,\n} from \"./app/runtime.js\";\nimport type { PluginConfigSource } from \"./app/config.js\";\nimport type { OpenCodeSdkClientLike } from \"./services/opencode/opencode.client.js\";\n\nexport interface OpenCodePluginContextLike {\n client: OpenCodeSdkClientLike;\n directory?: string;\n worktree?: string;\n}\n\nexport interface EnsureTelegramBotPluginRuntimeOptions {\n context: OpenCodePluginContextLike;\n config?: PluginConfigSource;\n bootstrapApp?: typeof bootstrapPluginApp;\n prepareConfiguration?: (input: {\n cwd: string;\n config?: PluginConfigSource;\n }) => Promise<PreparedPluginConfiguration>;\n startRuntime?: (input: StartTelegramBotRuntimeInput) => Promise<TelegramBotRuntime>;\n}\n\ninterface TelegramBotPluginRuntimeState {\n cwd: string;\n runtime: TelegramBotRuntime | null;\n runtimePromise: Promise<TelegramBotRuntime>;\n}\n\nlet runtimeState: TelegramBotPluginRuntimeState | null = null;\n\nexport async function ensureTelegramBotPluginRuntime(\n options: EnsureTelegramBotPluginRuntimeOptions,\n): Promise<TelegramBotRuntime> {\n const cwd = resolvePluginRuntimeCwd(options.context);\n\n if (runtimeState && runtimeState.cwd !== cwd) {\n const activeState = runtimeState;\n runtimeState = null;\n await disposeTelegramBotPluginRuntimeState(activeState);\n }\n\n if (!runtimeState) {\n const runtimePromise = startPluginRuntime(options, cwd)\n .then((runtime) => {\n if (runtimeState?.runtimePromise === runtimePromise) {\n runtimeState.runtime = runtime;\n }\n\n return runtime;\n })\n .catch((error) => {\n if (runtimeState?.runtimePromise === runtimePromise) {\n runtimeState = null;\n }\n\n throw error;\n });\n\n runtimeState = {\n cwd,\n runtime: null,\n runtimePromise,\n };\n }\n\n return runtimeState.runtimePromise;\n}\n\nexport const TelegramBotPlugin: Plugin = async (context) => {\n const runtime = await ensureTelegramBotPluginRuntime({\n context: context as unknown as OpenCodePluginContextLike,\n });\n\n return createHooks(runtime);\n};\n\nexport async function resetTelegramBotPluginRuntimeForTests() {\n if (!runtimeState) {\n return;\n }\n\n const activeState = runtimeState;\n runtimeState = null;\n await disposeTelegramBotPluginRuntimeState(activeState);\n}\n\nexport default TelegramBotPlugin;\n\nasync function startPluginRuntime(\n options: EnsureTelegramBotPluginRuntimeOptions,\n cwd: string,\n): Promise<TelegramBotRuntime> {\n const bootstrapApp = options.bootstrapApp ?? bootstrapPluginApp;\n const prepareConfiguration = options.prepareConfiguration ?? preparePluginConfiguration;\n const startRuntime = options.startRuntime ?? startTelegramBotRuntime;\n const preparedConfiguration = await prepareConfiguration({\n cwd,\n config: options.config,\n });\n const { config, container } = bootstrapApp(\n options.context.client,\n preparedConfiguration.config,\n { cwd: preparedConfiguration.cwd },\n );\n\n try {\n if (preparedConfiguration.ignoredProjectConfigFilePath) {\n container.logger.warn(\n {\n cwd: preparedConfiguration.cwd,\n ignoredProjectConfigFilePath: preparedConfiguration.ignoredProjectConfigFilePath,\n globalConfigFilePath: preparedConfiguration.globalConfigFilePath,\n },\n \"legacy worktree plugin config is ignored; migrate settings to the global opencode-tbot config\",\n );\n }\n\n const runtime = await startRuntime({ config, container });\n\n container.logger.info(\n {\n cwd: preparedConfiguration.cwd,\n globalConfigFilePath: preparedConfiguration.globalConfigFilePath,\n ignoredProjectConfigFilePath: preparedConfiguration.ignoredProjectConfigFilePath,\n configFilePath: preparedConfiguration.configFilePath,\n mode: \"plugin\",\n },\n \"telegram bot plugin runtime started\",\n );\n\n return runtime;\n } catch (error) {\n await container.dispose();\n throw error;\n }\n}\n\nfunction resolvePluginRuntimeCwd(context: OpenCodePluginContextLike): string {\n return context.worktree ?? context.directory ?? process.cwd();\n}\n\nasync function disposeTelegramBotPluginRuntimeState(\n state: TelegramBotPluginRuntimeState,\n): Promise<void> {\n const runtime = state.runtime ?? await state.runtimePromise.catch(() => null);\n\n await runtime?.dispose();\n}\n\nfunction createHooks(runtime: TelegramBotRuntime): Hooks {\n return {\n async event({ event }) {\n await handleTelegramBotPluginEvent(runtime, event as never);\n },\n async \"permission.ask\"(input, output) {\n const bindings = await runtime.container.sessionRepo.listBySessionId(input.sessionID);\n const foregroundChatIds = runtime.container.foregroundSessionTracker.listChatIds(input.sessionID);\n\n if (bindings.length > 0 || foregroundChatIds.length > 0) {\n output.status = \"ask\";\n }\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../src/infra/utils/redact.ts","../src/infra/logger/index.ts","../src/repositories/pending-action.repo.ts","../src/repositories/permission-approval.repo.ts","../src/repositories/session.repo.ts","../src/services/opencode/opencode.client.ts","../src/services/storage/bot-state.ts","../src/services/storage/json-state-store.ts","../src/services/telegram/telegram.client.ts","../src/services/session-activity/foreground-session-tracker.ts","../src/use-cases/abort-prompt.usecase.ts","../src/use-cases/session-creation.ts","../src/use-cases/create-session.usecase.ts","../src/use-cases/get-health.usecase.ts","../src/use-cases/get-path.usecase.ts","../src/use-cases/get-status.usecase.ts","../src/use-cases/agent-selection.ts","../src/use-cases/binding-update.ts","../src/use-cases/list-agents.usecase.ts","../src/use-cases/list-lsp.usecase.ts","../src/use-cases/list-mcp.usecase.ts","../src/use-cases/list-models.usecase.ts","../src/use-cases/list-sessions.usecase.ts","../src/use-cases/rename-session.usecase.ts","../src/use-cases/send-prompt.usecase.ts","../src/use-cases/switch-agent.usecase.ts","../src/use-cases/switch-model.usecase.ts","../src/use-cases/switch-session.usecase.ts","../src/use-cases/upload-file.usecase.ts","../src/app/container.ts","../src/app/bootstrap.ts","../src/services/permission/telegram-approval.ts","../src/app/plugin-events.ts","../src/bot/copy.ts","../src/bot/commands/command-list.ts","../src/bot/commands/sync-commands.ts","../src/bot/i18n.ts","../src/bot/presenters/keyboard.presenter.ts","../src/bot/presenters/error.presenter.ts","../src/bot/presenters/message.presenter.ts","../src/bot/commands/agents.ts","../src/bot/sessions-menu.ts","../src/bot/session-rename.ts","../src/bot/commands/cancel.ts","../src/bot/commands/language.ts","../src/bot/commands/models.ts","../src/bot/commands/new.ts","../src/services/telegram/telegram-format.ts","../src/bot/commands/status.ts","../src/bot/commands/sessions.ts","../src/bot/presenters/static.presenter.ts","../src/bot/commands/start.ts","../src/bot/handlers/callback.handler.ts","../src/bot/handlers/prompt.handler.ts","../src/bot/handlers/file.handler.ts","../src/bot/handlers/message.handler.ts","../src/bot/handlers/voice.handler.ts","../src/bot/middlewares/auth.ts","../src/bot/middlewares/logging.ts","../src/bot/index.ts","../src/app/runtime.ts","../src/plugin.ts"],"sourcesContent":["const REDACTED = \"[REDACTED]\";\nconst DEFAULT_PREVIEW_LENGTH = 160;\nconst TELEGRAM_TOKEN_PATTERN = /\\b\\d{6,}:[A-Za-z0-9_-]{20,}\\b/g;\nconst BEARER_TOKEN_PATTERN = /\\bBearer\\s+[A-Za-z0-9._~+/-]+=*\\b/gi;\nconst NAMED_SECRET_PATTERN = /\\b(api[_\\s-]?key|token|secret|password)\\b(\\s*[:=]\\s*)([^\\s,;]+)/gi;\nconst API_KEY_LIKE_PATTERN = /\\b(?:sk|pk)_[A-Za-z0-9_-]{10,}\\b/g;\n\nexport function redactSensitiveText(input: string): string {\n return input\n .replace(BEARER_TOKEN_PATTERN, `Bearer ${REDACTED}`)\n .replace(TELEGRAM_TOKEN_PATTERN, REDACTED)\n .replace(NAMED_SECRET_PATTERN, (_, name: string, separator: string) => `${name}${separator}${REDACTED}`)\n .replace(API_KEY_LIKE_PATTERN, REDACTED);\n}\n\nexport function createRedactedPreview(input: string, maxLength = DEFAULT_PREVIEW_LENGTH): string {\n const redacted = redactSensitiveText(input).replace(/\\s+/g, \" \").trim();\n\n if (redacted.length <= maxLength) {\n return redacted;\n }\n\n return `${redacted.slice(0, Math.max(0, maxLength - 3))}...`;\n}\n","import { redactSensitiveText } from \"../utils/redact.js\";\n\nexport type AppLogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport interface AppLogger {\n debug(input?: unknown, message?: string): void;\n info(input?: unknown, message?: string): void;\n warn(input?: unknown, message?: string): void;\n error(input?: unknown, message?: string): void;\n flush(): Promise<void>;\n}\n\ninterface OpenCodeAppLogClientLike {\n app: {\n log(input: {\n service: string;\n level: AppLogLevel;\n message: string;\n extra?: Record<string, unknown>;\n }): Promise<unknown>;\n };\n}\n\ninterface CreateOpenCodeAppLoggerOptions {\n level?: string | null;\n service?: string;\n}\n\nconst DEFAULT_SERVICE_NAME = \"opencode-tbot\";\nconst LEVEL_PRIORITY: Record<AppLogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\nexport function createOpenCodeAppLogger(\n client: OpenCodeAppLogClientLike,\n options: CreateOpenCodeAppLoggerOptions = {},\n): AppLogger {\n const service = normalizeServiceName(options.service);\n const minimumLevel = normalizeLogLevel(options.level);\n let queue = Promise.resolve();\n\n const enqueue = (level: AppLogLevel, input?: unknown, message?: string) => {\n if (LEVEL_PRIORITY[level] < LEVEL_PRIORITY[minimumLevel]) {\n return;\n }\n\n const { extra, text } = normalizeLogArguments(input, message);\n const payload = {\n service,\n level,\n message: text,\n ...(extra ? { extra } : {}),\n };\n\n queue = queue\n .catch(() => undefined)\n .then(async () => {\n try {\n await client.app.log(payload);\n } catch {\n // Swallow logging failures to avoid breaking the plugin runtime.\n }\n });\n };\n\n return {\n debug(input?: unknown, message?: string) {\n enqueue(\"debug\", input, message);\n },\n info(input?: unknown, message?: string) {\n enqueue(\"info\", input, message);\n },\n warn(input?: unknown, message?: string) {\n enqueue(\"warn\", input, message);\n },\n error(input?: unknown, message?: string) {\n enqueue(\"error\", input, message);\n },\n async flush() {\n await queue.catch(() => undefined);\n },\n };\n}\n\nfunction normalizeServiceName(value: string | undefined): string {\n const normalized = value?.trim();\n\n return normalized && normalized.length > 0 ? normalized : DEFAULT_SERVICE_NAME;\n}\n\nfunction normalizeLogLevel(value: string | null | undefined): AppLogLevel {\n switch (value?.trim().toLowerCase()) {\n case \"debug\":\n return \"debug\";\n case \"warn\":\n return \"warn\";\n case \"error\":\n return \"error\";\n default:\n return \"info\";\n }\n}\n\nfunction normalizeLogArguments(input?: unknown, message?: string): {\n extra?: Record<string, unknown>;\n text: string;\n} {\n if (typeof input === \"string\") {\n return {\n text: input,\n };\n }\n\n if (message && message.trim().length > 0) {\n const extra = serializeExtra(input);\n\n return {\n ...(extra ? { extra } : {}),\n text: message.trim(),\n };\n }\n\n if (input instanceof Error) {\n return {\n extra: serializeExtra(input) ?? undefined,\n text: input.message.trim() || input.name,\n };\n }\n\n const extra = serializeExtra(input);\n\n return {\n ...(extra ? { extra } : {}),\n text: \"log\",\n };\n}\n\nfunction serializeExtra(input: unknown): Record<string, unknown> | null {\n if (input === null || input === undefined) {\n return null;\n }\n\n if (input instanceof Error) {\n return {\n error: serializeError(input),\n };\n }\n\n if (Array.isArray(input)) {\n return {\n items: input.map((item) => sanitizeValue(item)),\n };\n }\n\n if (typeof input !== \"object\") {\n return {\n value: sanitizeValue(input),\n };\n }\n\n return sanitizeRecord(input as Record<string, unknown>);\n}\n\nfunction sanitizeRecord(record: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(record).map(([key, value]) => [\n key,\n isSensitiveKey(key)\n ? redactSensitiveFieldValue(value)\n : sanitizeValue(value),\n ]),\n );\n}\n\nfunction sanitizeValue(value: unknown): unknown {\n if (value instanceof Error) {\n return serializeError(value);\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => sanitizeValue(item));\n }\n\n if (typeof value === \"string\") {\n return redactSensitiveText(value);\n }\n\n if (!value || typeof value !== \"object\") {\n return value;\n }\n\n return sanitizeRecord(value as Record<string, unknown>);\n}\n\nfunction serializeError(error: Error): Record<string, unknown> {\n return {\n name: error.name,\n message: redactSensitiveText(error.message),\n ...(error.stack ? { stack: redactSensitiveText(error.stack) } : {}),\n ...(\"data\" in error && error.data && typeof error.data === \"object\"\n ? { data: sanitizeValue(error.data) }\n : {}),\n };\n}\n\nfunction isSensitiveKey(key: string): boolean {\n return /token|secret|api[-_]?key|authorization|password/iu.test(key);\n}\n\nfunction redactSensitiveFieldValue(value: unknown): unknown {\n if (typeof value === \"string\" && value.trim().length > 0) {\n return \"[REDACTED]\";\n }\n\n if (Array.isArray(value)) {\n return value.map(() => \"[REDACTED]\");\n }\n\n if (value && typeof value === \"object\") {\n return \"[REDACTED]\";\n }\n\n return value;\n}\n","import type { PendingChatAction } from \"../domain/pending-action/pending-action.entity.js\";\nimport type { PendingActionRepository } from \"../domain/pending-action/pending-action.repository.js\";\nimport type { OpencodeTbotState } from \"../services/storage/bot-state.js\";\nimport type { JsonStateStore } from \"../services/storage/json-state-store.js\";\n\nexport class InMemoryPendingActionRepository implements PendingActionRepository {\n private readonly actionsByChatId = new Map<number, PendingChatAction>();\n\n async getByChatId(chatId: number): Promise<PendingChatAction | null> {\n return this.actionsByChatId.get(chatId) ?? null;\n }\n\n async set(action: PendingChatAction): Promise<void> {\n this.actionsByChatId.set(action.chatId, action);\n }\n\n async clear(chatId: number): Promise<void> {\n this.actionsByChatId.delete(chatId);\n }\n}\n\nexport class FilePendingActionRepository implements PendingActionRepository {\n constructor(private readonly store: JsonStateStore<OpencodeTbotState>) { }\n\n async getByChatId(chatId: number): Promise<PendingChatAction | null> {\n const state = await this.store.read();\n\n return state.pendingActions[String(chatId)] ?? null;\n }\n\n async set(action: PendingChatAction): Promise<void> {\n await this.store.update((state) => {\n state.pendingActions[String(action.chatId)] = action;\n });\n }\n\n async clear(chatId: number): Promise<void> {\n await this.store.update((state) => {\n delete state.pendingActions[String(chatId)];\n });\n }\n}\n","import type { PendingPermissionApproval } from \"../domain/permission/approval.entity.js\";\nimport type { PermissionApprovalRepository } from \"../domain/permission/approval.repository.js\";\nimport type { OpencodeTbotState } from \"../services/storage/bot-state.js\";\nimport type { JsonStateStore } from \"../services/storage/json-state-store.js\";\n\nfunction buildApprovalKey(requestId: string, chatId: number): string {\n return `${requestId}:${chatId}`;\n}\n\nexport class InMemoryPermissionApprovalRepository implements PermissionApprovalRepository {\n private readonly approvalsByKey = new Map<string, PendingPermissionApproval>();\n\n async listByRequestId(requestId: string): Promise<PendingPermissionApproval[]> {\n return [...this.approvalsByKey.values()].filter((approval) => approval.requestId === requestId);\n }\n\n async set(approval: PendingPermissionApproval): Promise<void> {\n this.approvalsByKey.set(buildApprovalKey(approval.requestId, approval.chatId), approval);\n }\n}\n\nexport class FilePermissionApprovalRepository implements PermissionApprovalRepository {\n constructor(private readonly store: JsonStateStore<OpencodeTbotState>) { }\n\n async listByRequestId(requestId: string): Promise<PendingPermissionApproval[]> {\n const state = await this.store.read();\n\n return Object.values(state.pendingPermissions).filter((approval) => approval.requestId === requestId);\n }\n\n async set(approval: PendingPermissionApproval): Promise<void> {\n await this.store.update((state) => {\n state.pendingPermissions[buildApprovalKey(approval.requestId, approval.chatId)] = approval;\n });\n }\n}\n","import type { ChatSessionBinding } from \"../domain/session/session.entity.js\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { OpencodeTbotState } from \"../services/storage/bot-state.js\";\nimport type { JsonStateStore } from \"../services/storage/json-state-store.js\";\n\nexport class InMemorySessionRepository implements SessionRepository {\n private readonly sessionsByChatId = new Map<number, ChatSessionBinding>();\n\n async getByChatId(chatId: number): Promise<ChatSessionBinding | null> {\n return this.sessionsByChatId.get(chatId) ?? null;\n }\n\n async listBySessionId(sessionId: string): Promise<ChatSessionBinding[]> {\n return [...this.sessionsByChatId.values()].filter((binding) => binding.sessionId === sessionId);\n }\n\n async setCurrent(binding: ChatSessionBinding): Promise<void> {\n this.sessionsByChatId.set(binding.chatId, binding);\n }\n\n async touch(chatId: number): Promise<void> {\n const binding = this.sessionsByChatId.get(chatId);\n\n if (!binding) {\n return;\n }\n\n this.sessionsByChatId.set(chatId, {\n ...binding,\n updatedAt: new Date().toISOString(),\n });\n }\n}\n\nexport class FileSessionRepository implements SessionRepository {\n constructor(private readonly store: JsonStateStore<OpencodeTbotState>) { }\n\n async getByChatId(chatId: number): Promise<ChatSessionBinding | null> {\n const state = await this.store.read();\n\n return state.sessions[String(chatId)] ?? null;\n }\n\n async listBySessionId(sessionId: string): Promise<ChatSessionBinding[]> {\n const state = await this.store.read();\n\n return Object.values(state.sessions).filter((binding) => binding.sessionId === sessionId);\n }\n\n async setCurrent(binding: ChatSessionBinding): Promise<void> {\n await this.store.update((state) => {\n state.sessions[String(binding.chatId)] = binding;\n });\n }\n\n async touch(chatId: number): Promise<void> {\n await this.store.update((state) => {\n const current = state.sessions[String(chatId)];\n\n if (!current) {\n return;\n }\n\n state.sessions[String(chatId)] = {\n ...current,\n updatedAt: new Date().toISOString(),\n };\n });\n }\n}\n","import {\n type Agent,\n createOpencodeClient,\n type Config,\n type LspStatus,\n type McpStatus,\n type Model,\n type OpencodeClient,\n type Part,\n type PermissionRequest,\n type Path,\n type Provider,\n type Project,\n type Session,\n type SessionStatus,\n} from \"@opencode-ai/sdk/v2/client\";\nimport { z } from \"zod\";\n\nexport interface OpenCodeClientOptions {\n baseUrl: string;\n apiKey?: string;\n}\n\nexport interface OpenCodePromptTimeoutPolicy {\n pollRequestTimeoutMs: number;\n recoveryInactivityTimeoutMs: number;\n waitTimeoutMs: number;\n}\n\nexport function buildOpenCodeSdkConfig(options: OpenCodeClientOptions) {\n const apiKey = options.apiKey?.trim();\n\n return {\n baseUrl: options.baseUrl,\n ...(apiKey ? { auth: apiKey } : {}),\n };\n}\n\nexport interface PromptSessionInput {\n sessionId: string;\n prompt?: string | null;\n agent?: string | null;\n structured?: boolean;\n model?: {\n providerID: string;\n modelID: string;\n };\n variant?: string | null;\n files?: PromptSessionFilePartInput[];\n}\n\nexport interface PromptSessionFilePartInput {\n filename?: string;\n mime: string;\n url: string;\n}\n\nexport interface PromptSessionTokenMetrics {\n total: number | null;\n input: number | null;\n output: number | null;\n reasoning: number | null;\n cacheRead: number | null;\n cacheWrite: number | null;\n}\n\nexport interface PromptSessionMetrics {\n durationMs: number | null;\n tokens: PromptSessionTokenMetrics;\n}\n\nexport interface PromptSessionResult {\n assistantError: OpenCodeAssistantError | null;\n bodyMd: string | null;\n fallbackText: string;\n info: OpenCodeAssistantMessage | null;\n metrics: PromptSessionMetrics;\n parts: Part[];\n structured: unknown;\n}\n\nexport type OpenCodeProject = Project;\nexport type OpenCodeSession = Session;\nexport type OpenCodeModel = Model;\nexport type OpenCodeProvider = Provider;\nexport type OpenCodeConfig = Config;\nexport type OpenCodeAgent = Agent;\nexport type OpenCodeLspStatus = LspStatus;\nexport type OpenCodePath = Path;\nexport type OpenCodeMcpStatus = McpStatus;\nexport type OpenCodeSessionStatus = SessionStatus;\nexport type OpenCodePermissionRequest = PermissionRequest;\nexport interface OpenCodeHealth {\n healthy: boolean;\n version: string;\n}\n\nexport interface OpenCodeConfiguredModel {\n id: string;\n providerID: string;\n providerName: string;\n name: string;\n qualifiedId: string;\n reasoning: boolean;\n variants: Record<string, Record<string, unknown>>;\n}\n\ntype RawSdkRequestOptions = {\n body?: unknown;\n path?: Record<string, unknown>;\n query?: Record<string, unknown>;\n responseStyle?: \"data\" | \"fields\";\n signal?: AbortSignal;\n throwOnError?: boolean;\n url: string;\n};\n\ntype RawSdkHttpClient = {\n delete?(options: RawSdkRequestOptions): Promise<unknown>;\n get?(options: RawSdkRequestOptions): Promise<unknown>;\n patch?(options: RawSdkRequestOptions): Promise<unknown>;\n post?(options: RawSdkRequestOptions): Promise<unknown>;\n};\n\nexport type OpenCodeSdkClientLike = Pick<OpencodeClient, \"app\" | \"config\" | \"global\" | \"lsp\" | \"mcp\" | \"path\" | \"permission\" | \"project\" | \"session\"> & {\n _client?: RawSdkHttpClient;\n};\ntype MaybeWrappedResponse<T> = T | { data: T };\ntype OpenCodeProviderCatalog = {\n providers: OpenCodeProvider[];\n default: Record<string, string>;\n};\ntype OpenCodeProviderAvailability = Map<string, Set<string> | null>;\ntype OpenCodeProviderConfig = NonNullable<OpenCodeConfig[\"provider\"]>[string];\ntype OpenCodeModelConfig = NonNullable<OpenCodeProviderConfig[\"models\"]>[string];\ntype OpenCodePromptResponse = {\n info: unknown;\n parts?: Part[] | null;\n};\ntype SdkFlavor = \"legacy\" | \"v2\";\n\nexport type OpenCodePromptTimeoutStage =\n | \"capture-known-messages\"\n | \"send-prompt\"\n | \"poll-message\"\n | \"poll-messages\"\n | \"poll-status\"\n | \"final-scan\";\n\nexport interface OpenCodePromptTimeoutErrorData {\n elapsedMs: number;\n message?: string;\n messageId?: string;\n sessionId: string;\n stage: OpenCodePromptTimeoutStage;\n timeoutMs: number;\n}\n\nexport class OpenCodePromptTimeoutError extends Error {\n readonly data: OpenCodePromptTimeoutErrorData;\n\n constructor(data: OpenCodePromptTimeoutErrorData) {\n super(data.message ?? \"OpenCode prompt timed out.\");\n this.name = \"OpenCodePromptTimeoutError\";\n this.data = data;\n }\n}\n\nexport interface OpenCodeAssistantError {\n data?: Record<string, unknown>;\n name: string;\n [key: string]: unknown;\n}\n\nexport interface OpenCodeAssistantMessage {\n agent?: string;\n cost?: number;\n error?: OpenCodeAssistantError;\n finish?: string;\n id?: string;\n mode?: string;\n modelID?: string;\n parentID?: string;\n path?: {\n cwd?: string;\n root?: string;\n };\n providerID?: string;\n role?: \"assistant\";\n sessionID?: string;\n structured?: unknown;\n summary?: boolean;\n time?: {\n completed?: number;\n created?: number;\n };\n tokens?: {\n cache?: {\n read?: number;\n write?: number;\n };\n input?: number;\n output?: number;\n reasoning?: number;\n total?: number;\n };\n variant?: string;\n}\n\nconst EMPTY_RESPONSE_TEXT = \"OpenCode returned empty response.\";\nconst PROMPT_MESSAGE_POLL_INITIAL_DELAYS_MS = [0, 100, 250, 500, 1000];\nconst PROMPT_MESSAGE_POLL_INTERVAL_MS = 2000;\nconst PROMPT_MESSAGE_POLL_LIMIT = 20;\nconst PROMPT_LOG_SERVICE = \"opencode-tbot\";\nconst DEFAULT_OPENCODE_PROMPT_TIMEOUT_POLICY: OpenCodePromptTimeoutPolicy = {\n pollRequestTimeoutMs: 15_000,\n recoveryInactivityTimeoutMs: 120_000,\n waitTimeoutMs: 1_800_000,\n};\nconst STRUCTURED_REPLY_SCHEMA = {\n type: \"json_schema\" as const,\n retryCount: 2,\n schema: {\n type: \"object\",\n additionalProperties: false,\n required: [\"body_md\"],\n properties: {\n body_md: {\n type: \"string\",\n description: \"Markdown body only. Do not include duration, token usage, or any footer. Do not wrap the whole answer in ```md or ```markdown fences. Use Markdown formatting directly unless the user explicitly asks for raw Markdown source.\",\n },\n },\n },\n};\nconst SDK_OPTIONS = {\n responseStyle: \"data\" as const,\n throwOnError: true as const,\n};\nconst StructuredReplySchema = z.object({\n body_md: z.string(),\n});\n\nexport interface OpenCodeClientLike {\n readonly client?: OpenCodeSdkClientLike;\n abortSession(sessionId: string): Promise<boolean>;\n deleteSession(sessionId: string): Promise<boolean>;\n forkSession(sessionId: string, messageId?: string): Promise<OpenCodeSession>;\n getHealth(): Promise<OpenCodeHealth>;\n getPath(): Promise<OpenCodePath>;\n listLspStatuses(directory?: string): Promise<OpenCodeLspStatus[]>;\n listMcpStatuses(directory?: string): Promise<Record<string, OpenCodeMcpStatus>>;\n getSessionStatuses(): Promise<Record<string, OpenCodeSessionStatus>>;\n listProjects(): Promise<OpenCodeProject[]>;\n listSessions(): Promise<OpenCodeSession[]>;\n getCurrentProject(): Promise<OpenCodeProject>;\n createSessionForDirectory(directory: string, title?: string): Promise<OpenCodeSession>;\n renameSession(sessionId: string, title: string): Promise<OpenCodeSession>;\n listAgents(): Promise<OpenCodeAgent[]>;\n listPendingPermissions(directory?: string): Promise<OpenCodePermissionRequest[]>;\n replyToPermission(\n requestId: string,\n reply: \"once\" | \"always\" | \"reject\",\n message?: string,\n sessionId?: string,\n ): Promise<boolean>;\n listModels(): Promise<OpenCodeConfiguredModel[]>;\n promptSession(input: PromptSessionInput): Promise<PromptSessionResult>;\n}\n\nexport class OpenCodeClient implements OpenCodeClientLike {\n readonly client: OpenCodeSdkClientLike;\n private readonly fetchFn: typeof fetch;\n private readonly sdkFlavor: SdkFlavor;\n private promptTimeoutPolicy: OpenCodePromptTimeoutPolicy;\n private modelCache: {\n expiresAt: number;\n promise: Promise<OpenCodeConfiguredModel[]> | null;\n value: OpenCodeConfiguredModel[] | null;\n } = {\n expiresAt: 0,\n promise: null,\n value: null,\n };\n\n constructor(\n options?: OpenCodeClientOptions,\n client?: OpenCodeSdkClientLike,\n fetchFn: typeof fetch = fetch,\n promptTimeoutPolicy: Partial<OpenCodePromptTimeoutPolicy> = {},\n ) {\n if (!options && !client) {\n throw new Error(\"OpenCodeClient requires either base URL options or an injected SDK client.\");\n }\n\n this.client = client ?? createOpencodeClient(buildOpenCodeSdkConfig(options as OpenCodeClientOptions));\n this.fetchFn = fetchFn;\n this.sdkFlavor = detectSdkFlavor(this.client);\n this.promptTimeoutPolicy = resolvePromptTimeoutPolicy(promptTimeoutPolicy);\n }\n\n configurePromptTimeoutPolicy(\n promptTimeoutPolicy: Partial<OpenCodePromptTimeoutPolicy>,\n ): void {\n this.promptTimeoutPolicy = resolvePromptTimeoutPolicy({\n ...this.promptTimeoutPolicy,\n ...promptTimeoutPolicy,\n });\n }\n\n private async callScopedSdkMethod<T>(\n scope: \"app\" | \"config\" | \"global\" | \"lsp\" | \"mcp\" | \"path\" | \"permission\" | \"project\" | \"session\",\n method: string,\n input: {\n legacyOptions?: Record<string, unknown>;\n signal?: AbortSignal;\n v2Parameters?: unknown;\n },\n ): Promise<T> {\n const target = this.client[scope] as unknown as Record<string, unknown> | undefined;\n const handler = target?.[method];\n\n if (typeof handler !== \"function\") {\n throw new Error(`OpenCode SDK client does not expose a compatible ${scope}.${method} method.`);\n }\n\n if (this.sdkFlavor === \"legacy\") {\n const response = await (handler as (options: Record<string, unknown>) => Promise<MaybeWrappedResponse<T>>).call(\n target,\n {\n ...SDK_OPTIONS,\n ...(input.signal ? { signal: input.signal } : {}),\n ...(input.legacyOptions ?? {}),\n },\n );\n\n return unwrapSdkData(response);\n }\n\n const response = await (handler as (parameters: unknown, options: Record<string, unknown>) => Promise<MaybeWrappedResponse<T>>).call(\n target,\n input.v2Parameters,\n {\n ...SDK_OPTIONS,\n ...(input.signal ? { signal: input.signal } : {}),\n },\n );\n\n return unwrapSdkData(response);\n }\n\n async getHealth(): Promise<OpenCodeHealth> {\n const rawClient = getRawSdkClient(this.client);\n\n if (rawClient?.get) {\n const response = await this.requestRaw<OpenCodeHealth>(\n \"get\",\n { url: \"/global/health\" },\n );\n\n return response;\n }\n\n const healthEndpoint = this.client.global?.health;\n\n if (typeof healthEndpoint === \"function\") {\n const response = await healthEndpoint.call(\n this.client.global,\n SDK_OPTIONS,\n ) as MaybeWrappedResponse<OpenCodeHealth>;\n\n return unwrapSdkData(response);\n }\n\n if (!rawClient?.get) {\n throw new Error(\"OpenCode SDK client does not expose a compatible health endpoint.\");\n }\n\n return this.requestRaw<OpenCodeHealth>(\n \"get\",\n { url: \"/global/health\" },\n );\n }\n\n async abortSession(sessionId: string): Promise<boolean> {\n if (hasRawSdkMethod(this.client, \"post\")) {\n return this.requestRaw<boolean>(\n \"post\",\n buildRawSessionRequest(this.sdkFlavor, sessionId, \"/abort\"),\n );\n }\n\n return this.callScopedSdkMethod<boolean>(\"session\", \"abort\", {\n legacyOptions: {\n path: {\n id: sessionId,\n },\n },\n v2Parameters: {\n sessionID: sessionId,\n },\n });\n }\n\n async deleteSession(sessionId: string): Promise<boolean> {\n if (hasRawSdkMethod(this.client, \"delete\")) {\n return this.requestRaw<boolean>(\n \"delete\",\n buildRawSessionRequest(this.sdkFlavor, sessionId),\n );\n }\n\n return this.callScopedSdkMethod<boolean>(\"session\", \"delete\", {\n legacyOptions: {\n path: {\n id: sessionId,\n },\n },\n v2Parameters: {\n sessionID: sessionId,\n },\n });\n }\n\n async forkSession(sessionId: string, messageId?: string): Promise<OpenCodeSession> {\n if (hasRawSdkMethod(this.client, \"post\")) {\n return this.requestRaw<OpenCodeSession>(\n \"post\",\n buildRawSessionRequest(\n this.sdkFlavor,\n sessionId,\n \"/fork\",\n messageId?.trim()\n ? {\n body: {\n messageID: messageId.trim(),\n },\n }\n : {},\n ),\n );\n }\n\n return this.callScopedSdkMethod<OpenCodeSession>(\"session\", \"fork\", {\n legacyOptions: {\n path: {\n id: sessionId,\n },\n ...(messageId?.trim()\n ? {\n body: {\n messageID: messageId.trim(),\n },\n }\n : {}),\n },\n v2Parameters: {\n sessionID: sessionId,\n ...(messageId?.trim()\n ? { messageID: messageId.trim() }\n : {}),\n },\n });\n }\n\n async getPath(): Promise<OpenCodePath> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodePath>(\n \"get\",\n { url: \"/path\" },\n );\n }\n\n return this.callScopedSdkMethod<OpenCodePath>(\"path\", \"get\", {});\n }\n\n async listLspStatuses(directory?: string): Promise<OpenCodeLspStatus[]> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodeLspStatus[]>(\n \"get\",\n {\n url: \"/lsp\",\n ...(directory ? { query: { directory } } : {}),\n },\n );\n }\n\n return this.callScopedSdkMethod<OpenCodeLspStatus[]>(\"lsp\", \"status\", {\n legacyOptions: directory\n ? {\n query: {\n directory,\n },\n }\n : undefined,\n v2Parameters: directory\n ? { directory }\n : undefined,\n });\n }\n\n async listMcpStatuses(directory?: string): Promise<Record<string, OpenCodeMcpStatus>> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<Record<string, OpenCodeMcpStatus>>(\n \"get\",\n {\n url: \"/mcp\",\n ...(directory ? { query: { directory } } : {}),\n },\n );\n }\n\n return this.callScopedSdkMethod<Record<string, OpenCodeMcpStatus>>(\"mcp\", \"status\", {\n legacyOptions: directory\n ? {\n query: {\n directory,\n },\n }\n : undefined,\n v2Parameters: directory\n ? { directory }\n : undefined,\n });\n }\n\n async getSessionStatuses(): Promise<Record<string, OpenCodeSessionStatus>> {\n return this.loadSessionStatuses();\n }\n\n async listProjects(): Promise<OpenCodeProject[]> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodeProject[]>(\n \"get\",\n { url: \"/project\" },\n );\n }\n\n return this.callScopedSdkMethod<OpenCodeProject[]>(\"project\", \"list\", {});\n }\n\n async listSessions(): Promise<OpenCodeSession[]> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodeSession[]>(\n \"get\",\n { url: \"/session\" },\n );\n }\n\n return this.callScopedSdkMethod<OpenCodeSession[]>(\"session\", \"list\", {});\n }\n\n async getCurrentProject(): Promise<OpenCodeProject> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodeProject>(\n \"get\",\n { url: \"/project/current\" },\n );\n }\n\n return this.callScopedSdkMethod<OpenCodeProject>(\"project\", \"current\", {});\n }\n\n async createSessionForDirectory(directory: string, title?: string): Promise<OpenCodeSession> {\n if (hasRawSdkMethod(this.client, \"post\")) {\n return this.requestRaw<OpenCodeSession>(\n \"post\",\n {\n url: \"/session\",\n query: { directory },\n ...(title\n ? {\n body: {\n title,\n },\n }\n : {}),\n },\n );\n }\n\n return this.callScopedSdkMethod<OpenCodeSession>(\"session\", \"create\", {\n legacyOptions: {\n query: {\n directory,\n },\n ...(title\n ? {\n body: {\n title,\n },\n }\n : {}),\n },\n v2Parameters: title ? { directory, title } : { directory },\n });\n }\n\n async renameSession(sessionId: string, title: string): Promise<OpenCodeSession> {\n if (hasRawSdkMethod(this.client, \"patch\")) {\n return this.requestRaw<OpenCodeSession>(\n \"patch\",\n buildRawSessionRequest(this.sdkFlavor, sessionId, \"\", {\n body: {\n title,\n },\n }),\n );\n }\n\n return this.callScopedSdkMethod<OpenCodeSession>(\"session\", \"update\", {\n legacyOptions: {\n path: {\n id: sessionId,\n },\n body: {\n title,\n },\n },\n v2Parameters: {\n sessionID: sessionId,\n title,\n },\n });\n }\n\n async listAgents(): Promise<OpenCodeAgent[]> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodeAgent[]>(\n \"get\",\n { url: \"/agent\" },\n );\n }\n\n return this.callScopedSdkMethod<OpenCodeAgent[]>(\"app\", \"agents\", {});\n }\n\n async listPendingPermissions(directory?: string): Promise<OpenCodePermissionRequest[]> {\n if (this.sdkFlavor === \"v2\" && hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodePermissionRequest[]>(\n \"get\",\n {\n url: \"/permission\",\n ...(directory ? { query: { directory } } : {}),\n },\n );\n }\n\n return this.callScopedSdkMethod<OpenCodePermissionRequest[]>(\"permission\", \"list\", {\n legacyOptions: directory\n ? {\n query: {\n directory,\n },\n }\n : undefined,\n v2Parameters: directory\n ? { directory }\n : undefined,\n });\n }\n\n async replyToPermission(\n requestId: string,\n reply: \"once\" | \"always\" | \"reject\",\n message?: string,\n sessionId?: string,\n ): Promise<boolean> {\n if (this.sdkFlavor === \"legacy\" && sessionId) {\n if (hasRawSdkMethod(this.client, \"post\")) {\n return this.requestRaw<boolean>(\n \"post\",\n buildRawLegacyPermissionReplyRequest(sessionId, requestId, reply),\n );\n }\n\n const legacyClient = this.client as OpenCodeSdkClientLike & {\n postSessionIdPermissionsPermissionId?: (options: Record<string, unknown>) => Promise<MaybeWrappedResponse<boolean>>;\n };\n\n if (typeof legacyClient.postSessionIdPermissionsPermissionId === \"function\") {\n const response = await legacyClient.postSessionIdPermissionsPermissionId({\n ...SDK_OPTIONS,\n body: {\n response: reply,\n },\n path: {\n id: sessionId,\n permissionID: requestId,\n },\n });\n\n return unwrapSdkData(response);\n }\n }\n\n if (hasRawSdkMethod(this.client, \"post\")) {\n return this.requestRaw<boolean>(\n \"post\",\n {\n url: \"/permission/{requestID}/reply\",\n path: {\n requestID: requestId,\n },\n body: {\n reply,\n ...(message?.trim()\n ? { message: message.trim() }\n : {}),\n },\n },\n );\n }\n\n return this.callScopedSdkMethod<boolean>(\"permission\", \"reply\", {\n legacyOptions: sessionId\n ? {\n body: {\n response: reply,\n },\n path: {\n id: sessionId,\n permissionID: requestId,\n },\n }\n : undefined,\n v2Parameters: {\n requestID: requestId,\n reply,\n ...(message?.trim()\n ? { message: message.trim() }\n : {}),\n },\n });\n }\n\n async listModels(): Promise<OpenCodeConfiguredModel[]> {\n const now = Date.now();\n\n if (this.modelCache.value && this.modelCache.expiresAt > now) {\n return this.modelCache.value;\n }\n\n if (this.modelCache.promise) {\n return this.modelCache.promise;\n }\n\n const refreshPromise = this.loadModels().finally(() => {\n if (this.modelCache.promise === refreshPromise) {\n this.modelCache.promise = null;\n }\n });\n\n this.modelCache.promise = refreshPromise;\n\n return refreshPromise;\n }\n\n async promptSession(input: PromptSessionInput): Promise<PromptSessionResult> {\n const startedAt = Date.now();\n const promptText = input.prompt?.trim() ?? \"\";\n const parts = [\n ...(promptText\n ? [{\n type: \"text\" as const,\n text: promptText,\n }]\n : []),\n ...((input.files ?? []).map((file) => ({\n type: \"file\" as const,\n filename: file.filename,\n mime: file.mime,\n url: file.url,\n }))),\n ];\n\n if (parts.length === 0) {\n throw new Error(\"Prompt requires text or file attachments.\");\n }\n\n const knownMessageIds = await this.captureKnownMessageIds(input.sessionId);\n const initialData = await this.sendPromptRequest(input, parts);\n const resolvedData = await this.resolvePromptResponse(\n input,\n initialData,\n knownMessageIds,\n startedAt,\n );\n const finishedAt = Date.now();\n\n return buildPromptSessionResult(resolvedData, {\n emptyResponseText: EMPTY_RESPONSE_TEXT,\n finishedAt,\n startedAt,\n structured: input.structured ?? false,\n });\n }\n\n private async resolvePromptResponse(\n input: PromptSessionInput,\n data: OpenCodePromptResponse | null,\n knownMessageIds: Set<string>,\n startedAt: number,\n ): Promise<OpenCodePromptResponse> {\n const structured = input.structured ?? false;\n\n if (data && shouldReturnPromptResponseImmediately(data, structured)) {\n return data;\n }\n\n const messageId = data ? extractMessageId(data.info) : null;\n const candidateOptions: PromptResponseCandidateOptions = {\n initialMessageId: messageId,\n initialParentId: data ? toAssistantMessage(data.info)?.parentID ?? null : null,\n knownMessageIds,\n requestStartedAt: resolvePromptCandidateStartTime(startedAt, data),\n structured,\n };\n let bestCandidate = selectPromptResponseCandidate(\n data ? [data] : [],\n candidateOptions,\n );\n let lastProgressAt = Date.now();\n const deadlineAt = startedAt + this.promptTimeoutPolicy.waitTimeoutMs;\n let idleStatusSeen = false;\n let attempt = 0;\n\n while (true) {\n const remainingWaitMs = deadlineAt - Date.now();\n const remainingInactivityMs = this.promptTimeoutPolicy.recoveryInactivityTimeoutMs - (Date.now() - lastProgressAt);\n\n if (remainingWaitMs <= 0 || remainingInactivityMs <= 0) {\n break;\n }\n\n const delayMs = getPromptMessagePollDelayMs(attempt);\n attempt += 1;\n\n if (delayMs > 0) {\n const remainingMs = Math.min(remainingWaitMs, remainingInactivityMs);\n\n if (remainingMs <= 0) {\n break;\n }\n\n await delay(Math.min(delayMs, remainingMs));\n }\n\n if (messageId) {\n const next = await this.fetchPromptMessage(input.sessionId, messageId);\n\n if (next) {\n const nextCandidate = selectPromptResponseCandidate(\n [bestCandidate, next],\n candidateOptions,\n );\n\n if (nextCandidate) {\n if (didPromptResponseAdvance(bestCandidate, nextCandidate, structured)) {\n lastProgressAt = Date.now();\n idleStatusSeen = false;\n }\n\n bestCandidate = nextCandidate;\n }\n\n if (bestCandidate && shouldReturnPromptResponseImmediately(bestCandidate, structured)) {\n return bestCandidate;\n }\n }\n }\n\n const latest = await this.findLatestPromptResponse(\n input.sessionId,\n candidateOptions,\n \"poll-messages\",\n );\n\n if (latest) {\n const nextCandidate = selectPromptResponseCandidate(\n [bestCandidate, latest],\n candidateOptions,\n );\n\n if (nextCandidate) {\n if (didPromptResponseAdvance(bestCandidate, nextCandidate, structured)) {\n lastProgressAt = Date.now();\n idleStatusSeen = false;\n }\n\n bestCandidate = nextCandidate;\n }\n\n if (bestCandidate && shouldReturnPromptResponseImmediately(bestCandidate, structured)) {\n return bestCandidate;\n }\n }\n\n const status = await this.fetchPromptSessionStatus(input.sessionId);\n\n if (status?.type === \"busy\" || status?.type === \"retry\") {\n lastProgressAt = Date.now();\n idleStatusSeen = false;\n } else if (status?.type === \"idle\") {\n if (idleStatusSeen) {\n break;\n }\n\n idleStatusSeen = true;\n }\n\n if (\n bestCandidate\n && isCompletedEmptyPromptResponse(bestCandidate, structured)\n && status?.type !== \"busy\"\n && status?.type !== \"retry\"\n ) {\n break;\n }\n\n if (Date.now() >= deadlineAt) {\n break;\n }\n }\n\n const latest = await this.findLatestPromptResponse(\n input.sessionId,\n candidateOptions,\n \"final-scan\",\n );\n\n const resolved = selectPromptResponseCandidate(\n [bestCandidate, latest],\n candidateOptions,\n );\n\n if (!resolved || shouldPollPromptMessage(resolved, structured)) {\n const timeoutReason = Date.now() >= deadlineAt\n ? \"max-wait\"\n : \"recovery-inactivity\";\n const timeoutMs = timeoutReason === \"max-wait\"\n ? this.promptTimeoutPolicy.waitTimeoutMs\n : this.promptTimeoutPolicy.recoveryInactivityTimeoutMs;\n const error = createOpenCodePromptTimeoutError({\n sessionId: input.sessionId,\n stage: \"final-scan\",\n timeoutMs,\n messageId: messageId ?? undefined,\n });\n this.logPromptRequest(\"warn\", {\n lastProgressAt,\n messageId: messageId ?? undefined,\n sdkFlavor: this.sdkFlavor,\n sessionId: input.sessionId,\n stage: \"final-scan\",\n timeoutMs,\n timeoutReason,\n }, \"OpenCode prompt recovery timed out\");\n throw error;\n }\n\n return resolved;\n }\n\n private async fetchPromptMessage(\n sessionId: string,\n messageId: string,\n ): Promise<OpenCodePromptResponse | null> {\n try {\n return await this.runPromptRequestWithTimeout(\n {\n sessionId,\n stage: \"poll-message\",\n timeoutMs: this.promptTimeoutPolicy.pollRequestTimeoutMs,\n messageId,\n },\n async (signal) => {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return normalizePromptResponse(await this.requestRaw<OpenCodePromptResponse>(\n \"get\",\n buildRawSessionMessageRequest(this.sdkFlavor, sessionId, messageId, { signal }),\n ));\n }\n\n if (typeof this.client.session.message !== \"function\") {\n return null;\n }\n\n return normalizePromptResponse(await this.callScopedSdkMethod<OpenCodePromptResponse>(\"session\", \"message\", {\n legacyOptions: {\n path: {\n id: sessionId,\n messageID: messageId,\n },\n },\n signal,\n v2Parameters: {\n sessionID: sessionId,\n messageID: messageId,\n },\n }));\n },\n );\n } catch (error) {\n this.logPromptRequestFailure(error, {\n sessionId,\n stage: \"poll-message\",\n timeoutMs: this.promptTimeoutPolicy.pollRequestTimeoutMs,\n messageId,\n });\n return null;\n }\n }\n\n private async captureKnownMessageIds(sessionId: string): Promise<Set<string>> {\n const messages = await this.fetchRecentPromptMessages(sessionId, \"capture-known-messages\");\n\n if (!messages) {\n return new Set();\n }\n\n return new Set(\n messages\n .map((message) => extractMessageId(message.info))\n .filter((id): id is string => typeof id === \"string\" && id.length > 0),\n );\n }\n\n private async fetchRecentPromptMessages(\n sessionId: string,\n stage: Extract<OpenCodePromptTimeoutStage, \"capture-known-messages\" | \"poll-messages\" | \"final-scan\">,\n ): Promise<OpenCodePromptResponse[] | null> {\n try {\n return await this.runPromptRequestWithTimeout(\n {\n sessionId,\n stage,\n timeoutMs: this.promptTimeoutPolicy.pollRequestTimeoutMs,\n },\n async (signal) => {\n if (hasRawSdkMethod(this.client, \"get\")) {\n const response = await this.requestRaw<OpenCodePromptResponse[]>(\n \"get\",\n buildRawSessionRequest(this.sdkFlavor, sessionId, \"/message\", {\n query: {\n limit: PROMPT_MESSAGE_POLL_LIMIT,\n },\n signal,\n }),\n );\n\n return normalizePromptResponses(response);\n }\n\n if (typeof this.client.session.messages !== \"function\") {\n return null;\n }\n\n return normalizePromptResponses(await this.callScopedSdkMethod<OpenCodePromptResponse[]>(\"session\", \"messages\", {\n legacyOptions: {\n path: {\n id: sessionId,\n },\n query: {\n limit: PROMPT_MESSAGE_POLL_LIMIT,\n },\n },\n signal,\n v2Parameters: {\n sessionID: sessionId,\n limit: PROMPT_MESSAGE_POLL_LIMIT,\n },\n }));\n },\n );\n } catch (error) {\n this.logPromptRequestFailure(error, {\n sessionId,\n stage,\n timeoutMs: this.promptTimeoutPolicy.pollRequestTimeoutMs,\n });\n return null;\n }\n }\n\n private async fetchPromptSessionStatus(sessionId: string): Promise<OpenCodeSessionStatus | null> {\n try {\n const statuses = await this.runPromptRequestWithTimeout(\n {\n sessionId,\n stage: \"poll-status\",\n timeoutMs: this.promptTimeoutPolicy.pollRequestTimeoutMs,\n },\n async (signal) => this.loadSessionStatuses(signal),\n );\n\n return statuses[sessionId] ?? null;\n } catch (error) {\n this.logPromptRequestFailure(error, {\n sessionId,\n stage: \"poll-status\",\n timeoutMs: this.promptTimeoutPolicy.pollRequestTimeoutMs,\n });\n return null;\n }\n }\n\n private async findLatestPromptResponse(\n sessionId: string,\n options: PromptResponseCandidateOptions,\n stage: Extract<OpenCodePromptTimeoutStage, \"poll-messages\" | \"final-scan\">,\n ): Promise<OpenCodePromptResponse | null> {\n const messages = await this.fetchRecentPromptMessages(sessionId, stage);\n\n if (!messages || messages.length === 0) {\n return null;\n }\n\n return selectPromptResponseCandidate(messages, options);\n }\n\n private async loadModels(): Promise<OpenCodeConfiguredModel[]> {\n const [config, providerCatalog] = await Promise.all([\n this.loadConfig(),\n this.loadProviderCatalog(),\n ]);\n const providerAvailability = await resolveProviderAvailability(config, this.fetchFn);\n const models = buildSelectableModels(\n config,\n providerCatalog.providers,\n providerAvailability,\n );\n\n this.modelCache = {\n expiresAt: Date.now() + 60_000,\n promise: null,\n value: models,\n };\n\n return models;\n }\n\n private async loadConfig(): Promise<OpenCodeConfig> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodeConfig>(\n \"get\",\n { url: \"/config\" },\n );\n }\n\n return this.callScopedSdkMethod<OpenCodeConfig>(\"config\", \"get\", {});\n }\n\n private async loadProviderCatalog(): Promise<OpenCodeProviderCatalog> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<OpenCodeProviderCatalog>(\n \"get\",\n { url: \"/config/providers\" },\n );\n }\n\n return this.callScopedSdkMethod<OpenCodeProviderCatalog>(\"config\", \"providers\", {});\n }\n\n private async sendPromptRequest(\n input: PromptSessionInput,\n parts: Array<\n | { type: \"text\"; text: string }\n | { type: \"file\"; filename?: string; mime: string; url: string }\n >,\n ): Promise<OpenCodePromptResponse | null> {\n const requestBody = {\n ...(input.agent ? { agent: input.agent } : {}),\n ...(input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {}),\n ...(input.model ? { model: input.model } : {}),\n ...(input.variant ? { variant: input.variant } : {}),\n parts,\n };\n const requestParameters = {\n sessionID: input.sessionId,\n ...requestBody,\n };\n\n try {\n if (hasRawSdkMethod(this.client, \"post\")) {\n return await this.runPromptRequestWithTimeout(\n {\n sessionId: input.sessionId,\n stage: \"send-prompt\",\n timeoutMs: this.promptTimeoutPolicy.waitTimeoutMs,\n },\n async (signal) => normalizePromptResponse(await this.requestRaw<OpenCodePromptResponse>(\n \"post\",\n buildRawSessionRequest(this.sdkFlavor, input.sessionId, \"/message\", {\n body: requestBody,\n signal,\n }),\n )),\n );\n }\n\n if (typeof this.client.session?.prompt === \"function\") {\n return await this.runPromptRequestWithTimeout(\n {\n sessionId: input.sessionId,\n stage: \"send-prompt\",\n timeoutMs: this.promptTimeoutPolicy.waitTimeoutMs,\n },\n async (signal) => normalizePromptResponse(await this.callScopedSdkMethod<OpenCodePromptResponse>(\"session\", \"prompt\", {\n legacyOptions: {\n body: requestBody,\n path: {\n id: input.sessionId,\n },\n },\n signal,\n v2Parameters: requestParameters,\n })),\n );\n }\n\n if (typeof this.client.session?.promptAsync === \"function\") {\n await this.runPromptRequestWithTimeout(\n {\n sessionId: input.sessionId,\n stage: \"send-prompt\",\n timeoutMs: this.promptTimeoutPolicy.waitTimeoutMs,\n },\n async (signal) => {\n await this.callScopedSdkMethod<void>(\"session\", \"promptAsync\", {\n legacyOptions: {\n body: requestBody,\n path: {\n id: input.sessionId,\n },\n },\n signal,\n v2Parameters: requestParameters,\n });\n },\n );\n\n return null;\n }\n } catch (error) {\n this.logPromptRequestFailure(error, {\n sessionId: input.sessionId,\n stage: \"send-prompt\",\n timeoutMs: this.promptTimeoutPolicy.waitTimeoutMs,\n });\n throw error;\n }\n\n throw new Error(\"OpenCode SDK client does not expose a compatible prompt endpoint.\");\n }\n\n private async requestRaw<T>(\n method: keyof Pick<RawSdkHttpClient, \"delete\" | \"get\" | \"patch\" | \"post\">,\n options: {\n body?: unknown;\n path?: Record<string, unknown>;\n query?: Record<string, unknown>;\n signal?: AbortSignal;\n url: string;\n },\n ): Promise<T> {\n const rawClient = getRawSdkClient(this.client);\n const handler = rawClient?.[method];\n\n if (typeof handler !== \"function\") {\n throw new Error(`OpenCode SDK client does not expose a compatible raw ${method.toUpperCase()} method.`);\n }\n\n const response = await handler({\n ...SDK_OPTIONS,\n ...options,\n }) as MaybeWrappedResponse<T>;\n\n return unwrapSdkData(response);\n }\n\n private async loadSessionStatuses(\n signal?: AbortSignal,\n ): Promise<Record<string, OpenCodeSessionStatus>> {\n if (hasRawSdkMethod(this.client, \"get\")) {\n return this.requestRaw<Record<string, OpenCodeSessionStatus>>(\n \"get\",\n {\n url: \"/session/status\",\n ...(signal ? { signal } : {}),\n },\n );\n }\n\n return this.callScopedSdkMethod<Record<string, OpenCodeSessionStatus>>(\"session\", \"status\", {\n signal,\n });\n }\n\n private async runPromptRequestWithTimeout<T>(\n input: {\n messageId?: string | null;\n sessionId: string;\n stage: OpenCodePromptTimeoutStage;\n timeoutMs: number;\n },\n operation: (signal: AbortSignal) => Promise<T>,\n ): Promise<T> {\n const startedAt = Date.now();\n const controller = new AbortController();\n let timeoutHandle: ReturnType<typeof setTimeout> | null = null;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n reject(createOpenCodePromptTimeoutError({\n sessionId: input.sessionId,\n stage: input.stage,\n timeoutMs: input.timeoutMs,\n messageId: input.messageId ?? undefined,\n elapsedMs: Date.now() - startedAt,\n }));\n controller.abort();\n }, input.timeoutMs);\n });\n\n try {\n return await Promise.race([\n operation(controller.signal),\n timeoutPromise,\n ]);\n } finally {\n if (timeoutHandle !== null) {\n clearTimeout(timeoutHandle);\n }\n }\n }\n\n private logPromptRequestFailure(\n error: unknown,\n input: {\n messageId?: string | null;\n sessionId: string;\n stage: OpenCodePromptTimeoutStage;\n timeoutMs: number;\n },\n ) {\n if (error instanceof OpenCodePromptTimeoutError) {\n this.logPromptRequest(\"warn\", {\n endpointKind: resolvePromptEndpointKind(error.data.stage),\n elapsedMs: error.data.elapsedMs,\n messageId: error.data.messageId,\n sdkFlavor: this.sdkFlavor,\n sessionId: error.data.sessionId,\n stage: error.data.stage,\n timeoutMs: error.data.timeoutMs,\n }, \"OpenCode prompt request timed out\");\n return;\n }\n\n this.logPromptRequest(\"warn\", {\n endpointKind: resolvePromptEndpointKind(input.stage),\n error,\n messageId: input.messageId ?? undefined,\n sdkFlavor: this.sdkFlavor,\n sessionId: input.sessionId,\n stage: input.stage,\n timeoutMs: input.timeoutMs,\n }, \"OpenCode prompt request failed\");\n }\n\n private logPromptRequest(\n level: \"warn\" | \"error\" | \"info\",\n extra: Record<string, unknown>,\n message: string,\n ) {\n const log = this.client.app?.log;\n\n if (typeof log !== \"function\") {\n return;\n }\n\n void log.call(this.client.app, {\n service: PROMPT_LOG_SERVICE,\n level,\n message,\n extra,\n }).catch(() => undefined);\n }\n}\n\nexport function createOpenCodeClientFromSdkClient(\n client: OpenCodeSdkClientLike,\n fetchFn: typeof fetch = fetch,\n promptTimeoutPolicy: Partial<OpenCodePromptTimeoutPolicy> = {},\n): OpenCodeClientLike {\n return new OpenCodeClient(undefined, client, fetchFn, promptTimeoutPolicy);\n}\n\nfunction buildSelectableModels(\n config: OpenCodeConfig,\n providers: OpenCodeProvider[],\n providerAvailability: OpenCodeProviderAvailability = new Map(),\n): OpenCodeConfiguredModel[] {\n const configuredProviders = config.provider ?? {};\n const providersById = new Map(providers.map((provider) => [provider.id, provider]));\n const models = new Map<string, OpenCodeConfiguredModel>();\n\n for (const [providerId, providerConfig] of Object.entries(configuredProviders)) {\n const configuredModels = providerConfig.models ?? {};\n\n for (const [configuredModelKey, modelConfig] of Object.entries(configuredModels)) {\n const model = buildConfiguredModel(\n providerId,\n providerConfig,\n configuredModelKey,\n modelConfig,\n providersById.get(providerId),\n providerAvailability.get(providerId) ?? null,\n );\n\n if (model) {\n models.set(model.qualifiedId, model);\n }\n }\n }\n\n for (const provider of providers) {\n for (const model of buildCatalogProviderModels(provider, providerAvailability.get(provider.id) ?? null)) {\n if (!models.has(model.qualifiedId)) {\n models.set(model.qualifiedId, model);\n }\n }\n }\n\n return [...models.values()];\n}\n\nfunction buildConfiguredModel(\n providerId: string,\n providerConfig: OpenCodeProviderConfig,\n configuredModelKey: string,\n modelConfig: OpenCodeModelConfig,\n providerCatalog: OpenCodeProvider | undefined,\n availableModelIds: Set<string> | null,\n): OpenCodeConfiguredModel | null {\n const modelId = modelConfig.id ?? configuredModelKey;\n const catalogModel = providerCatalog?.models[modelId];\n\n if (!isTextModel(modelConfig, catalogModel)) {\n return null;\n }\n\n if (!isModelAvailable(modelId, availableModelIds)) {\n return null;\n }\n\n return {\n id: modelId,\n providerID: providerId,\n providerName: resolveProviderDisplayName(providerId, providerConfig, providerCatalog),\n name: modelConfig.name ?? catalogModel?.name ?? modelId,\n qualifiedId: `${providerId}/${modelId}`,\n reasoning: modelConfig.reasoning ?? catalogModel?.capabilities.reasoning ?? false,\n variants: normalizeVariants(modelConfig.variants ?? catalogModel?.variants),\n };\n}\n\nfunction buildCatalogProviderModels(\n provider: OpenCodeProvider,\n availableModelIds: Set<string> | null,\n): OpenCodeConfiguredModel[] {\n return Object.values(provider.models)\n .filter((model) => isCatalogTextModel(model) && isModelAvailable(model.id, availableModelIds))\n .map((model) => ({\n id: model.id,\n providerID: provider.id,\n providerName: provider.name,\n name: model.name,\n qualifiedId: `${provider.id}/${model.id}`,\n reasoning: model.capabilities.reasoning,\n variants: normalizeVariants(model.variants),\n }));\n}\n\nfunction isTextModel(modelConfig: OpenCodeModelConfig, catalogModel: OpenCodeModel | undefined): boolean {\n if (modelConfig.modalities) {\n return modelConfig.modalities.input.includes(\"text\") && modelConfig.modalities.output.includes(\"text\");\n }\n\n if (catalogModel) {\n return catalogModel.capabilities.input.text && catalogModel.capabilities.output.text;\n }\n\n return true;\n}\n\nfunction isCatalogTextModel(catalogModel: OpenCodeModel): boolean {\n return catalogModel.capabilities.input.text && catalogModel.capabilities.output.text;\n}\n\nfunction isModelAvailable(modelId: string, availableModelIds: Set<string> | null): boolean {\n return availableModelIds === null || availableModelIds.has(modelId);\n}\n\nfunction resolveProviderDisplayName(\n providerId: string,\n providerConfig: OpenCodeProviderConfig,\n providerCatalog: OpenCodeProvider | undefined,\n): string {\n if (providerConfig.name) {\n return providerConfig.name;\n }\n\n const configuredBaseUrl = extractConfiguredBaseUrl(providerConfig);\n\n if (configuredBaseUrl) {\n const host = safeParseUrlHost(configuredBaseUrl);\n const label = providerCatalog?.name ?? providerId;\n\n return host ? `${label} (${host})` : label;\n }\n\n return providerCatalog?.name ?? providerId;\n}\n\nfunction extractConfiguredBaseUrl(providerConfig: OpenCodeProviderConfig): string | null {\n const options = providerConfig.options;\n\n if (!options || typeof options !== \"object\") {\n return null;\n }\n\n const baseUrl = \"baseURL\" in options ? options.baseURL : null;\n\n return typeof baseUrl === \"string\" && baseUrl.trim().length > 0\n ? baseUrl.trim()\n : null;\n}\n\nfunction safeParseUrlHost(value: string): string | null {\n try {\n return new URL(value).host || null;\n } catch {\n return null;\n }\n}\n\nfunction normalizeVariants(\n variants: OpenCodeModelConfig[\"variants\"] | OpenCodeModel[\"variants\"] | undefined,\n): Record<string, Record<string, unknown>> {\n if (!variants) {\n return {};\n }\n\n return Object.fromEntries(\n Object.entries(variants)\n .filter(([, config]) => !config?.disabled)\n .map(([variant, config]) => {\n const normalized = Object.fromEntries(\n Object.entries(config).filter(([key]) => key !== \"disabled\"),\n );\n\n return [variant, normalized];\n }),\n );\n}\n\nfunction extractTextFromParts(parts: Part[] | null | undefined): string {\n if (!Array.isArray(parts)) {\n return \"\";\n }\n\n return parts\n .filter((part): part is Extract<Part, { type: \"text\"; text: string }> => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\")\n .trim();\n}\n\nfunction buildPromptSessionResult(\n data: OpenCodePromptResponse,\n options: {\n emptyResponseText: string;\n finishedAt: number;\n startedAt: number;\n structured: boolean;\n },\n): PromptSessionResult {\n const assistantInfo = toAssistantMessage(data.info);\n const structuredPayload = extractStructuredPayload(assistantInfo);\n const bodyMd = options.structured ? extractStructuredMarkdown(structuredPayload) : null;\n const responseParts = Array.isArray(data.parts) ? data.parts : [];\n const fallbackText = extractTextFromParts(responseParts) || bodyMd || options.emptyResponseText;\n\n return {\n assistantError: assistantInfo?.error ?? null,\n bodyMd,\n fallbackText,\n info: assistantInfo,\n metrics: extractPromptMetrics(assistantInfo, options.startedAt, options.finishedAt),\n parts: responseParts,\n structured: structuredPayload ?? null,\n };\n}\n\nfunction shouldPollPromptMessage(\n data: OpenCodePromptResponse,\n structured: boolean,\n): boolean {\n const assistantInfo = toAssistantMessage(data.info);\n const bodyMd = structured ? extractStructuredMarkdown(extractStructuredPayload(assistantInfo)) : null;\n const responseParts = Array.isArray(data.parts) ? data.parts : [];\n const hasText = extractTextFromParts(responseParts).length > 0;\n const hasAssistantError = !!assistantInfo?.error;\n const isCompleted = isAssistantMessageCompleted(assistantInfo);\n\n return !hasText && !bodyMd && !hasAssistantError && !isCompleted;\n}\n\nfunction shouldReturnPromptResponseImmediately(\n data: OpenCodePromptResponse,\n structured: boolean,\n): boolean {\n return !shouldPollPromptMessage(data, structured)\n && !isCompletedEmptyPromptResponse(data, structured);\n}\n\nfunction isPromptResponseUsable(\n data: OpenCodePromptResponse,\n structured: boolean,\n): boolean {\n return !shouldPollPromptMessage(data, structured)\n && !isCompletedEmptyPromptResponse(data, structured);\n}\n\nfunction normalizePromptResponse(response: OpenCodePromptResponse | null | undefined): OpenCodePromptResponse {\n return {\n info: isPlainRecord(response?.info) ? response.info : null,\n parts: normalizePromptParts(response?.parts),\n };\n}\n\nfunction normalizePromptResponses(\n responses: OpenCodePromptResponse[] | null | undefined,\n): OpenCodePromptResponse[] | null {\n if (!Array.isArray(responses)) {\n return null;\n }\n\n return responses.map((response) => normalizePromptResponse(response));\n}\n\nfunction normalizePromptParts(parts: Part[] | null | undefined): Part[] {\n return Array.isArray(parts) ? parts : [];\n}\n\nfunction toAssistantMessage(message: unknown): OpenCodeAssistantMessage | null {\n if (!message || typeof message !== \"object\") {\n return null;\n }\n\n if (\"role\" in message && message.role !== \"assistant\") {\n return null;\n }\n\n const normalized: OpenCodeAssistantMessage = {};\n\n if (\"agent\" in message && typeof message.agent === \"string\" && message.agent.trim().length > 0) {\n normalized.agent = message.agent;\n }\n\n if (\"cost\" in message && typeof message.cost === \"number\" && Number.isFinite(message.cost)) {\n normalized.cost = message.cost;\n }\n\n const error = normalizeAssistantError(\"error\" in message ? message.error : undefined);\n\n if (error) {\n normalized.error = error;\n }\n\n if (\"finish\" in message && typeof message.finish === \"string\" && message.finish.trim().length > 0) {\n normalized.finish = message.finish;\n }\n\n if (\"id\" in message && typeof message.id === \"string\" && message.id.trim().length > 0) {\n normalized.id = message.id;\n }\n\n if (\"mode\" in message && typeof message.mode === \"string\" && message.mode.trim().length > 0) {\n normalized.mode = message.mode;\n }\n\n if (\"modelID\" in message && typeof message.modelID === \"string\" && message.modelID.trim().length > 0) {\n normalized.modelID = message.modelID;\n }\n\n if (\"parentID\" in message && typeof message.parentID === \"string\" && message.parentID.trim().length > 0) {\n normalized.parentID = message.parentID;\n }\n\n if (\"path\" in message && isPlainRecord(message.path)) {\n normalized.path = {\n ...(typeof message.path.cwd === \"string\" && message.path.cwd.trim().length > 0\n ? { cwd: message.path.cwd }\n : {}),\n ...(typeof message.path.root === \"string\" && message.path.root.trim().length > 0\n ? { root: message.path.root }\n : {}),\n };\n }\n\n if (\"providerID\" in message && typeof message.providerID === \"string\" && message.providerID.trim().length > 0) {\n normalized.providerID = message.providerID;\n }\n\n if (\"role\" in message && message.role === \"assistant\") {\n normalized.role = \"assistant\";\n }\n\n if (\"sessionID\" in message && typeof message.sessionID === \"string\" && message.sessionID.trim().length > 0) {\n normalized.sessionID = message.sessionID;\n }\n\n const structuredPayload = extractStructuredPayload(message);\n\n if (structuredPayload !== null) {\n normalized.structured = structuredPayload;\n }\n\n if (\"summary\" in message && typeof message.summary === \"boolean\") {\n normalized.summary = message.summary;\n }\n\n if (\"time\" in message && isPlainRecord(message.time)) {\n normalized.time = {\n ...(typeof message.time.created === \"number\" && Number.isFinite(message.time.created)\n ? { created: message.time.created }\n : {}),\n ...(typeof message.time.completed === \"number\" && Number.isFinite(message.time.completed)\n ? { completed: message.time.completed }\n : {}),\n };\n }\n\n if (\"tokens\" in message && isPlainRecord(message.tokens)) {\n normalized.tokens = {\n ...(typeof message.tokens.input === \"number\" && Number.isFinite(message.tokens.input)\n ? { input: message.tokens.input }\n : {}),\n ...(typeof message.tokens.output === \"number\" && Number.isFinite(message.tokens.output)\n ? { output: message.tokens.output }\n : {}),\n ...(typeof message.tokens.reasoning === \"number\" && Number.isFinite(message.tokens.reasoning)\n ? { reasoning: message.tokens.reasoning }\n : {}),\n ...(typeof message.tokens.total === \"number\" && Number.isFinite(message.tokens.total)\n ? { total: message.tokens.total }\n : {}),\n ...(isPlainRecord(message.tokens.cache)\n ? {\n cache: {\n ...(typeof message.tokens.cache.read === \"number\" && Number.isFinite(message.tokens.cache.read)\n ? { read: message.tokens.cache.read }\n : {}),\n ...(typeof message.tokens.cache.write === \"number\" && Number.isFinite(message.tokens.cache.write)\n ? { write: message.tokens.cache.write }\n : {}),\n },\n }\n : {}),\n };\n }\n\n if (\"variant\" in message && typeof message.variant === \"string\" && message.variant.trim().length > 0) {\n normalized.variant = message.variant;\n }\n\n return normalized;\n}\n\nfunction extractMessageId(message: unknown): string | null {\n if (!isPlainRecord(message)) {\n return null;\n }\n\n return typeof message.id === \"string\" && message.id.trim().length > 0\n ? message.id\n : null;\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction didPromptResponseAdvance(\n previous: OpenCodePromptResponse | null,\n next: OpenCodePromptResponse,\n structured: boolean,\n): boolean {\n return getPromptResponseProgressSignature(previous, structured)\n !== getPromptResponseProgressSignature(next, structured);\n}\n\nfunction createOpenCodePromptTimeoutError(\n input: Omit<OpenCodePromptTimeoutErrorData, \"elapsedMs\" | \"message\"> & {\n elapsedMs?: number;\n message?: string;\n },\n): OpenCodePromptTimeoutError {\n return new OpenCodePromptTimeoutError({\n ...input,\n elapsedMs: input.elapsedMs ?? input.timeoutMs,\n message: input.message ?? \"The OpenCode host did not finish this request in time.\",\n });\n}\n\nfunction resolvePromptEndpointKind(stage: OpenCodePromptTimeoutStage): \"message\" | \"messages\" | \"prompt\" | \"status\" {\n switch (stage) {\n case \"capture-known-messages\":\n case \"poll-messages\":\n case \"final-scan\":\n return \"messages\";\n case \"poll-message\":\n return \"message\";\n case \"poll-status\":\n return \"status\";\n case \"send-prompt\":\n default:\n return \"prompt\";\n }\n}\n\nfunction getPromptMessagePollDelayMs(attempt: number): number {\n return PROMPT_MESSAGE_POLL_INITIAL_DELAYS_MS[attempt] ?? PROMPT_MESSAGE_POLL_INTERVAL_MS;\n}\n\nfunction extractStructuredMarkdown(structured: unknown): string | null {\n const parsed = StructuredReplySchema.safeParse(structured);\n\n if (!parsed.success) {\n return null;\n }\n\n const bodyMd = parsed.data.body_md.replace(/\\r\\n?/g, \"\\n\").trim();\n\n return bodyMd.length > 0 ? bodyMd : null;\n}\n\nfunction extractPromptMetrics(\n info: OpenCodeAssistantMessage | null | undefined,\n startedAt: number,\n finishedAt: number,\n): PromptSessionMetrics {\n const createdAt = typeof info?.time?.created === \"number\" && Number.isFinite(info.time.created)\n ? info.time.created\n : null;\n const completedAt = typeof info?.time?.completed === \"number\" && Number.isFinite(info.time.completed)\n ? info.time.completed\n : null;\n const completedDuration = createdAt !== null && completedAt !== null\n ? completedAt - createdAt\n : null;\n const durationMs = completedDuration !== null && completedDuration >= 0\n ? completedDuration\n : Math.max(0, finishedAt - startedAt);\n\n return {\n durationMs,\n tokens: extractTokenMetrics(info),\n };\n}\n\nfunction extractTokenMetrics(info: OpenCodeAssistantMessage | null | undefined): PromptSessionTokenMetrics {\n if (!info?.tokens) {\n return {\n total: null,\n input: null,\n output: null,\n reasoning: null,\n cacheRead: null,\n cacheWrite: null,\n };\n }\n\n const input = coerceFiniteNumber(info.tokens.input);\n const output = coerceFiniteNumber(info.tokens.output);\n const reasoning = coerceFiniteNumber(info.tokens.reasoning);\n const cacheRead = coerceFiniteNumber(info.tokens.cache?.read);\n const cacheWrite = coerceFiniteNumber(info.tokens.cache?.write);\n const total = coerceFiniteNumber(info.tokens.total);\n const fallbackTotal = sumTokenMetrics(input, output, cacheRead, cacheWrite);\n\n return {\n total: total ?? fallbackTotal,\n input,\n output,\n reasoning,\n cacheRead,\n cacheWrite,\n };\n}\n\nfunction coerceFiniteNumber(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\n\nfunction sumTokenMetrics(...values: Array<number | null>): number | null {\n if (values.every((value) => value === null)) {\n return null;\n }\n\n const total = values.reduce<number>((sum, value) => sum + (value ?? 0), 0);\n\n return Number.isFinite(total) ? total : null;\n}\n\nfunction unwrapSdkData<T>(response: MaybeWrappedResponse<T>): T {\n if (response && typeof response === \"object\" && \"data\" in response) {\n return response.data;\n }\n\n return response;\n}\n\nfunction resolvePromptTimeoutPolicy(\n input: Partial<OpenCodePromptTimeoutPolicy>,\n): OpenCodePromptTimeoutPolicy {\n return {\n pollRequestTimeoutMs: input.pollRequestTimeoutMs ?? DEFAULT_OPENCODE_PROMPT_TIMEOUT_POLICY.pollRequestTimeoutMs,\n recoveryInactivityTimeoutMs: input.recoveryInactivityTimeoutMs ?? DEFAULT_OPENCODE_PROMPT_TIMEOUT_POLICY.recoveryInactivityTimeoutMs,\n waitTimeoutMs: input.waitTimeoutMs ?? DEFAULT_OPENCODE_PROMPT_TIMEOUT_POLICY.waitTimeoutMs,\n };\n}\n\nfunction detectSdkFlavor(client: OpenCodeSdkClientLike): SdkFlavor {\n if (typeof (client as {\n global?: {\n health?: unknown;\n };\n permission?: unknown;\n }).global?.health === \"function\" || \"permission\" in client) {\n return \"v2\";\n }\n\n return clientExposesTwoArgumentSdkMethod(client)\n ? \"v2\"\n : \"legacy\";\n}\n\nfunction buildRawSessionRequest(\n sdkFlavor: SdkFlavor,\n sessionId: string,\n suffix = \"\",\n extra: Omit<RawSdkRequestOptions, \"path\" | \"responseStyle\" | \"throwOnError\" | \"url\"> = {},\n): RawSdkRequestOptions {\n return {\n url: sdkFlavor === \"legacy\"\n ? `/session/{id}${suffix}`\n : `/session/{sessionID}${suffix}`,\n path: sdkFlavor === \"legacy\"\n ? { id: sessionId }\n : { sessionID: sessionId },\n ...extra,\n };\n}\n\nfunction buildRawSessionMessageRequest(\n sdkFlavor: SdkFlavor,\n sessionId: string,\n messageId: string,\n extra: Omit<RawSdkRequestOptions, \"path\" | \"responseStyle\" | \"throwOnError\" | \"url\"> = {},\n): RawSdkRequestOptions {\n return {\n url: sdkFlavor === \"legacy\"\n ? \"/session/{id}/message/{messageID}\"\n : \"/session/{sessionID}/message/{messageID}\",\n path: sdkFlavor === \"legacy\"\n ? {\n id: sessionId,\n messageID: messageId,\n }\n : {\n sessionID: sessionId,\n messageID: messageId,\n },\n ...extra,\n };\n}\n\nfunction buildRawLegacyPermissionReplyRequest(\n sessionId: string,\n requestId: string,\n reply: \"once\" | \"always\" | \"reject\",\n): RawSdkRequestOptions {\n return {\n body: {\n response: reply,\n },\n path: {\n id: sessionId,\n permissionID: requestId,\n },\n url: \"/session/{id}/permissions/{permissionID}\",\n };\n}\n\nfunction getRawSdkClient(client: OpenCodeSdkClientLike): RawSdkHttpClient | null {\n const withRawClient = client as OpenCodeSdkClientLike & {\n client?: RawSdkHttpClient;\n };\n\n return withRawClient.client ?? client._client ?? null;\n}\n\nfunction hasRawSdkMethod(\n client: OpenCodeSdkClientLike,\n method: keyof Pick<RawSdkHttpClient, \"delete\" | \"get\" | \"patch\" | \"post\">,\n): boolean {\n const rawClient = getRawSdkClient(client);\n\n return typeof rawClient?.[method] === \"function\";\n}\n\nfunction normalizeAssistantError(value: unknown): OpenCodeAssistantError | undefined {\n if (!isPlainRecord(value) || typeof value.name !== \"string\" || value.name.trim().length === 0) {\n return undefined;\n }\n\n return {\n ...value,\n name: value.name,\n ...(isPlainRecord(value.data)\n ? { data: value.data }\n : {}),\n } as OpenCodeAssistantError;\n}\n\nfunction isAssistantMessageCompleted(\n message: OpenCodeAssistantMessage | null | undefined,\n): boolean {\n return !!message?.error\n || typeof message?.time?.completed === \"number\"\n || (typeof message?.finish === \"string\" && message.finish.trim().length > 0);\n}\n\nfunction isCompletedEmptyPromptResponse(\n data: OpenCodePromptResponse,\n structured: boolean,\n): boolean {\n const assistantInfo = toAssistantMessage(data.info);\n const bodyMd = structured ? extractStructuredMarkdown(extractStructuredPayload(assistantInfo)) : null;\n const responseParts = Array.isArray(data.parts) ? data.parts : [];\n const hasText = extractTextFromParts(responseParts).length > 0;\n\n return isAssistantMessageCompleted(assistantInfo)\n && !assistantInfo?.error\n && !hasText\n && !bodyMd;\n}\n\nfunction clientExposesTwoArgumentSdkMethod(client: OpenCodeSdkClientLike): boolean {\n const scopes = [\"app\", \"config\", \"global\", \"lsp\", \"mcp\", \"path\", \"permission\", \"project\", \"session\"] as const;\n\n return scopes.some((scope) => {\n const target = client[scope];\n\n if (!target || typeof target !== \"object\") {\n return false;\n }\n\n return Object.values(target).some((value) => typeof value === \"function\" && value.length >= 2);\n });\n}\n\nfunction extractStructuredPayload(message: unknown): unknown | null {\n if (!isPlainRecord(message)) {\n return null;\n }\n\n if (\"structured\" in message && message.structured !== undefined) {\n return message.structured;\n }\n\n if (\"structured_output\" in message && message.structured_output !== undefined) {\n return message.structured_output;\n }\n\n return null;\n}\n\ntype PromptResponseCandidateOptions = {\n initialMessageId: string | null;\n initialParentId: string | null;\n knownMessageIds: Set<string>;\n requestStartedAt: number | null;\n structured: boolean;\n};\n\nfunction selectPromptResponseCandidate(\n candidates: Array<OpenCodePromptResponse | null | undefined>,\n options: PromptResponseCandidateOptions,\n): OpenCodePromptResponse | null {\n const availableCandidates = candidates\n .filter((candidate): candidate is OpenCodePromptResponse => !!candidate)\n .filter((candidate) => toAssistantMessage(candidate.info) !== null);\n\n if (availableCandidates.length === 0) {\n return null;\n }\n\n return [...availableCandidates].sort((left, right) =>\n comparePromptResponseCandidates(left, right, options)\n )[0] ?? null;\n}\n\nfunction comparePromptResponseCandidates(\n left: OpenCodePromptResponse,\n right: OpenCodePromptResponse,\n options: PromptResponseCandidateOptions,\n): number {\n const leftRank = getPromptResponseCandidateRank(left, options);\n const rightRank = getPromptResponseCandidateRank(right, options);\n\n return Number(rightRank.isUsable) - Number(leftRank.isUsable)\n || Number(rightRank.isInitial) - Number(leftRank.isInitial)\n || Number(rightRank.sharesParent) - Number(leftRank.sharesParent)\n || Number(rightRank.isNewSinceRequestStart) - Number(leftRank.isNewSinceRequestStart)\n || rightRank.createdAt - leftRank.createdAt;\n}\n\nfunction getPromptResponseCandidateRank(\n message: OpenCodePromptResponse,\n options: PromptResponseCandidateOptions,\n): {\n createdAt: number;\n isInitial: boolean;\n isNewSinceRequestStart: boolean;\n isUsable: boolean;\n sharesParent: boolean;\n} {\n const assistant = toAssistantMessage(message.info);\n const id = assistant?.id ?? null;\n const createdAt = typeof assistant?.time?.created === \"number\" && Number.isFinite(assistant.time.created)\n ? assistant.time.created\n : 0;\n\n return {\n createdAt,\n isInitial: !!id && id === options.initialMessageId,\n isNewSinceRequestStart: isPromptResponseNewSinceRequestStart(\n id,\n createdAt,\n options.knownMessageIds,\n options.requestStartedAt,\n ),\n isUsable: isPromptResponseUsable(message, options.structured),\n sharesParent: !!assistant?.parentID && assistant.parentID === options.initialParentId,\n };\n}\n\nfunction resolvePromptCandidateStartTime(\n startedAt: number,\n initialMessage: OpenCodePromptResponse | null,\n): number | null {\n if (!initialMessage) {\n return null;\n }\n\n const initialCreatedAt = coerceFiniteNumber(toAssistantMessage(initialMessage.info)?.time?.created);\n\n if (initialCreatedAt === null) {\n return startedAt;\n }\n\n return areComparablePromptTimestamps(startedAt, initialCreatedAt)\n ? startedAt\n : initialCreatedAt;\n}\n\nfunction getPromptResponseProgressSignature(\n response: OpenCodePromptResponse | null,\n structured: boolean,\n): string {\n if (!response) {\n return \"null\";\n }\n\n const assistant = toAssistantMessage(response.info);\n const responseParts = Array.isArray(response.parts) ? response.parts : [];\n\n return JSON.stringify({\n assistantError: assistant?.error?.name ?? null,\n bodyMd: structured ? extractStructuredMarkdown(extractStructuredPayload(assistant)) : null,\n completedAt: assistant?.time?.completed ?? null,\n finish: assistant?.finish ?? null,\n id: assistant?.id ?? null,\n partCount: responseParts.length,\n text: extractTextFromParts(responseParts),\n });\n}\n\nfunction isPromptResponseNewSinceRequestStart(\n messageId: string | null,\n createdAt: number,\n knownMessageIds: Set<string>,\n requestStartedAt: number | null,\n): boolean {\n if (!messageId || knownMessageIds.has(messageId)) {\n return false;\n }\n\n if (requestStartedAt === null) {\n return true;\n }\n\n return createdAt >= requestStartedAt;\n}\n\nfunction areComparablePromptTimestamps(left: number, right: number): boolean {\n const epochThresholdMs = 1_000_000_000_000;\n\n return left >= epochThresholdMs && right >= epochThresholdMs;\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, any> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nasync function resolveProviderAvailability(\n config: OpenCodeConfig,\n fetchFn: typeof fetch,\n): Promise<OpenCodeProviderAvailability> {\n const configuredProviders = Object.entries(config.provider ?? {});\n const availabilityEntries = await Promise.all(\n configuredProviders.map(async ([providerId, providerConfig]) => [\n providerId,\n await fetchProviderAvailableModelIds(providerConfig, fetchFn),\n ] as const),\n );\n\n return new Map(availabilityEntries);\n}\n\nasync function fetchProviderAvailableModelIds(\n providerConfig: OpenCodeProviderConfig,\n fetchFn: typeof fetch,\n): Promise<Set<string> | null> {\n const baseUrl = extractConfiguredBaseUrl(providerConfig);\n const apiKey = extractConfiguredApiKey(providerConfig);\n\n if (!baseUrl || !apiKey) {\n return null;\n }\n\n try {\n const response = await fetchFn(\n buildModelsEndpointUrl(baseUrl),\n {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n Accept: \"application/json\",\n },\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (!response.ok) {\n return null;\n }\n\n const payload = await response.json();\n const modelIds = extractModelIdsFromPayload(payload);\n\n return modelIds ? new Set(modelIds) : null;\n } catch {\n return null;\n }\n}\n\nfunction extractConfiguredApiKey(providerConfig: OpenCodeProviderConfig): string | null {\n const options = providerConfig.options;\n\n if (!options || typeof options !== \"object\") {\n return null;\n }\n\n const apiKey = \"apiKey\" in options ? options.apiKey : null;\n\n return typeof apiKey === \"string\" && apiKey.trim().length > 0\n ? apiKey.trim()\n : null;\n}\n\nfunction buildModelsEndpointUrl(baseUrl: string): string {\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`;\n\n return new URL(\"models\", normalizedBaseUrl).toString();\n}\n\nfunction extractModelIdsFromPayload(payload: unknown): string[] | null {\n if (Array.isArray(payload)) {\n return payload\n .map(extractModelId)\n .filter((id): id is string => !!id);\n }\n\n if (payload && typeof payload === \"object\" && \"data\" in payload && Array.isArray(payload.data)) {\n return payload.data\n .map(extractModelId)\n .filter((id): id is string => !!id);\n }\n\n return null;\n}\n\nfunction extractModelId(value: unknown): string | null {\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value.trim();\n }\n\n if (value && typeof value === \"object\" && \"id\" in value) {\n const id = (value as { id?: unknown }).id;\n\n return typeof id === \"string\" && id.trim().length > 0\n ? id.trim()\n : null;\n }\n\n return null;\n}\n","import type { PendingChatAction } from \"../../domain/pending-action/pending-action.entity.js\";\nimport type { PendingPermissionApproval } from \"../../domain/permission/approval.entity.js\";\nimport type { ChatSessionBinding } from \"../../domain/session/session.entity.js\";\n\nexport interface OpencodeTbotState {\n version: 1;\n sessions: Record<string, ChatSessionBinding>;\n pendingActions: Record<string, PendingChatAction>;\n pendingPermissions: Record<string, PendingPermissionApproval>;\n}\n\nexport function createDefaultOpencodeTbotState(): OpencodeTbotState {\n return {\n version: 1,\n sessions: {},\n pendingActions: {},\n pendingPermissions: {},\n };\n}\n","import { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\n\nexport interface JsonStateStoreOptions<TState> {\n createDefaultState: () => TState;\n filePath: string;\n}\n\nexport class JsonStateStore<TState> {\n private readonly createDefaultState: () => TState;\n private readonly filePath: string;\n private statePromise: Promise<TState> | null = null;\n private writeQueue = Promise.resolve();\n\n constructor(options: JsonStateStoreOptions<TState>) {\n this.createDefaultState = options.createDefaultState;\n this.filePath = options.filePath;\n }\n\n async read(): Promise<TState> {\n const state = await this.loadState();\n\n return cloneState(state);\n }\n\n async update(mutator: (state: TState) => void): Promise<TState> {\n const nextStatePromise = this.writeQueue.then(async () => {\n const state = await this.loadState();\n const draft = cloneState(state);\n\n mutator(draft);\n await writeStateFile(this.filePath, draft);\n this.statePromise = Promise.resolve(draft);\n\n return cloneState(draft);\n });\n\n this.writeQueue = nextStatePromise.then(() => undefined, () => undefined);\n\n return nextStatePromise;\n }\n\n private async loadState(): Promise<TState> {\n if (!this.statePromise) {\n this.statePromise = readStateFile(this.filePath, this.createDefaultState);\n }\n\n return this.statePromise;\n }\n}\n\nasync function readStateFile<TState>(\n filePath: string,\n createDefaultState: () => TState,\n): Promise<TState> {\n try {\n const content = await readFile(filePath, \"utf8\");\n\n return JSON.parse(content) as TState;\n } catch (error) {\n if (isMissingFileError(error)) {\n return createDefaultState();\n }\n\n throw error;\n }\n}\n\nasync function writeStateFile<TState>(filePath: string, state: TState): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n\n const temporaryFilePath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;\n const serialized = `${JSON.stringify(state, null, 2)}\\n`;\n\n await writeFile(temporaryFilePath, serialized, \"utf8\");\n await rename(temporaryFilePath, filePath);\n}\n\nfunction cloneState<TState>(state: TState): TState {\n return JSON.parse(JSON.stringify(state)) as TState;\n}\n\nfunction isMissingFileError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n}\n","export interface DownloadTelegramFileInput {\n filePath: string;\n}\n\nexport interface DownloadTelegramFileResult {\n data: Uint8Array;\n mimeType: string | null;\n}\n\nexport interface TelegramFileClientLike {\n downloadFile(input: DownloadTelegramFileInput): Promise<DownloadTelegramFileResult>;\n}\n\nexport interface TelegramFileClientOptions {\n baseUrl?: string;\n apiRoot?: string;\n botToken: string;\n}\n\nexport class TelegramFileDownloadError extends Error {\n readonly data: { message: string };\n\n constructor(message: string) {\n super(message);\n this.name = \"TelegramFileDownloadError\";\n this.data = { message };\n }\n}\n\nexport class TelegramFileClient implements TelegramFileClientLike {\n private readonly baseUrl: string;\n private readonly fetchFn: typeof fetch;\n\n constructor(options: TelegramFileClientOptions, fetchFn: typeof fetch = fetch) {\n this.baseUrl = options.baseUrl ?? buildTelegramFileApiRoot(options.apiRoot, options.botToken);\n this.fetchFn = fetchFn;\n }\n\n async downloadFile(input: DownloadTelegramFileInput): Promise<DownloadTelegramFileResult> {\n const filePath = input.filePath.trim();\n\n if (!filePath) {\n throw new TelegramFileDownloadError(\"Telegram did not provide a downloadable file path.\");\n }\n\n let response: Response;\n\n try {\n response = await this.fetchFn(new URL(filePath, this.baseUrl));\n } catch (error) {\n throw new TelegramFileDownloadError(extractErrorMessage(error) ?? \"Failed to download the Telegram file.\");\n }\n\n if (!response.ok) {\n throw new TelegramFileDownloadError(await buildDownloadFailureMessage(response));\n }\n\n const data = new Uint8Array(await response.arrayBuffer());\n\n if (data.byteLength === 0) {\n throw new TelegramFileDownloadError(\"Telegram returned an empty file.\");\n }\n\n return {\n data,\n mimeType: response.headers.get(\"content-type\"),\n };\n }\n}\n\nexport function buildTelegramFileApiRoot(apiRoot: string | undefined, botToken: string): string {\n const normalizedApiRoot = (apiRoot ?? \"https://api.telegram.org\").trim().replace(/\\/+$/u, \"\");\n\n return `${normalizedApiRoot}/file/bot${botToken}/`;\n}\n\nasync function buildDownloadFailureMessage(response: Response): Promise<string> {\n const responseText = await safeReadResponseText(response);\n const normalizedText = responseText?.trim();\n\n return normalizedText\n ? `Telegram file download failed with status ${response.status}: ${normalizedText}`\n : `Telegram file download failed with status ${response.status}.`;\n}\n\nasync function safeReadResponseText(response: Response): Promise<string | null> {\n try {\n const text = await response.text();\n\n return text.trim().length > 0 ? text : null;\n } catch {\n return null;\n }\n}\n\nfunction extractErrorMessage(error: unknown): string | null {\n return error instanceof Error && error.message.trim().length > 0\n ? error.message.trim()\n : null;\n}\n","export interface ForegroundSessionTrackerLike {\n begin(chatId: number, sessionId: string): () => void;\n clear(sessionId: string): boolean;\n getActiveSessionId(chatId: number): string | null;\n isForeground(sessionId: string): boolean;\n listChatIds(sessionId: string): number[];\n}\n\nexport class ForegroundSessionTracker implements ForegroundSessionTrackerLike {\n private readonly chatStacks = new Map<number, string[]>();\n private readonly counts = new Map<string, number>();\n private readonly sessionChats = new Map<string, Map<number, number>>();\n\n begin(chatId: number, sessionId: string): () => void {\n const currentCount = this.counts.get(sessionId) ?? 0;\n\n this.counts.set(sessionId, currentCount + 1);\n this.incrementChat(chatId, sessionId);\n\n return () => {\n this.decrement(chatId, sessionId);\n };\n }\n\n clear(sessionId: string): boolean {\n const wasForeground = this.counts.has(sessionId);\n const chatCounts = this.sessionChats.get(sessionId);\n\n this.counts.delete(sessionId);\n this.sessionChats.delete(sessionId);\n\n for (const chatId of chatCounts?.keys() ?? []) {\n const stack = this.chatStacks.get(chatId);\n\n if (!stack) {\n continue;\n }\n\n const nextStack = stack.filter((trackedSessionId) => trackedSessionId !== sessionId);\n\n if (nextStack.length === 0) {\n this.chatStacks.delete(chatId);\n continue;\n }\n\n this.chatStacks.set(chatId, nextStack);\n }\n\n return wasForeground;\n }\n\n getActiveSessionId(chatId: number): string | null {\n const stack = this.chatStacks.get(chatId);\n\n return stack?.at(-1) ?? null;\n }\n\n isForeground(sessionId: string): boolean {\n return this.counts.has(sessionId);\n }\n\n listChatIds(sessionId: string): number[] {\n return [...(this.sessionChats.get(sessionId)?.keys() ?? [])];\n }\n\n private decrement(chatId: number, sessionId: string) {\n const currentCount = this.counts.get(sessionId);\n\n if (!currentCount || currentCount <= 1) {\n this.counts.delete(sessionId);\n } else {\n this.counts.set(sessionId, currentCount - 1);\n }\n\n this.decrementChat(chatId, sessionId);\n }\n\n private incrementChat(chatId: number, sessionId: string) {\n const stack = this.chatStacks.get(chatId) ?? [];\n\n stack.push(sessionId);\n this.chatStacks.set(chatId, stack);\n\n const chatCounts = this.sessionChats.get(sessionId) ?? new Map<number, number>();\n const currentCount = chatCounts.get(chatId) ?? 0;\n\n chatCounts.set(chatId, currentCount + 1);\n this.sessionChats.set(sessionId, chatCounts);\n }\n\n private decrementChat(chatId: number, sessionId: string) {\n const stack = this.chatStacks.get(chatId);\n\n if (stack) {\n const index = stack.lastIndexOf(sessionId);\n\n if (index >= 0) {\n stack.splice(index, 1);\n }\n\n if (stack.length === 0) {\n this.chatStacks.delete(chatId);\n } else {\n this.chatStacks.set(chatId, stack);\n }\n }\n\n const chatCounts = this.sessionChats.get(sessionId);\n\n if (!chatCounts) {\n return;\n }\n\n const currentCount = chatCounts.get(chatId) ?? 0;\n\n if (currentCount <= 1) {\n chatCounts.delete(chatId);\n } else {\n chatCounts.set(chatId, currentCount - 1);\n }\n\n if (chatCounts.size === 0) {\n this.sessionChats.delete(sessionId);\n } else {\n this.sessionChats.set(sessionId, chatCounts);\n }\n }\n}\n\nexport const NOOP_FOREGROUND_SESSION_TRACKER: ForegroundSessionTrackerLike = {\n begin() {\n return () => undefined;\n },\n clear() {\n return false;\n },\n getActiveSessionId() {\n return null;\n },\n isForeground() {\n return false;\n },\n listChatIds() {\n return [];\n },\n};\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { OpenCodeClientLike, OpenCodeSessionStatus } from \"../services/opencode/opencode.client.js\";\nimport {\n NOOP_FOREGROUND_SESSION_TRACKER,\n type ForegroundSessionTrackerLike,\n} from \"../services/session-activity/foreground-session-tracker.js\";\n\nexport interface AbortPromptResult {\n sessionId: string | null;\n status: \"aborted\" | \"no_session\" | \"not_running\";\n sessionStatus: OpenCodeSessionStatus | null;\n}\n\nexport class AbortPromptUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n private readonly foregroundSessionTracker: ForegroundSessionTrackerLike = NOOP_FOREGROUND_SESSION_TRACKER,\n ) { }\n\n async execute(input: { chatId: number }): Promise<AbortPromptResult> {\n const activeSessionId = this.foregroundSessionTracker.getActiveSessionId(input.chatId);\n const binding = activeSessionId\n ? null\n : await this.sessionRepo.getByChatId(input.chatId);\n\n const sessionId = activeSessionId ?? binding?.sessionId ?? null;\n\n if (!sessionId) {\n return {\n sessionId: null,\n status: \"no_session\",\n sessionStatus: null,\n };\n }\n\n const sessionStatus = (await this.opencodeClient.getSessionStatuses())[sessionId] ?? null;\n\n if (!sessionStatus || sessionStatus.type === \"idle\") {\n return {\n sessionId,\n status: \"not_running\",\n sessionStatus,\n };\n }\n\n const aborted = await this.opencodeClient.abortSession(sessionId);\n\n return {\n sessionId,\n status: aborted ? \"aborted\" : \"not_running\",\n sessionStatus,\n };\n }\n}\n","import type { ChatSessionBinding } from \"../domain/session/session.entity.js\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type {\n OpenCodeClientLike,\n OpenCodeProject,\n OpenCodeSession,\n} from \"../services/opencode/opencode.client.js\";\n\ninterface CreateAndBindCurrentProjectSessionInput {\n chatId: number;\n title?: string | null;\n binding?: ChatSessionBinding | null;\n}\n\nexport interface CreateAndBindCurrentProjectSessionResult {\n binding: ChatSessionBinding;\n project: OpenCodeProject;\n session: OpenCodeSession;\n}\n\nexport async function createAndBindCurrentProjectSession(\n sessionRepo: SessionRepository,\n opencodeClient: OpenCodeClientLike,\n input: CreateAndBindCurrentProjectSessionInput,\n): Promise<CreateAndBindCurrentProjectSessionResult> {\n const binding = input.binding ?? await sessionRepo.getByChatId(input.chatId);\n const project = await opencodeClient.getCurrentProject();\n const title = normalizeOptionalText(input.title);\n const session = await opencodeClient.createSessionForDirectory(project.worktree, title ?? undefined);\n const nextBinding: ChatSessionBinding = {\n chatId: input.chatId,\n sessionId: session.id,\n projectId: session.projectID,\n directory: session.directory,\n agentName: binding?.agentName ?? null,\n modelProviderId: binding?.modelProviderId ?? null,\n modelId: binding?.modelId ?? null,\n modelVariant: binding?.modelVariant ?? null,\n language: binding?.language ?? null,\n updatedAt: new Date().toISOString(),\n };\n\n await sessionRepo.setCurrent(nextBinding);\n\n return {\n binding: nextBinding,\n project,\n session,\n };\n}\n\nfunction normalizeOptionalText(value: string | null | undefined): string | null {\n const normalized = value?.trim();\n\n return normalized ? normalized : null;\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { AppLogger } from \"../infra/logger/index.js\";\nimport type {\n OpenCodeClientLike,\n OpenCodeSession,\n} from \"../services/opencode/opencode.client.js\";\nimport { createAndBindCurrentProjectSession } from \"./session-creation.js\";\n\nexport interface CreateSessionResult {\n session: OpenCodeSession;\n}\n\nexport class CreateSessionUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n private readonly logger: AppLogger,\n ) { }\n\n async execute(input: { chatId: number; title?: string | null }): Promise<CreateSessionResult> {\n const binding = await this.sessionRepo.getByChatId(input.chatId);\n const result = await createAndBindCurrentProjectSession(\n this.sessionRepo,\n this.opencodeClient,\n {\n chatId: input.chatId,\n title: input.title,\n binding,\n },\n );\n\n this.logger.info(\n {\n chatId: input.chatId,\n sessionId: result.session.id,\n projectId: result.session.projectID,\n directory: result.session.directory,\n title: input.title?.trim() || null,\n },\n \"session created\",\n );\n\n return {\n session: result.session,\n };\n }\n}\n","import type {\n OpenCodeClientLike,\n OpenCodeHealth,\n} from \"../services/opencode/opencode.client.js\";\n\nexport class GetHealthUseCase {\n constructor(private readonly opencodeClient: OpenCodeClientLike) { }\n\n async execute(): Promise<OpenCodeHealth> {\n return this.opencodeClient.getHealth();\n }\n}\n","import type { OpenCodeClientLike, OpenCodePath } from \"../services/opencode/opencode.client.js\";\n\nexport class GetPathUseCase {\n constructor(private readonly opencodeClient: OpenCodeClientLike) { }\n\n async execute(): Promise<OpenCodePath> {\n return this.opencodeClient.getPath();\n }\n}\n","import { readFile, stat } from \"node:fs/promises\";\nimport { isAbsolute, join } from \"node:path\";\nimport { parse, printParseErrorCode, type ParseError } from \"jsonc-parser\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type {\n OpenCodeHealth,\n OpenCodePath,\n OpenCodeSession,\n} from \"../services/opencode/opencode.client.js\";\nimport type { GetHealthUseCase } from \"./get-health.usecase.js\";\nimport type { GetPathUseCase } from \"./get-path.usecase.js\";\nimport type { ListLspResult, ListLspUseCase } from \"./list-lsp.usecase.js\";\nimport type { ListMcpResult, ListMcpUseCase } from \"./list-mcp.usecase.js\";\nimport type { ListSessionsUseCase } from \"./list-sessions.usecase.js\";\n\nexport interface StatusSectionOk<TData> {\n data: TData;\n status: \"ok\";\n}\n\nexport interface StatusSectionError {\n error: unknown;\n status: \"error\";\n}\n\nexport type StatusSectionResult<TData> = StatusSectionOk<TData> | StatusSectionError;\n\nexport interface ConfiguredPluginsResult {\n configFilePath: string;\n plugins: string[];\n}\n\nexport interface WorkspaceStatusResult {\n currentProject: string;\n currentSession: string | null;\n}\n\nexport interface GetStatusResult {\n health: StatusSectionResult<OpenCodeHealth>;\n lsp: StatusSectionResult<ListLspResult>;\n mcp: StatusSectionResult<ListMcpResult>;\n path: StatusSectionResult<OpenCodePath>;\n plugins: StatusSectionResult<ConfiguredPluginsResult>;\n workspace: StatusSectionResult<WorkspaceStatusResult>;\n}\n\nexport class GetStatusUseCase {\n constructor(\n private readonly getHealthUseCase: GetHealthUseCase,\n private readonly getPathUseCase: GetPathUseCase,\n private readonly listLspUseCase: ListLspUseCase,\n private readonly listMcpUseCase: ListMcpUseCase,\n private readonly listSessionsUseCase: ListSessionsUseCase,\n private readonly sessionRepo: SessionRepository,\n ) { }\n\n async execute(input: { chatId: number }): Promise<GetStatusResult> {\n const [health, path, lsp, mcp] = await Promise.allSettled([\n this.getHealthUseCase.execute(),\n this.getPathUseCase.execute(),\n this.listLspUseCase.execute({ chatId: input.chatId }),\n this.listMcpUseCase.execute({ chatId: input.chatId }),\n ]);\n const pathResult = mapSettledResult(path);\n const [plugins, workspace] = await Promise.all([\n loadConfiguredPluginsResult(pathResult),\n loadWorkspaceStatusResult(\n input.chatId,\n pathResult,\n this.listSessionsUseCase,\n this.sessionRepo,\n ),\n ]);\n\n return {\n health: mapSettledResult(health),\n path: pathResult,\n plugins,\n workspace,\n lsp: mapSettledResult(lsp),\n mcp: mapSettledResult(mcp),\n };\n }\n}\n\nfunction mapSettledResult<TData>(result: PromiseSettledResult<TData>): StatusSectionResult<TData> {\n if (result.status === \"fulfilled\") {\n return {\n data: result.value,\n status: \"ok\",\n };\n }\n\n return {\n error: result.reason,\n status: \"error\",\n };\n}\n\nasync function loadConfiguredPluginsResult(\n path: StatusSectionResult<OpenCodePath>,\n): Promise<StatusSectionResult<ConfiguredPluginsResult>> {\n if (path.status === \"error\") {\n return {\n error: path.error,\n status: \"error\",\n };\n }\n\n try {\n return {\n data: await loadConfiguredPlugins(path.data.config),\n status: \"ok\",\n };\n } catch (error) {\n return {\n error,\n status: \"error\",\n };\n }\n}\n\nasync function loadConfiguredPlugins(configFilePath: string): Promise<ConfiguredPluginsResult> {\n const resolvedConfigFilePath = await resolveOpenCodeConfigFilePath(configFilePath);\n let content: string;\n\n try {\n content = await readFile(resolvedConfigFilePath, \"utf8\");\n } catch (error) {\n if (isMissingFileError(error)) {\n return {\n configFilePath: resolvedConfigFilePath,\n plugins: [],\n };\n }\n\n throw error;\n }\n\n const parseErrors: ParseError[] = [];\n const parsed = parse(content, parseErrors, {\n allowTrailingComma: true,\n });\n\n if (parseErrors.length > 0) {\n const errorSummary = parseErrors\n .map((error) => printParseErrorCode(error.error))\n .join(\", \");\n\n throw new Error(`Failed to parse ${resolvedConfigFilePath}: ${errorSummary}`);\n }\n\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return {\n configFilePath: resolvedConfigFilePath,\n plugins: [],\n };\n }\n\n const pluginSpecs = Array.isArray((parsed as { plugin?: unknown }).plugin)\n ? (parsed as { plugin: unknown[] }).plugin\n : [];\n const plugins = [...new Set(\n pluginSpecs\n .filter((value): value is string => typeof value === \"string\")\n .map((value) => value.trim())\n .filter((value) => value.length > 0),\n )];\n\n return {\n configFilePath: resolvedConfigFilePath,\n plugins,\n };\n}\n\nasync function loadWorkspaceStatusResult(\n chatId: number,\n path: StatusSectionResult<OpenCodePath>,\n listSessionsUseCase: ListSessionsUseCase,\n sessionRepo: SessionRepository,\n): Promise<StatusSectionResult<WorkspaceStatusResult>> {\n if (path.status === \"error\") {\n return {\n error: path.error,\n status: \"error\",\n };\n }\n\n const binding = await sessionRepo.getByChatId(chatId);\n let currentProject = resolveWorkspaceProjectPath(path.data);\n let currentSession = binding?.sessionId ?? null;\n\n try {\n const sessions = await listSessionsUseCase.execute({ chatId });\n\n currentProject = resolveWorkspaceProjectPath(path.data, sessions.currentDirectory);\n currentSession = sessions.currentSessionId\n ? formatSessionStatusLabel(\n sessions.sessions.find((session) => session.id === sessions.currentSessionId) ?? null,\n sessions.currentSessionId,\n )\n : null;\n } catch {\n // Keep path/session binding fallbacks when session listing is unavailable.\n }\n\n return {\n data: {\n currentProject,\n currentSession,\n },\n status: \"ok\",\n };\n}\n\nfunction formatSessionStatusLabel(session: OpenCodeSession | null, fallbackId: string): string {\n if (!session) {\n return fallbackId;\n }\n\n const title = session.title.trim() || session.slug || session.id;\n\n return title === session.slug\n ? title\n : `${title} (${session.slug})`;\n}\n\nfunction resolveWorkspaceProjectPath(path: OpenCodePath, preferredPath?: string | null): string {\n const candidates = [\n preferredPath,\n path.worktree,\n path.directory,\n ];\n\n for (const candidate of candidates) {\n if (isUsableWorkspacePath(candidate)) {\n return candidate;\n }\n }\n\n return preferredPath ?? path.worktree ?? path.directory;\n}\n\nfunction isUsableWorkspacePath(value: string | null | undefined): value is string {\n if (typeof value !== \"string\") {\n return false;\n }\n\n const normalized = value.trim();\n\n return normalized.length > 0\n && normalized !== \"/\"\n && normalized !== \"\\\\\"\n && isAbsolute(normalized);\n}\n\nasync function resolveOpenCodeConfigFilePath(configPath: string): Promise<string> {\n try {\n const info = await stat(configPath);\n\n return info.isDirectory()\n ? join(configPath, \"opencode.json\")\n : configPath;\n } catch (error) {\n if (isMissingFileError(error)) {\n return configPath;\n }\n\n throw error;\n }\n}\n\nfunction isMissingFileError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n}\n","import type { OpenCodeAgent } from \"../services/opencode/opencode.client.js\";\n\nexport const DEFAULT_AGENT_NAME = \"build\";\n\nexport function isSelectableAgent(agent: OpenCodeAgent): boolean {\n return !agent.hidden && agent.mode !== \"subagent\";\n}\n\nexport function getSelectableAgents(agents: OpenCodeAgent[]): OpenCodeAgent[] {\n return agents.filter((agent) => isSelectableAgent(agent));\n}\n\nexport function resolveSelectedAgent(\n agents: OpenCodeAgent[],\n requestedAgentName: string | null | undefined,\n): OpenCodeAgent | null {\n const visibleAgents = getSelectableAgents(agents);\n\n if (requestedAgentName) {\n const requestedAgent = visibleAgents.find((agent) => agent.name === requestedAgentName);\n\n if (requestedAgent) {\n return requestedAgent;\n }\n }\n\n return visibleAgents.find((agent) => agent.name === DEFAULT_AGENT_NAME) ?? null;\n}\n","import type { ChatSessionBinding } from \"../domain/session/session.entity.js\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\n\ntype MutableBindingFields = Omit<ChatSessionBinding, \"chatId\" | \"updatedAt\">;\n\nexport async function persistBindingUpdate(\n sessionRepo: SessionRepository,\n binding: ChatSessionBinding,\n updates: Partial<MutableBindingFields>,\n): Promise<ChatSessionBinding> {\n const nextBinding: ChatSessionBinding = {\n ...binding,\n ...updates,\n updatedAt: new Date().toISOString(),\n };\n\n await sessionRepo.setCurrent(nextBinding);\n return nextBinding;\n}\n\nexport async function clearStoredAgentSelection(\n sessionRepo: SessionRepository,\n binding: ChatSessionBinding,\n): Promise<ChatSessionBinding> {\n return persistBindingUpdate(sessionRepo, binding, {\n agentName: null,\n });\n}\n\nexport async function clearStoredModelSelection(\n sessionRepo: SessionRepository,\n binding: ChatSessionBinding,\n): Promise<ChatSessionBinding> {\n return persistBindingUpdate(sessionRepo, binding, {\n modelProviderId: null,\n modelId: null,\n modelVariant: null,\n });\n}\n\nexport async function clearStoredModelVariant(\n sessionRepo: SessionRepository,\n binding: ChatSessionBinding,\n): Promise<ChatSessionBinding> {\n return persistBindingUpdate(sessionRepo, binding, {\n modelVariant: null,\n });\n}\n\nexport async function clearStoredSessionContext(\n sessionRepo: SessionRepository,\n binding: ChatSessionBinding,\n): Promise<ChatSessionBinding> {\n return persistBindingUpdate(sessionRepo, binding, {\n sessionId: null,\n projectId: null,\n directory: null,\n });\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type {\n OpenCodeAgent,\n OpenCodeClientLike,\n} from \"../services/opencode/opencode.client.js\";\nimport { getSelectableAgents, resolveSelectedAgent } from \"./agent-selection.js\";\nimport { clearStoredAgentSelection } from \"./binding-update.js\";\n\nexport interface ListAgentsResult {\n agents: OpenCodeAgent[];\n currentAgentName: string | null;\n}\n\nexport class ListAgentsUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n ) { }\n\n async execute(input: { chatId: number }): Promise<ListAgentsResult> {\n let [binding, agents] = await Promise.all([\n this.sessionRepo.getByChatId(input.chatId),\n this.opencodeClient.listAgents(),\n ]);\n const visibleAgents = getSelectableAgents(agents);\n const currentAgent = resolveSelectedAgent(visibleAgents, binding?.agentName);\n\n if (binding?.agentName && currentAgent?.name !== binding.agentName) {\n binding = await clearStoredAgentSelection(this.sessionRepo, binding);\n }\n\n return {\n agents: visibleAgents,\n currentAgentName: currentAgent?.name ?? null,\n };\n }\n}\n","import type { ChatSessionBinding } from \"../domain/session/session.entity.js\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type {\n OpenCodeClientLike,\n OpenCodeLspStatus,\n OpenCodeProject,\n} from \"../services/opencode/opencode.client.js\";\nimport { clearStoredSessionContext } from \"./binding-update.js\";\n\nexport interface ListLspResult {\n currentDirectory: string;\n statuses: OpenCodeLspStatus[];\n}\n\nexport class ListLspUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n ) { }\n\n async execute(input: { chatId: number }): Promise<ListLspResult> {\n let binding = await this.sessionRepo.getByChatId(input.chatId);\n\n if (binding?.projectId && binding.directory) {\n const projects = await this.opencodeClient.listProjects();\n\n if (!hasMatchingProject(projects, binding)) {\n binding = await clearStoredSessionContext(this.sessionRepo, binding);\n }\n }\n\n const projectContext = binding?.projectId && binding.directory\n ? {\n projectId: binding.projectId,\n directory: binding.directory,\n }\n : await this.getCurrentProjectContext();\n const statuses = await this.opencodeClient.listLspStatuses(projectContext.directory);\n\n return {\n currentDirectory: projectContext.directory,\n statuses: [...statuses].sort(compareLspStatuses),\n };\n }\n\n private async getCurrentProjectContext() {\n const currentProject = await this.opencodeClient.getCurrentProject();\n\n return {\n projectId: currentProject.id,\n directory: currentProject.worktree,\n };\n }\n}\n\nfunction compareLspStatuses(left: OpenCodeLspStatus, right: OpenCodeLspStatus): number {\n return left.name.localeCompare(right.name)\n || left.root.localeCompare(right.root)\n || left.id.localeCompare(right.id);\n}\n\nfunction hasMatchingProject(projects: OpenCodeProject[], binding: ChatSessionBinding): boolean {\n return projects.some((project) =>\n project.id === binding.projectId && project.worktree === binding.directory,\n );\n}\n","import type { ChatSessionBinding } from \"../domain/session/session.entity.js\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type {\n OpenCodeClientLike,\n OpenCodeMcpStatus,\n OpenCodeProject,\n} from \"../services/opencode/opencode.client.js\";\nimport { clearStoredSessionContext } from \"./binding-update.js\";\n\nexport interface ListMcpEntry {\n name: string;\n status: OpenCodeMcpStatus;\n}\n\nexport interface ListMcpResult {\n currentDirectory: string;\n statuses: ListMcpEntry[];\n}\n\nexport class ListMcpUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n ) { }\n\n async execute(input: { chatId: number }): Promise<ListMcpResult> {\n let binding = await this.sessionRepo.getByChatId(input.chatId);\n\n if (binding?.projectId && binding.directory) {\n const projects = await this.opencodeClient.listProjects();\n\n if (!hasMatchingProject(projects, binding)) {\n binding = await clearStoredSessionContext(this.sessionRepo, binding);\n }\n }\n\n const projectContext = binding?.projectId && binding.directory\n ? {\n projectId: binding.projectId,\n directory: binding.directory,\n }\n : await this.getCurrentProjectContext();\n const statuses = await this.opencodeClient.listMcpStatuses(projectContext.directory);\n\n return {\n currentDirectory: projectContext.directory,\n statuses: Object.entries(statuses)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([name, status]) => ({ name, status })),\n };\n }\n\n private async getCurrentProjectContext() {\n const currentProject = await this.opencodeClient.getCurrentProject();\n\n return {\n projectId: currentProject.id,\n directory: currentProject.worktree,\n };\n }\n}\n\nfunction hasMatchingProject(projects: OpenCodeProject[], binding: ChatSessionBinding): boolean {\n return projects.some((project) =>\n project.id === binding.projectId && project.worktree === binding.directory,\n );\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type {\n OpenCodeClientLike,\n OpenCodeConfiguredModel,\n} from \"../services/opencode/opencode.client.js\";\nimport {\n clearStoredModelSelection,\n clearStoredModelVariant,\n} from \"./binding-update.js\";\n\nexport interface ListModelsResult {\n models: OpenCodeConfiguredModel[];\n currentModelProviderId: string | null;\n currentModelId: string | null;\n currentModelVariant: string | null;\n}\n\nexport class ListModelsUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n ) { }\n\n async execute(input: { chatId: number }): Promise<ListModelsResult> {\n let [binding, models] = await Promise.all([\n this.sessionRepo.getByChatId(input.chatId),\n this.opencodeClient.listModels(),\n ]);\n\n if (binding?.modelProviderId && binding?.modelId) {\n const selectedModel = models.find((model) =>\n model.providerID === binding?.modelProviderId && model.id === binding?.modelId,\n );\n\n if (!selectedModel) {\n binding = await clearStoredModelSelection(this.sessionRepo, binding);\n } else if (binding.modelVariant && !(binding.modelVariant in selectedModel.variants)) {\n binding = await clearStoredModelVariant(this.sessionRepo, binding);\n }\n }\n\n return {\n models,\n currentModelProviderId: binding?.modelProviderId ?? null,\n currentModelId: binding?.modelId ?? null,\n currentModelVariant: binding?.modelVariant ?? null,\n };\n }\n}\n","import type { ChatSessionBinding } from \"../domain/session/session.entity.js\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type {\n OpenCodeClientLike,\n OpenCodeProject,\n OpenCodeSession,\n} from \"../services/opencode/opencode.client.js\";\nimport { clearStoredSessionContext } from \"./binding-update.js\";\n\nexport interface ListSessionsResult {\n sessions: OpenCodeSession[];\n currentProjectId: string;\n currentDirectory: string;\n currentSessionId: string | null;\n}\n\nexport class ListSessionsUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n ) { }\n\n async execute(input: { chatId: number }): Promise<ListSessionsResult> {\n let binding = await this.sessionRepo.getByChatId(input.chatId);\n const [sessions, projects] = await Promise.all([\n this.opencodeClient.listSessions(),\n binding?.projectId && binding.directory\n ? this.opencodeClient.listProjects()\n : Promise.resolve<OpenCodeProject[] | null>(null),\n ]);\n\n if (binding?.projectId && binding.directory && !hasMatchingProject(projects, binding)) {\n binding = await clearStoredSessionContext(this.sessionRepo, binding);\n }\n\n if (binding?.sessionId && binding.projectId && !hasMatchingSession(sessions, binding)) {\n binding = await clearStoredSessionContext(this.sessionRepo, binding);\n }\n\n const projectContext = binding?.projectId && binding.directory\n ? {\n projectId: binding.projectId,\n directory: binding.directory,\n }\n : await this.getCurrentProjectContext();\n\n return {\n sessions: sessions.filter((session) => session.projectID === projectContext.projectId),\n currentProjectId: projectContext.projectId,\n currentDirectory: projectContext.directory,\n currentSessionId: binding?.projectId === projectContext.projectId ? binding.sessionId : null,\n };\n }\n\n private async getCurrentProjectContext() {\n const currentProject = await this.opencodeClient.getCurrentProject();\n\n return {\n projectId: currentProject.id,\n directory: currentProject.worktree,\n };\n }\n}\n\nfunction hasMatchingProject(\n projects: OpenCodeProject[] | null,\n binding: ChatSessionBinding,\n): boolean {\n if (!projects) {\n return true;\n }\n\n return projects.some((project) =>\n project.id === binding.projectId && project.worktree === binding.directory,\n );\n}\n\nfunction hasMatchingSession(\n sessions: OpenCodeSession[],\n binding: ChatSessionBinding,\n): boolean {\n return sessions.some((session) =>\n session.id === binding.sessionId && session.projectID === binding.projectId,\n );\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { AppLogger } from \"../infra/logger/index.js\";\nimport type { OpenCodeClientLike, OpenCodeSession } from \"../services/opencode/opencode.client.js\";\n\nexport type RenameSessionResult =\n | {\n found: false;\n }\n | {\n found: true;\n session: OpenCodeSession;\n };\n\nexport class RenameSessionUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n private readonly logger: AppLogger,\n ) { }\n\n async execute(input: { chatId: number; sessionId: string; title: string }): Promise<RenameSessionResult> {\n const title = input.title.trim();\n\n if (!title) {\n throw new Error(\"Session title cannot be empty.\");\n }\n\n const [binding, sessions] = await Promise.all([\n this.sessionRepo.getByChatId(input.chatId),\n this.opencodeClient.listSessions(),\n ]);\n const currentProjectId = binding?.projectId ?? (await this.opencodeClient.getCurrentProject()).id;\n const session = sessions.find((item) => item.id === input.sessionId && item.projectID === currentProjectId);\n\n if (!session) {\n return { found: false };\n }\n\n const renamedSession = await this.opencodeClient.renameSession(session.id, title);\n\n this.logger.info(\n {\n chatId: input.chatId,\n sessionId: renamedSession.id,\n projectId: renamedSession.projectID,\n title: renamedSession.title,\n },\n \"session renamed\",\n );\n\n return {\n found: true,\n session: renamedSession,\n };\n }\n}\n","import type { ChatSessionBinding } from \"../domain/session/session.entity.js\";\nimport type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { AppLogger } from \"../infra/logger/index.js\";\nimport type {\n OpenCodeClientLike,\n PromptSessionFilePartInput,\n} from \"../services/opencode/opencode.client.js\";\nimport {\n NOOP_FOREGROUND_SESSION_TRACKER,\n type ForegroundSessionTrackerLike,\n} from \"../services/session-activity/foreground-session-tracker.js\";\nimport { resolveSelectedAgent } from \"./agent-selection.js\";\nimport {\n clearStoredAgentSelection,\n clearStoredModelSelection,\n clearStoredModelVariant,\n clearStoredSessionContext,\n} from \"./binding-update.js\";\nimport { createAndBindCurrentProjectSession } from \"./session-creation.js\";\n\ntype ActiveChatBinding = ChatSessionBinding & {\n directory: string;\n projectId: string;\n sessionId: string;\n};\n\nexport class SendPromptUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n private readonly logger: AppLogger,\n private readonly foregroundSessionTracker: ForegroundSessionTrackerLike = NOOP_FOREGROUND_SESSION_TRACKER,\n ) { }\n\n async execute(input: { chatId: number; text?: string | null; files?: PromptSessionFilePartInput[] }) {\n const files = input.files ?? [];\n const promptText = buildPromptText(input.text, files);\n\n if (!promptText && files.length === 0) {\n throw new Error(\"Prompt requires text or file attachments.\");\n }\n\n let binding = await this.sessionRepo.getByChatId(input.chatId);\n\n if (binding?.projectId && binding.directory) {\n const projects = await this.opencodeClient.listProjects();\n const selectedProject = projects.find((project) =>\n project.id === binding?.projectId && project.worktree === binding.directory,\n );\n\n if (!selectedProject) {\n binding = await this.clearInvalidSessionContext(\n input.chatId,\n binding,\n \"saved project is no longer available\",\n );\n }\n }\n\n if (binding?.sessionId && binding.projectId) {\n const sessions = await this.opencodeClient.listSessions();\n const selectedSession = sessions.find((session) =>\n session.id === binding?.sessionId && session.projectID === binding.projectId,\n );\n\n if (!selectedSession) {\n binding = await this.clearInvalidSessionContext(\n input.chatId,\n binding,\n \"saved session is no longer available\",\n );\n }\n }\n\n if (!binding || !binding.sessionId || !binding.projectId || !binding.directory) {\n const createdSession = await createAndBindCurrentProjectSession(\n this.sessionRepo,\n this.opencodeClient,\n {\n chatId: input.chatId,\n binding,\n },\n );\n binding = createdSession.binding;\n this.logger.info(\n {\n chatId: input.chatId,\n sessionId: createdSession.session.id,\n projectId: createdSession.session.projectID,\n directory: createdSession.session.directory,\n },\n \"session created\",\n );\n }\n\n if (binding?.modelProviderId && binding?.modelId) {\n const availableModels = await this.opencodeClient.listModels();\n const selectedModel = availableModels.find((model) =>\n model.providerID === binding?.modelProviderId && model.id === binding?.modelId,\n );\n\n if (!selectedModel) {\n binding = await clearStoredModelSelection(this.sessionRepo, binding);\n this.logger.warn?.(\n {\n chatId: input.chatId,\n },\n \"selected model is no longer available, falling back to OpenCode default\",\n );\n } else if (binding.modelVariant && !(binding.modelVariant in selectedModel.variants)) {\n binding = await clearStoredModelVariant(this.sessionRepo, binding);\n this.logger.warn?.(\n {\n chatId: input.chatId,\n providerId: selectedModel.providerID,\n modelId: selectedModel.id,\n },\n \"selected model variant is no longer available, falling back to default variant\",\n );\n }\n }\n\n if (!binding || !binding.sessionId || !binding.projectId) {\n throw new Error(\"Failed to initialize chat session.\");\n }\n\n let activeBinding = binding as ActiveChatBinding;\n const shouldIsolateImageTurn = hasImageFiles(files);\n const model = activeBinding.modelProviderId && activeBinding.modelId\n ? {\n providerID: activeBinding.modelProviderId,\n modelID: activeBinding.modelId,\n }\n : null;\n const availableAgents = await this.opencodeClient.listAgents();\n const selectedAgent = resolveSelectedAgent(availableAgents, activeBinding.agentName);\n\n if (activeBinding.agentName && selectedAgent?.name !== activeBinding.agentName) {\n activeBinding = await clearStoredAgentSelection(this.sessionRepo, activeBinding) as ActiveChatBinding;\n this.logger.warn?.(\n {\n chatId: input.chatId,\n },\n \"selected agent is no longer available, falling back to OpenCode default\",\n );\n }\n\n const temporarySessionId = shouldIsolateImageTurn\n ? await this.createTemporaryImageSession(input.chatId, activeBinding.sessionId)\n : null;\n const executionSessionId = temporarySessionId ?? activeBinding.sessionId;\n const endForegroundSession = this.foregroundSessionTracker.begin(input.chatId, executionSessionId);\n let result;\n\n try {\n result = await this.opencodeClient.promptSession({\n sessionId: executionSessionId,\n prompt: promptText,\n ...(files.length > 0 ? { files } : {}),\n ...(selectedAgent ? { agent: selectedAgent.name } : {}),\n structured: true,\n ...(model ? { model } : {}),\n ...(activeBinding.modelVariant ? { variant: activeBinding.modelVariant } : {}),\n });\n } finally {\n endForegroundSession();\n\n if (temporarySessionId) {\n await this.cleanupTemporaryImageSession(\n input.chatId,\n activeBinding.sessionId,\n temporarySessionId,\n );\n }\n }\n\n await this.sessionRepo.touch(input.chatId);\n\n return {\n assistantReply: result,\n sessionId: activeBinding.sessionId,\n projectId: activeBinding.projectId,\n };\n }\n\n private async clearInvalidSessionContext(\n chatId: number,\n binding: ChatSessionBinding,\n reason: string,\n ) {\n const nextBinding = await clearStoredSessionContext(this.sessionRepo, binding);\n this.logger.warn?.(\n {\n chatId,\n },\n `${reason}, falling back to the current OpenCode project`,\n );\n\n return nextBinding;\n }\n\n private async createTemporaryImageSession(chatId: number, sessionId: string): Promise<string> {\n const temporarySession = await this.opencodeClient.forkSession(sessionId);\n\n if (!temporarySession.id || temporarySession.id === sessionId) {\n throw new Error(\"OpenCode did not return a distinct temporary session for the image turn.\");\n }\n\n this.logger.info?.(\n {\n chatId,\n parentSessionId: sessionId,\n sessionId: temporarySession.id,\n },\n \"created temporary image session\",\n );\n\n return temporarySession.id;\n }\n\n private async cleanupTemporaryImageSession(\n chatId: number,\n parentSessionId: string,\n sessionId: string,\n ): Promise<void> {\n try {\n const deleted = await this.opencodeClient.deleteSession(sessionId);\n\n if (!deleted) {\n this.logger.warn?.(\n {\n chatId,\n parentSessionId,\n sessionId,\n },\n \"failed to delete temporary image session\",\n );\n }\n } catch (error) {\n this.logger.warn?.(\n {\n error,\n chatId,\n parentSessionId,\n sessionId,\n },\n \"failed to delete temporary image session\",\n );\n }\n }\n}\n\nfunction buildPromptText(\n text: string | null | undefined,\n files: PromptSessionFilePartInput[],\n): string {\n const trimmedText = text?.trim() ?? \"\";\n\n if (!files.some(isImageFile)) {\n return trimmedText;\n }\n\n const promptSections = [\n [\n \"System note for this turn:\",\n \"The user attached one or more images in this message and you can inspect those images directly.\",\n \"Ignore any earlier statements in this conversation claiming that you could not view images or attachments, because those may have come from a previous model or an earlier limitation.\",\n \"If an image is blurry or unreadable, say that it is blurry or unreadable instead of saying that you cannot view images.\",\n ].join(\" \"),\n ];\n\n if (trimmedText) {\n promptSections.push(trimmedText);\n }\n\n return promptSections.join(\"\\n\\n\");\n}\n\nfunction isImageFile(file: PromptSessionFilePartInput): boolean {\n return file.mime.trim().toLowerCase().startsWith(\"image/\");\n}\n\nfunction hasImageFiles(files: PromptSessionFilePartInput[]): boolean {\n return files.some(isImageFile);\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { AppLogger } from \"../infra/logger/index.js\";\nimport type {\n OpenCodeAgent,\n OpenCodeClientLike,\n} from \"../services/opencode/opencode.client.js\";\nimport { isSelectableAgent } from \"./agent-selection.js\";\n\nexport type SwitchAgentResult =\n | {\n found: false;\n }\n | {\n found: true;\n agent: OpenCodeAgent;\n };\n\nexport class SwitchAgentUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n private readonly logger: AppLogger,\n ) { }\n\n async execute(input: { chatId: number; agentName: string }): Promise<SwitchAgentResult> {\n const [binding, agents] = await Promise.all([\n this.sessionRepo.getByChatId(input.chatId),\n this.opencodeClient.listAgents(),\n ]);\n const agent = agents.find((item) => item.name === input.agentName && isSelectableAgent(item));\n\n if (!agent) {\n return { found: false };\n }\n\n await this.sessionRepo.setCurrent({\n chatId: input.chatId,\n sessionId: binding?.sessionId ?? null,\n projectId: binding?.projectId ?? null,\n directory: binding?.directory ?? null,\n agentName: agent.name,\n modelProviderId: binding?.modelProviderId ?? null,\n modelId: binding?.modelId ?? null,\n modelVariant: binding?.modelVariant ?? null,\n language: binding?.language ?? null,\n updatedAt: new Date().toISOString(),\n });\n\n this.logger.info(\n {\n chatId: input.chatId,\n agentName: agent.name,\n },\n \"agent switched\",\n );\n\n return {\n found: true,\n agent,\n };\n }\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { AppLogger } from \"../infra/logger/index.js\";\nimport type {\n OpenCodeClientLike,\n OpenCodeConfiguredModel,\n} from \"../services/opencode/opencode.client.js\";\n\nexport type SwitchModelResult =\n | {\n found: false;\n }\n | {\n found: true;\n model: OpenCodeConfiguredModel;\n variant: string | null;\n };\n\nexport class SwitchModelUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n private readonly logger: AppLogger,\n ) { }\n\n async execute(input: {\n chatId: number;\n providerId: string;\n modelId: string;\n variant?: string | null;\n }): Promise<SwitchModelResult> {\n const [binding, models] = await Promise.all([\n this.sessionRepo.getByChatId(input.chatId),\n this.opencodeClient.listModels(),\n ]);\n const model = models.find((item) =>\n item.providerID === input.providerId && item.id === input.modelId,\n );\n\n if (!model) {\n return { found: false };\n }\n\n const variant = input.variant ?? null;\n\n if (variant && !(variant in model.variants)) {\n return { found: false };\n }\n\n await this.sessionRepo.setCurrent({\n chatId: input.chatId,\n sessionId: binding?.sessionId ?? null,\n projectId: binding?.projectId ?? null,\n directory: binding?.directory ?? null,\n agentName: binding?.agentName ?? null,\n modelProviderId: model.providerID,\n modelId: model.id,\n modelVariant: variant,\n language: binding?.language ?? null,\n updatedAt: new Date().toISOString(),\n });\n\n this.logger.info(\n {\n chatId: input.chatId,\n providerId: model.providerID,\n modelId: model.id,\n variant,\n },\n \"model switched\",\n );\n\n return {\n found: true,\n model,\n variant,\n };\n }\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport type { AppLogger } from \"../infra/logger/index.js\";\nimport type { OpenCodeClientLike, OpenCodeSession } from \"../services/opencode/opencode.client.js\";\n\nexport type SwitchSessionResult =\n | {\n found: false;\n }\n | {\n found: true;\n session: OpenCodeSession;\n };\n\nexport class SwitchSessionUseCase {\n constructor(\n private readonly sessionRepo: SessionRepository,\n private readonly opencodeClient: OpenCodeClientLike,\n private readonly logger: AppLogger,\n ) { }\n\n async execute(input: { chatId: number; sessionId: string }): Promise<SwitchSessionResult> {\n const [binding, sessions] = await Promise.all([\n this.sessionRepo.getByChatId(input.chatId),\n this.opencodeClient.listSessions(),\n ]);\n const currentProjectId = binding?.projectId ?? (await this.opencodeClient.getCurrentProject()).id;\n const session = sessions.find((item) => item.id === input.sessionId && item.projectID === currentProjectId);\n\n if (!session) {\n return { found: false };\n }\n\n await this.sessionRepo.setCurrent({\n chatId: input.chatId,\n sessionId: session.id,\n projectId: session.projectID,\n directory: session.directory,\n agentName: binding?.agentName ?? null,\n modelProviderId: binding?.modelProviderId ?? null,\n modelId: binding?.modelId ?? null,\n modelVariant: binding?.modelVariant ?? null,\n language: binding?.language ?? null,\n updatedAt: new Date().toISOString(),\n });\n\n this.logger.info(\n {\n chatId: input.chatId,\n sessionId: session.id,\n projectId: session.projectID,\n directory: session.directory,\n },\n \"session switched\",\n );\n\n return {\n found: true,\n session,\n };\n }\n}\n","import type { PromptSessionFilePartInput } from \"../services/opencode/opencode.client.js\";\nimport {\n TelegramFileDownloadError,\n type TelegramFileClientLike,\n} from \"../services/telegram/telegram.client.js\";\n\nexport interface UploadFileInput {\n expectedType: \"image\";\n filePath: string;\n filename?: string | null;\n mimeType?: string | null;\n}\n\nexport class ImageFileDownloadError extends Error {\n readonly data: { message: string };\n\n constructor(message: string) {\n super(message);\n this.name = \"ImageFileDownloadError\";\n this.data = { message };\n }\n}\n\nexport class ImageMessageUnsupportedError extends Error {\n readonly data: { message: string };\n\n constructor(message: string) {\n super(message);\n this.name = \"ImageMessageUnsupportedError\";\n this.data = { message };\n }\n}\n\nexport class UploadFileUseCase {\n constructor(private readonly telegramFileClient: TelegramFileClientLike) { }\n\n async execute(input: UploadFileInput): Promise<PromptSessionFilePartInput> {\n if (input.expectedType !== \"image\") {\n throw new ImageMessageUnsupportedError(\"Only image uploads are supported.\");\n }\n\n let download;\n\n try {\n download = await this.telegramFileClient.downloadFile({\n filePath: input.filePath,\n });\n } catch (error) {\n if (error instanceof TelegramFileDownloadError) {\n throw new ImageFileDownloadError(error.message);\n }\n\n throw error;\n }\n\n const mimeType = normalizeMimeType(input.mimeType ?? download.mimeType);\n\n if (!mimeType.startsWith(\"image/\")) {\n throw new ImageMessageUnsupportedError(`Unsupported image MIME type: ${mimeType}`);\n }\n\n const filename = resolveFilename(input.filename, input.filePath, mimeType);\n\n return {\n filename,\n mime: mimeType,\n url: buildDataUrl(download.data, mimeType),\n };\n }\n}\n\nfunction buildDataUrl(data: Uint8Array, mimeType: string): string {\n const buffer = Buffer.from(data.buffer, data.byteOffset, data.byteLength);\n\n return `data:${mimeType};base64,${buffer.toString(\"base64\")}`;\n}\n\nfunction normalizeMimeType(value: string | null): string {\n const normalized = value?.trim().toLowerCase();\n\n return normalized && normalized.length > 0\n ? normalized\n : \"application/octet-stream\";\n}\n\nfunction resolveFilename(\n explicitFilename: string | null | undefined,\n filePath: string,\n mimeType: string,\n): string {\n const preferredFilename = explicitFilename?.trim();\n\n if (preferredFilename) {\n return preferredFilename;\n }\n\n const filePathFilename = filePath.split(\"/\").at(-1)?.trim();\n\n if (filePathFilename) {\n return filePathFilename;\n }\n\n return `telegram-upload${resolveExtension(mimeType)}`;\n}\n\nfunction resolveExtension(mimeType: string): string {\n switch (mimeType) {\n case \"image/jpeg\":\n return \".jpg\";\n case \"image/png\":\n return \".png\";\n case \"image/webp\":\n return \".webp\";\n case \"image/gif\":\n return \".gif\";\n default:\n return \"\";\n }\n}\n","import type { BotDependencies } from \"../bot/types.js\";\nimport type { PermissionApprovalRepository } from \"../domain/permission/approval.repository.js\";\nimport { createOpenCodeAppLogger } from \"../infra/logger/index.js\";\nimport { FilePendingActionRepository } from \"../repositories/pending-action.repo.js\";\nimport { FilePermissionApprovalRepository } from \"../repositories/permission-approval.repo.js\";\nimport { FileSessionRepository } from \"../repositories/session.repo.js\";\nimport {\n createOpenCodeClientFromSdkClient,\n type OpenCodeClientLike,\n type OpenCodeSdkClientLike,\n} from \"../services/opencode/opencode.client.js\";\nimport { createDefaultOpencodeTbotState } from \"../services/storage/bot-state.js\";\nimport { JsonStateStore } from \"../services/storage/json-state-store.js\";\nimport { TelegramFileClient } from \"../services/telegram/telegram.client.js\";\nimport { ForegroundSessionTracker } from \"../services/session-activity/foreground-session-tracker.js\";\nimport { AbortPromptUseCase } from \"../use-cases/abort-prompt.usecase.js\";\nimport { CreateSessionUseCase } from \"../use-cases/create-session.usecase.js\";\nimport { GetHealthUseCase } from \"../use-cases/get-health.usecase.js\";\nimport { GetPathUseCase } from \"../use-cases/get-path.usecase.js\";\nimport { GetStatusUseCase } from \"../use-cases/get-status.usecase.js\";\nimport { ListAgentsUseCase } from \"../use-cases/list-agents.usecase.js\";\nimport { ListLspUseCase } from \"../use-cases/list-lsp.usecase.js\";\nimport { ListMcpUseCase } from \"../use-cases/list-mcp.usecase.js\";\nimport { ListModelsUseCase } from \"../use-cases/list-models.usecase.js\";\nimport { ListSessionsUseCase } from \"../use-cases/list-sessions.usecase.js\";\nimport { RenameSessionUseCase } from \"../use-cases/rename-session.usecase.js\";\nimport { SendPromptUseCase } from \"../use-cases/send-prompt.usecase.js\";\nimport { SwitchAgentUseCase } from \"../use-cases/switch-agent.usecase.js\";\nimport { SwitchModelUseCase } from \"../use-cases/switch-model.usecase.js\";\nimport { SwitchSessionUseCase } from \"../use-cases/switch-session.usecase.js\";\nimport { UploadFileUseCase } from \"../use-cases/upload-file.usecase.js\";\nimport type { AppConfig } from \"./config.js\";\n\nexport interface AppContainer extends BotDependencies {\n opencodeClient: OpenCodeClientLike;\n foregroundSessionTracker: ForegroundSessionTracker;\n permissionApprovalRepo: PermissionApprovalRepository;\n dispose(): Promise<void>;\n}\n\nexport function createAppContainer(\n config: AppConfig,\n client: OpenCodeSdkClientLike,\n): AppContainer {\n const logger = createOpenCodeAppLogger(client, {\n level: config.logLevel,\n });\n\n return createContainer(\n config,\n createOpenCodeClientFromSdkClient(\n client,\n fetch,\n {\n waitTimeoutMs: config.promptWaitTimeoutMs,\n pollRequestTimeoutMs: config.promptPollRequestTimeoutMs,\n recoveryInactivityTimeoutMs: config.promptRecoveryInactivityTimeoutMs,\n },\n ),\n logger,\n );\n}\n\nfunction createContainer(\n config: AppConfig,\n opencodeClient: OpenCodeClientLike,\n logger: BotDependencies[\"logger\"],\n): AppContainer {\n const stateStore = new JsonStateStore({\n filePath: config.stateFilePath,\n createDefaultState: createDefaultOpencodeTbotState,\n });\n const pendingActionRepo = new FilePendingActionRepository(stateStore);\n const permissionApprovalRepo = new FilePermissionApprovalRepository(stateStore);\n const sessionRepo = new FileSessionRepository(stateStore);\n const foregroundSessionTracker = new ForegroundSessionTracker();\n const telegramFileClient = new TelegramFileClient({\n botToken: config.telegramBotToken,\n apiRoot: config.telegramApiRoot,\n });\n const uploadFileUseCase = new UploadFileUseCase(telegramFileClient);\n const abortPromptUseCase = new AbortPromptUseCase(\n sessionRepo,\n opencodeClient,\n foregroundSessionTracker,\n );\n const createSessionUseCase = new CreateSessionUseCase(sessionRepo, opencodeClient, logger);\n const getHealthUseCase = new GetHealthUseCase(opencodeClient);\n const getPathUseCase = new GetPathUseCase(opencodeClient);\n const listAgentsUseCase = new ListAgentsUseCase(sessionRepo, opencodeClient);\n const listLspUseCase = new ListLspUseCase(sessionRepo, opencodeClient);\n const listMcpUseCase = new ListMcpUseCase(sessionRepo, opencodeClient);\n const listSessionsUseCase = new ListSessionsUseCase(sessionRepo, opencodeClient);\n const getStatusUseCase = new GetStatusUseCase(\n getHealthUseCase,\n getPathUseCase,\n listLspUseCase,\n listMcpUseCase,\n listSessionsUseCase,\n sessionRepo,\n );\n const listModelsUseCase = new ListModelsUseCase(sessionRepo, opencodeClient);\n const renameSessionUseCase = new RenameSessionUseCase(sessionRepo, opencodeClient, logger);\n const sendPromptUseCase = new SendPromptUseCase(\n sessionRepo,\n opencodeClient,\n logger,\n foregroundSessionTracker,\n );\n const switchAgentUseCase = new SwitchAgentUseCase(sessionRepo, opencodeClient, logger);\n const switchModelUseCase = new SwitchModelUseCase(sessionRepo, opencodeClient, logger);\n const switchSessionUseCase = new SwitchSessionUseCase(sessionRepo, opencodeClient, logger);\n let disposed = false;\n\n return {\n abortPromptUseCase,\n createSessionUseCase,\n getHealthUseCase,\n getPathUseCase,\n getStatusUseCase,\n listAgentsUseCase,\n listLspUseCase,\n listMcpUseCase,\n logger,\n listModelsUseCase,\n listSessionsUseCase,\n pendingActionRepo,\n renameSessionUseCase,\n sessionRepo,\n sendPromptUseCase,\n switchAgentUseCase,\n switchModelUseCase,\n switchSessionUseCase,\n telegramFileClient,\n uploadFileUseCase,\n opencodeClient,\n foregroundSessionTracker,\n permissionApprovalRepo,\n async dispose() {\n if (disposed) {\n return;\n }\n\n disposed = true;\n logger.info({ filePath: config.stateFilePath }, \"disposing telegram bot container\");\n await logger.flush();\n },\n };\n}\n","import {\n loadAppConfig,\n type LoadAppConfigOptions,\n type AppConfig,\n type PluginConfigSource,\n} from \"./config.js\";\nimport {\n createAppContainer,\n type AppContainer,\n} from \"./container.js\";\nimport type { OpenCodeSdkClientLike } from \"../services/opencode/opencode.client.js\";\n\nexport function bootstrapPluginApp(\n client: OpenCodeSdkClientLike,\n configSource: PluginConfigSource | undefined = {},\n options: LoadAppConfigOptions = {},\n): { config: AppConfig; container: AppContainer } {\n const config = loadAppConfig(configSource, options);\n\n return {\n config,\n container: createAppContainer(config, client),\n };\n}\n","import type { PermissionApprovalReply } from \"../../domain/permission/approval.entity.js\";\nimport type { PermissionApprovalCallbackData } from \"../../bot/types.js\";\nimport type { OpenCodePermissionRequest } from \"../opencode/opencode.client.js\";\n\nexport function buildPermissionApprovalMessage(\n request: OpenCodePermissionRequest,\n): string {\n const patterns = request.patterns.length > 0\n ? request.patterns.map((pattern) => `- ${pattern}`).join(\"\\n\")\n : \"- (none)\";\n\n return [\n \"*Permission Request*\",\n \"\",\n `Permission: \\`${escapeMarkdownV2(request.permission)}\\``,\n `Session: \\`${escapeMarkdownV2(request.sessionID)}\\``,\n \"\",\n \"Patterns:\",\n patterns,\n ].join(\"\\n\");\n}\n\nexport function buildPermissionApprovalResolvedMessage(\n requestId: string,\n reply: PermissionApprovalReply,\n): string {\n return [\n \"Permission request resolved.\",\n \"\",\n `Request: ${requestId}`,\n `Reply: ${reply}`,\n ].join(\"\\n\");\n}\n\nexport function buildPermissionApprovalKeyboard(requestId: string) {\n return {\n inline_keyboard: [[\n {\n text: \"Allow once\",\n callback_data: buildPermissionApprovalCallbackData(\"once\", requestId),\n },\n {\n text: \"Always allow\",\n callback_data: buildPermissionApprovalCallbackData(\"always\", requestId),\n },\n {\n text: \"Reject\",\n callback_data: buildPermissionApprovalCallbackData(\"reject\", requestId),\n },\n ]],\n };\n}\n\nexport function buildPermissionApprovalCallbackData(\n reply: PermissionApprovalReply,\n requestId: string,\n): PermissionApprovalCallbackData {\n return `permission:${reply}:${requestId}`;\n}\n\nexport function parsePermissionApprovalCallbackData(data: string): {\n reply: PermissionApprovalReply;\n requestId: string;\n} | null {\n if (!data.startsWith(\"permission:\")) {\n return null;\n }\n\n const [, reply, requestId] = data.split(\":\", 3);\n\n if (!requestId || !isPermissionApprovalReply(reply)) {\n return null;\n }\n\n return {\n reply,\n requestId,\n };\n}\n\nfunction isPermissionApprovalReply(value: string): value is PermissionApprovalReply {\n return value === \"once\" || value === \"always\" || value === \"reject\";\n}\n\nfunction escapeMarkdownV2(value: string): string {\n return value.replaceAll(/([_*\\[\\]()~`>#+\\-=|{}.!\\\\])/g, \"\\\\$1\");\n}\n","import type { PendingPermissionApproval, PermissionApprovalReply } from \"../domain/permission/approval.entity.js\";\nimport type { OpenCodePermissionRequest } from \"../services/opencode/opencode.client.js\";\nimport {\n buildPermissionApprovalKeyboard,\n buildPermissionApprovalMessage,\n buildPermissionApprovalResolvedMessage,\n} from \"../services/permission/telegram-approval.js\";\nimport type { TelegramBotRuntime } from \"./runtime.js\";\n\ntype TelegramBotPluginEvent =\n | {\n type: \"permission.asked\";\n properties: OpenCodePermissionRequest;\n }\n | {\n type: \"permission.replied\";\n properties: {\n sessionID: string;\n requestID: string;\n reply: PermissionApprovalReply;\n };\n }\n | {\n type: \"session.error\";\n properties: {\n sessionID?: string;\n error?: {\n name?: string;\n data?: {\n message?: string;\n };\n };\n };\n }\n | {\n type: \"session.idle\";\n properties: {\n sessionID: string;\n };\n };\n\nexport async function handleTelegramBotPluginEvent(\n runtime: TelegramBotRuntime,\n event: TelegramBotPluginEvent,\n): Promise<void> {\n switch (event.type) {\n case \"permission.asked\":\n await handlePermissionAsked(runtime, event.properties);\n return;\n case \"permission.replied\":\n await handlePermissionReplied(runtime, event.properties.requestID, event.properties.reply);\n return;\n case \"session.error\":\n await handleSessionError(runtime, event.properties.sessionID, event.properties.error);\n return;\n case \"session.idle\":\n await handleSessionIdle(runtime, event.properties.sessionID);\n return;\n default:\n return;\n }\n}\n\nasync function handlePermissionAsked(\n runtime: TelegramBotRuntime,\n request: OpenCodePermissionRequest,\n): Promise<void> {\n const bindings = await runtime.container.sessionRepo.listBySessionId(request.sessionID);\n const chatIds = new Set([\n ...bindings.map((binding) => binding.chatId),\n ...runtime.container.foregroundSessionTracker.listChatIds(request.sessionID),\n ]);\n const approvals = await runtime.container.permissionApprovalRepo.listByRequestId(request.id);\n const approvedChatIds = new Set(approvals.map((approval) => approval.chatId));\n\n for (const chatId of chatIds) {\n if (approvedChatIds.has(chatId)) {\n continue;\n }\n\n try {\n const message = await runtime.bot.api.sendMessage(\n chatId,\n buildPermissionApprovalMessage(request),\n {\n parse_mode: \"MarkdownV2\",\n reply_markup: buildPermissionApprovalKeyboard(request.id),\n },\n );\n\n await runtime.container.permissionApprovalRepo.set({\n requestId: request.id,\n sessionId: request.sessionID,\n chatId,\n messageId: message.message_id,\n status: \"pending\",\n updatedAt: new Date().toISOString(),\n });\n } catch (error) {\n runtime.container.logger.error(\n {\n error,\n chatId,\n requestId: request.id,\n },\n \"failed to deliver permission request to Telegram\",\n );\n }\n }\n}\n\nasync function handlePermissionReplied(\n runtime: TelegramBotRuntime,\n requestId: string,\n reply: PermissionApprovalReply,\n): Promise<void> {\n const approvals = await runtime.container.permissionApprovalRepo.listByRequestId(requestId);\n\n await Promise.all(\n approvals.map(async (approval) => {\n try {\n await runtime.bot.api.editMessageText(\n approval.chatId,\n approval.messageId,\n buildPermissionApprovalResolvedMessage(requestId, reply),\n );\n } catch (error) {\n runtime.container.logger.warn(\n {\n error,\n chatId: approval.chatId,\n requestId,\n },\n \"failed to update Telegram permission message\",\n );\n }\n\n await runtime.container.permissionApprovalRepo.set(toResolvedApproval(approval, reply));\n }),\n );\n}\n\nasync function handleSessionError(\n runtime: TelegramBotRuntime,\n sessionId: string | undefined,\n error: {\n name?: string;\n data?: {\n message?: string;\n };\n } | undefined,\n): Promise<void> {\n if (!sessionId) {\n runtime.container.logger.error({ error }, \"session error received without a session id\");\n return;\n }\n\n const wasForeground = runtime.container.foregroundSessionTracker.clear(sessionId);\n\n if (wasForeground) {\n runtime.container.logger.warn({ error, sessionId }, \"session error suppressed for foreground Telegram session\");\n return;\n }\n\n const message = error?.data?.message?.trim()\n || error?.name?.trim()\n || \"Unknown session error.\";\n\n await notifyBoundChats(runtime, sessionId, `Session failed.\\n\\nSession: ${sessionId}\\nError: ${message}`);\n}\n\nasync function handleSessionIdle(\n runtime: TelegramBotRuntime,\n sessionId: string,\n): Promise<void> {\n const wasForeground = runtime.container.foregroundSessionTracker.clear(sessionId);\n\n if (wasForeground) {\n runtime.container.logger.info({ sessionId }, \"session idle notification suppressed for foreground Telegram session\");\n return;\n }\n\n await notifyBoundChats(runtime, sessionId, `Session finished.\\n\\nSession: ${sessionId}`);\n}\n\nasync function notifyBoundChats(\n runtime: TelegramBotRuntime,\n sessionId: string,\n text: string,\n): Promise<void> {\n const bindings = await runtime.container.sessionRepo.listBySessionId(sessionId);\n const chatIds = [...new Set(bindings.map((binding) => binding.chatId))];\n\n await Promise.all(\n chatIds.map(async (chatId) => {\n try {\n await runtime.bot.api.sendMessage(chatId, text);\n } catch (error) {\n runtime.container.logger.warn(\n { error, chatId, sessionId },\n \"failed to notify Telegram chat about session event\",\n );\n }\n }),\n );\n}\n\nfunction toResolvedApproval(\n approval: PendingPermissionApproval,\n reply: PermissionApprovalReply,\n): PendingPermissionApproval {\n return {\n ...approval,\n status: reply,\n updatedAt: new Date().toISOString(),\n };\n}\n","export type BotLanguage = \"en\" | \"zh-CN\" | \"ja\";\n\nexport interface BotCopy {\n locale: BotLanguage;\n commands: {\n start: string;\n status: string;\n new: string;\n agents: string;\n sessions: string;\n cancel: string;\n model: string;\n language: string;\n };\n start: {\n lines: string[];\n };\n systemStatus: {\n title: string;\n };\n common: {\n notSelected: string;\n openCodeDefault: string;\n previousPage: string;\n nextPage: string;\n page(currentPage: number, totalPages: number): string;\n };\n status: {\n processing: string;\n alreadyProcessing: string;\n };\n prompt: {\n emptyResponse: string;\n };\n replyMetrics: {\n durationLabel: string;\n tokensLabel: string;\n totalLabel: string;\n inputLabel: string;\n outputLabel: string;\n reasoningLabel: string;\n cacheReadLabel: string;\n cacheWriteLabel: string;\n notAvailable: string;\n };\n abort: {\n noSession: string;\n notRunning: string;\n aborted: string;\n };\n errors: {\n unexpected: string;\n providerAuth: string;\n requestAborted: string;\n promptTimeout: string;\n structuredOutput: string;\n voiceUnsupported: string;\n imageDownload: string;\n imageUnsupported: string;\n outputLength: string;\n contextOverflow: string;\n providerRequest: string;\n notFound: string;\n badRequest: string;\n causeLabel: string;\n retryableLabel: string;\n statusCodeLabel: string;\n };\n health: {\n title: string;\n status(healthy: boolean): string;\n version(version: string): string;\n };\n path: {\n title: string;\n home(path: string): string;\n state(path: string): string;\n config(path: string): string;\n worktree(path: string): string;\n directory(path: string): string;\n };\n sessions: {\n none: string;\n title: string;\n actionTitle: string;\n chooseAction: string;\n currentProject(worktree: string): string;\n currentSession(session: string): string;\n selectedSession(session: string): string;\n switched: string;\n created: string;\n renamed: string;\n renameCancelled: string;\n renameEmpty: string;\n renameExpired: string;\n renamePendingInput: string;\n renamePrompt(session: string): string;\n switchAction: string;\n renameAction: string;\n backToList: string;\n expired: string;\n };\n lsp: {\n none: string;\n title: string;\n currentProject(worktree: string): string;\n connected: string;\n error: string;\n };\n mcp: {\n none: string;\n title: string;\n currentProject(worktree: string): string;\n connected: string;\n disabled: string;\n needsAuth: string;\n failed(error: string): string;\n needsClientRegistration(error: string): string;\n };\n agents: {\n none: string;\n title: string;\n current(agent: string): string;\n switched: string;\n expired: string;\n };\n models: {\n none: string;\n title: string;\n configuredOnly: string;\n current(model: string): string;\n switched: string;\n currentReasoningLevel(variant: string): string;\n reasoningLevel(variant: string): string;\n noReasoningLevels: string;\n reasoningLevelsTitle: string;\n model(model: string): string;\n modelNumber(modelIndex: number): string;\n expired: string;\n reasoningLevelExpired: string;\n defaultReasoningLevel: string;\n };\n language: {\n title: string;\n choose: string;\n current(label: string): string;\n switched: string;\n expired: string;\n labels: Record<BotLanguage, string>;\n };\n}\n\nexport const DEFAULT_BOT_LANGUAGE: BotLanguage = \"en\";\nexport const SUPPORTED_BOT_LANGUAGES: readonly BotLanguage[] = [\"en\", \"zh-CN\", \"ja\"];\n\nconst EN_BOT_COPY: BotCopy = {\n locale: \"en\",\n commands: {\n start: \"Show welcome and quick start\",\n status: \"Show system status\",\n new: \"Create a new session\",\n agents: \"Show and switch agents\",\n sessions: \"Show and switch sessions\",\n cancel: \"Cancel rename or abort running request\",\n model: \"Show and switch models\",\n language: \"Show and switch language\",\n },\n start: {\n lines: [\n \"# Welcome to opencode-tbot\",\n \"\",\n \"Talk to your OpenCode server from Telegram.\",\n \"\",\n \"## Quick start\",\n \"1. Run `/status` to confirm the server is ready.\",\n \"2. Run `/new [title]` to create a fresh session.\",\n \"\",\n \"Send a text or image message directly.\",\n ],\n },\n systemStatus: {\n title: \"System Status\",\n },\n common: {\n notSelected: \"Not selected\",\n openCodeDefault: \"Not selected (using OpenCode default)\",\n previousPage: \"Previous\",\n nextPage: \"Next\",\n page(currentPage: number, totalPages: number) {\n return `Page ${currentPage}/${totalPages}`;\n },\n },\n status: {\n processing: \"Processing...\",\n alreadyProcessing: \"Another request is still running. Wait for it to finish before sending a new prompt.\",\n },\n prompt: {\n emptyResponse: \"OpenCode returned empty response.\",\n },\n replyMetrics: {\n durationLabel: \"Duration\",\n tokensLabel: \"Tokens\",\n totalLabel: \"total\",\n inputLabel: \"input\",\n outputLabel: \"output\",\n reasoningLabel: \"reasoning\",\n cacheReadLabel: \"cache.read\",\n cacheWriteLabel: \"cache.write\",\n notAvailable: \"n/a\",\n },\n abort: {\n noSession: \"No active session is bound to this chat yet.\",\n notRunning: \"No request is currently running for the current session.\",\n aborted: \"Abort signal sent to the current session.\",\n },\n errors: {\n unexpected: \"Unexpected error.\",\n providerAuth: \"Provider authentication failed.\",\n requestAborted: \"Request was aborted.\",\n promptTimeout: \"OpenCode request timed out.\",\n structuredOutput: \"Structured output validation failed.\",\n voiceUnsupported: \"Voice messages are not supported. Send text or an image instead.\",\n imageDownload: \"Failed to download the Telegram image file.\",\n imageUnsupported: \"Image file is too large or unsupported.\",\n outputLength: \"Reply hit the model output limit.\",\n contextOverflow: \"Conversation exceeded the model context window.\",\n providerRequest: \"Provider request failed.\",\n notFound: \"Requested resource was not found.\",\n badRequest: \"Request was rejected by OpenCode.\",\n causeLabel: \"Cause\",\n retryableLabel: \"retryable\",\n statusCodeLabel: \"status\",\n },\n health: {\n title: \"Server Health\",\n status(healthy: boolean) {\n return `Status: ${healthy ? \"healthy\" : \"unhealthy\"}`;\n },\n version(version: string) {\n return `Version: ${version}`;\n },\n },\n path: {\n title: \"Current Paths\",\n home(path: string) {\n return `Home: ${path}`;\n },\n state(path: string) {\n return `State: ${path}`;\n },\n config(path: string) {\n return `Config: ${path}`;\n },\n worktree(path: string) {\n return `Worktree: ${path}`;\n },\n directory(path: string) {\n return `Current working directory: ${path}`;\n },\n },\n sessions: {\n none: \"No sessions available in the current project.\",\n title: \"Session List\",\n actionTitle: \"Session Actions\",\n chooseAction: \"Choose an action for this session.\",\n currentProject(worktree: string) {\n return `Current project: ${worktree}`;\n },\n currentSession(session: string) {\n return `Current session: ${session}`;\n },\n selectedSession(session: string) {\n return `Selected session: ${session}`;\n },\n switched: \"Session switched.\",\n created: \"Session created.\",\n renamed: \"Session renamed.\",\n renameCancelled: \"Session rename cancelled.\",\n renameEmpty: \"Session name cannot be empty. Send a new name or /cancel.\",\n renameExpired: \"The session is no longer available. Run /sessions again.\",\n renamePendingInput: \"Waiting for the new session name. Send plain text or /cancel.\",\n renamePrompt(session: string) {\n return [\n `Rename session: ${session}`,\n \"Send the new session name as your next text message.\",\n \"Send /cancel to cancel.\",\n ].join(\"\\n\");\n },\n switchAction: \"Switch\",\n renameAction: \"Rename\",\n backToList: \"Back to list\",\n expired: \"The session is no longer available. Run /sessions again.\",\n },\n lsp: {\n none: \"No LSP servers detected for the current project.\",\n title: \"LSP Servers\",\n currentProject(worktree: string) {\n return `Current project: ${worktree}`;\n },\n connected: \"connected\",\n error: \"error\",\n },\n mcp: {\n none: \"No MCP servers configured for the current project.\",\n title: \"MCP Servers\",\n currentProject(worktree: string) {\n return `Current project: ${worktree}`;\n },\n connected: \"connected\",\n disabled: \"disabled\",\n needsAuth: \"needs auth\",\n failed(error: string) {\n return `failed: ${error}`;\n },\n needsClientRegistration(error: string) {\n return `needs client registration: ${error}`;\n },\n },\n agents: {\n none: \"No agents available.\",\n title: \"Agent List\",\n current(agent: string) {\n return `Current agent: ${agent}`;\n },\n switched: \"Agent switched.\",\n expired: \"The agent is no longer available. Run /agents again.\",\n },\n models: {\n none: \"No models available.\",\n title: \"Model List\",\n configuredOnly: \"Only models currently available in OpenCode and connected providers are shown.\",\n current(model: string) {\n return `Current model: ${model}`;\n },\n switched: \"Model switched.\",\n currentReasoningLevel(variant: string) {\n return `Current reasoning level: ${variant}`;\n },\n reasoningLevel(variant: string) {\n return `Reasoning level: ${variant}`;\n },\n noReasoningLevels: \"This model has no selectable reasoning levels.\",\n reasoningLevelsTitle: \"Reasoning Levels\",\n model(model: string) {\n return `Model: ${model}`;\n },\n modelNumber(modelIndex: number) {\n return `Model number: ${modelIndex}`;\n },\n expired: \"The model is no longer available. Run /model again.\",\n reasoningLevelExpired: \"The reasoning level is no longer available. Run /model again.\",\n defaultReasoningLevel: \"default\",\n },\n language: {\n title: \"Language\",\n choose: \"Choose the display language for bot menus and replies.\",\n current(label: string) {\n return `Current language: ${label}`;\n },\n switched: \"Language switched.\",\n expired: \"The language option is no longer available. Run /language again.\",\n labels: {\n en: \"English\",\n \"zh-CN\": \"简体中文\",\n ja: \"日本語\",\n },\n },\n};\n\nconst ZH_CN_BOT_COPY: BotCopy = {\n locale: \"zh-CN\",\n commands: {\n start: \"查看欢迎信息与快速开始\",\n status: \"查看系统状态\",\n new: \"新建会话\",\n agents: \"查看并切换 Agent\",\n sessions: \"查看并切换会话\",\n cancel: \"取消重命名或中止当前请求\",\n model: \"查看并切换模型\",\n language: \"查看并切换语言\",\n },\n start: {\n lines: [\n \"# 欢迎使用 opencode-tbot\",\n \"\",\n \"通过 Telegram 直接和 OpenCode 服务对话。\",\n \"\",\n \"## 快速开始\",\n \"1. 先运行 `/status` 确认服务状态正常。\",\n \"2. 运行 `/new [title]` 创建一个新会话。\",\n \"\",\n \"直接发送文本或图片消息即可。\",\n ],\n },\n systemStatus: {\n title: \"系统状态\",\n },\n common: {\n notSelected: \"未选择\",\n openCodeDefault: \"未选择(使用 OpenCode 默认值)\",\n previousPage: \"上一页\",\n nextPage: \"下一页\",\n page(currentPage: number, totalPages: number) {\n return `第 ${currentPage}/${totalPages} 页`;\n },\n },\n status: {\n processing: \"处理中...\",\n alreadyProcessing: \"另一个请求仍在运行。请等待其完成后再发送新的提示词。\",\n },\n prompt: {\n emptyResponse: \"OpenCode 返回了空响应。\",\n },\n replyMetrics: {\n durationLabel: \"耗时\",\n tokensLabel: \"令牌数\",\n totalLabel: \"总计\",\n inputLabel: \"输入\",\n outputLabel: \"输出\",\n reasoningLabel: \"推理\",\n cacheReadLabel: \"缓存读取\",\n cacheWriteLabel: \"缓存写入\",\n notAvailable: \"不可用\",\n },\n abort: {\n noSession: \"当前聊天还没有绑定活动会话。\",\n notRunning: \"当前会话没有正在运行的请求。\",\n aborted: \"已向当前会话发送中止信号。\",\n },\n errors: {\n unexpected: \"发生未知错误。\",\n providerAuth: \"Provider 认证失败。\",\n requestAborted: \"请求已中止。\",\n promptTimeout: \"OpenCode 响应超时。\",\n structuredOutput: \"结构化输出校验失败。\",\n voiceUnsupported: \"暂不支持语音消息,请改发文本或图片。\",\n imageDownload: \"下载 Telegram 图片文件失败。\",\n imageUnsupported: \"图片文件过大或不受支持。\",\n outputLength: \"回复触发了模型输出长度上限。\",\n contextOverflow: \"会话已超过模型上下文窗口。\",\n providerRequest: \"Provider 请求失败。\",\n notFound: \"请求的资源不存在。\",\n badRequest: \"OpenCode 拒绝了该请求。\",\n causeLabel: \"原因\",\n retryableLabel: \"可重试\",\n statusCodeLabel: \"状态码\",\n },\n health: {\n title: \"服务健康状态\",\n status(healthy: boolean) {\n return `状态: ${healthy ? \"健康\" : \"异常\"}`;\n },\n version(version: string) {\n return `版本: ${version}`;\n },\n },\n path: {\n title: \"当前路径\",\n home(path: string) {\n return `主目录: ${path}`;\n },\n state(path: string) {\n return `状态目录: ${path}`;\n },\n config(path: string) {\n return `配置文件: ${path}`;\n },\n worktree(path: string) {\n return `工作树: ${path}`;\n },\n directory(path: string) {\n return `当前工作目录: ${path}`;\n },\n },\n sessions: {\n none: \"当前项目下没有可用会话。\",\n title: \"会话列表\",\n actionTitle: \"会话操作\",\n chooseAction: \"请选择该会话的操作。\",\n currentProject(worktree: string) {\n return `当前项目: ${worktree}`;\n },\n currentSession(session: string) {\n return `当前会话: ${session}`;\n },\n selectedSession(session: string) {\n return `已选会话: ${session}`;\n },\n switched: \"会话已切换。\",\n created: \"会话已创建。\",\n renamed: \"会话已重命名。\",\n renameCancelled: \"已取消会话重命名。\",\n renameEmpty: \"会话名称不能为空。请重新发送名称或发送 /cancel。\",\n renameExpired: \"该会话已不可用。请重新运行 /sessions。\",\n renamePendingInput: \"当前正在等待新的会话名称。请发送纯文本或 /cancel。\",\n renamePrompt(session: string) {\n return [\n `重命名会话: ${session}`,\n \"请发送新的会话名称。\",\n \"发送 /cancel 取消。\",\n ].join(\"\\n\");\n },\n switchAction: \"切换\",\n renameAction: \"重命名\",\n backToList: \"返回列表\",\n expired: \"该会话已不可用。请重新运行 /sessions。\",\n },\n lsp: {\n none: \"当前项目未检测到 LSP。\",\n title: \"LSP 服务\",\n currentProject(worktree: string) {\n return `当前项目: ${worktree}`;\n },\n connected: \"已连接\",\n error: \"异常\",\n },\n mcp: {\n none: \"当前项目未配置 MCP。\",\n title: \"MCP 服务\",\n currentProject(worktree: string) {\n return `当前项目: ${worktree}`;\n },\n connected: \"已连接\",\n disabled: \"已禁用\",\n needsAuth: \"需要认证\",\n failed(error: string) {\n return `失败: ${error}`;\n },\n needsClientRegistration(error: string) {\n return `需要客户端注册: ${error}`;\n },\n },\n agents: {\n none: \"没有可用 Agent。\",\n title: \"Agent 列表\",\n current(agent: string) {\n return `当前 Agent: ${agent}`;\n },\n switched: \"Agent 已切换。\",\n expired: \"该 Agent 已不可用。请重新运行 /agents。\",\n },\n models: {\n none: \"没有可用模型。\",\n title: \"模型列表\",\n configuredOnly: \"仅显示当前在 OpenCode 和已连接 provider 中实际可用的模型。\",\n current(model: string) {\n return `当前模型: ${model}`;\n },\n switched: \"模型已切换。\",\n currentReasoningLevel(variant: string) {\n return `当前推理级别: ${variant}`;\n },\n reasoningLevel(variant: string) {\n return `推理级别: ${variant}`;\n },\n noReasoningLevels: \"该模型没有可选的推理级别。\",\n reasoningLevelsTitle: \"推理级别\",\n model(model: string) {\n return `模型: ${model}`;\n },\n modelNumber(modelIndex: number) {\n return `模型编号: ${modelIndex}`;\n },\n expired: \"该模型已不可用。请重新运行 /model。\",\n reasoningLevelExpired: \"该推理级别已不可用。请重新运行 /model。\",\n defaultReasoningLevel: \"默认\",\n },\n language: {\n title: \"语言\",\n choose: \"选择 Bot 菜单和回复的显示语言。\",\n current(label: string) {\n return `当前语言: ${label}`;\n },\n switched: \"语言已切换。\",\n expired: \"该语言选项已不可用。请重新运行 /language。\",\n labels: {\n en: \"English\",\n \"zh-CN\": \"简体中文\",\n ja: \"日本語\",\n },\n },\n};\n\nconst JA_BOT_COPY: BotCopy = {\n locale: \"ja\",\n commands: {\n start: \"ようこそとクイックスタートを表示\",\n status: \"システム状態を表示\",\n new: \"新しいセッションを作成\",\n agents: \"エージェントを表示して切り替え\",\n sessions: \"セッションを表示して切り替え\",\n cancel: \"名前変更を取り消すか実行中のリクエストを中止\",\n model: \"モデルを表示して切り替え\",\n language: \"言語を表示して切り替え\",\n },\n start: {\n lines: [\n \"# opencode-tbot へようこそ\",\n \"\",\n \"Telegram から OpenCode サーバーとやり取りできます。\",\n \"\",\n \"## クイックスタート\",\n \"1. `/status` を実行してサーバーの準備完了を確認します。\",\n \"2. `/new [title]` を実行して新しいセッションを作成します。\",\n \"\",\n \"そのままテキストまたは画像メッセージを送信できます。\",\n ],\n },\n systemStatus: {\n title: \"システム状態\",\n },\n common: {\n notSelected: \"未選択\",\n openCodeDefault: \"未選択(OpenCode のデフォルトを使用)\",\n previousPage: \"前へ\",\n nextPage: \"次へ\",\n page(currentPage: number, totalPages: number) {\n return `ページ ${currentPage}/${totalPages}`;\n },\n },\n status: {\n processing: \"処理中...\",\n alreadyProcessing: \"別のリクエストがまだ実行中です。完了するまで新しいプロンプトを送信しないでください。\",\n },\n prompt: {\n emptyResponse: \"OpenCode から空の応答が返されました。\",\n },\n replyMetrics: {\n durationLabel: \"所要時間\",\n tokensLabel: \"トークン\",\n totalLabel: \"合計\",\n inputLabel: \"入力\",\n outputLabel: \"出力\",\n reasoningLabel: \"推論\",\n cacheReadLabel: \"キャッシュ読込\",\n cacheWriteLabel: \"キャッシュ書込\",\n notAvailable: \"該当なし\",\n },\n abort: {\n noSession: \"このチャットにはまだアクティブなセッションが紐付いていません。\",\n notRunning: \"現在のセッションで実行中のリクエストはありません。\",\n aborted: \"現在のセッションに中止シグナルを送信しました。\",\n },\n errors: {\n unexpected: \"予期しないエラーが発生しました。\",\n providerAuth: \"Provider の認証に失敗しました。\",\n requestAborted: \"リクエストは中止されました。\",\n promptTimeout: \"OpenCode リクエストがタイムアウトしました。\",\n structuredOutput: \"構造化出力の検証に失敗しました。\",\n voiceUnsupported: \"音声メッセージには対応していません。代わりにテキストまたは画像を送信してください。\",\n imageDownload: \"Telegram の画像ファイルをダウンロードできませんでした。\",\n imageUnsupported: \"画像ファイルが大きすぎるか、サポートされていません。\",\n outputLength: \"返信がモデルの出力上限に達しました。\",\n contextOverflow: \"会話がモデルのコンテキスト上限を超えました。\",\n providerRequest: \"Provider へのリクエストに失敗しました。\",\n notFound: \"要求されたリソースが見つかりませんでした。\",\n badRequest: \"リクエストは OpenCode に拒否されました。\",\n causeLabel: \"原因\",\n retryableLabel: \"再試行可能\",\n statusCodeLabel: \"ステータス\",\n },\n health: {\n title: \"サーバー状態\",\n status(healthy: boolean) {\n return `状態: ${healthy ? \"正常\" : \"異常\"}`;\n },\n version(version: string) {\n return `バージョン: ${version}`;\n },\n },\n path: {\n title: \"現在のパス\",\n home(path: string) {\n return `ホーム: ${path}`;\n },\n state(path: string) {\n return `状態: ${path}`;\n },\n config(path: string) {\n return `設定: ${path}`;\n },\n worktree(path: string) {\n return `ワークツリー: ${path}`;\n },\n directory(path: string) {\n return `現在の作業ディレクトリ: ${path}`;\n },\n },\n sessions: {\n none: \"現在のプロジェクトで利用できるセッションはありません。\",\n title: \"セッション一覧\",\n actionTitle: \"セッション操作\",\n chooseAction: \"このセッションに対する操作を選択してください。\",\n currentProject(worktree: string) {\n return `現在のプロジェクト: ${worktree}`;\n },\n currentSession(session: string) {\n return `現在のセッション: ${session}`;\n },\n selectedSession(session: string) {\n return `選択中のセッション: ${session}`;\n },\n switched: \"セッションを切り替えました。\",\n created: \"セッションを作成しました。\",\n renamed: \"セッション名を変更しました。\",\n renameCancelled: \"セッション名の変更を取り消しました。\",\n renameEmpty: \"セッション名は空にできません。新しい名前を送信するか /cancel を実行してください。\",\n renameExpired: \"このセッションはもう利用できません。/sessions を再実行してください。\",\n renamePendingInput: \"新しいセッション名の入力待ちです。プレーンテキストを送るか /cancel を実行してください。\",\n renamePrompt(session: string) {\n return [\n `セッション名を変更: ${session}`,\n \"次のテキストメッセージで新しいセッション名を送信してください。\",\n \"/cancel で取り消します。\",\n ].join(\"\\n\");\n },\n switchAction: \"切り替え\",\n renameAction: \"名前変更\",\n backToList: \"一覧に戻る\",\n expired: \"このセッションはもう利用できません。/sessions を再実行してください。\",\n },\n lsp: {\n none: \"現在のプロジェクトで LSP サーバーは検出されませんでした。\",\n title: \"LSP サーバー\",\n currentProject(worktree: string) {\n return `現在のプロジェクト: ${worktree}`;\n },\n connected: \"接続済み\",\n error: \"エラー\",\n },\n mcp: {\n none: \"現在のプロジェクトで MCP サーバーは設定されていません。\",\n title: \"MCP サーバー\",\n currentProject(worktree: string) {\n return `現在のプロジェクト: ${worktree}`;\n },\n connected: \"接続済み\",\n disabled: \"無効\",\n needsAuth: \"認証が必要\",\n failed(error: string) {\n return `失敗: ${error}`;\n },\n needsClientRegistration(error: string) {\n return `クライアント登録が必要: ${error}`;\n },\n },\n agents: {\n none: \"利用可能なエージェントはありません。\",\n title: \"エージェント一覧\",\n current(agent: string) {\n return `現在のエージェント: ${agent}`;\n },\n switched: \"エージェントを切り替えました。\",\n expired: \"このエージェントはもう利用できません。/agents を再実行してください。\",\n },\n models: {\n none: \"利用可能なモデルはありません。\",\n title: \"モデル一覧\",\n configuredOnly: \"OpenCode と接続済み provider で現在利用可能なモデルのみ表示します。\",\n current(model: string) {\n return `現在のモデル: ${model}`;\n },\n switched: \"モデルを切り替えました。\",\n currentReasoningLevel(variant: string) {\n return `現在の推論レベル: ${variant}`;\n },\n reasoningLevel(variant: string) {\n return `推論レベル: ${variant}`;\n },\n noReasoningLevels: \"このモデルには選択可能な推論レベルがありません。\",\n reasoningLevelsTitle: \"推論レベル\",\n model(model: string) {\n return `モデル: ${model}`;\n },\n modelNumber(modelIndex: number) {\n return `モデル番号: ${modelIndex}`;\n },\n expired: \"このモデルはもう利用できません。/model を再実行してください。\",\n reasoningLevelExpired: \"この推論レベルはもう利用できません。/model を再実行してください。\",\n defaultReasoningLevel: \"デフォルト\",\n },\n language: {\n title: \"言語\",\n choose: \"Bot のメニューと返信に使う表示言語を選択してください。\",\n current(label: string) {\n return `現在の言語: ${label}`;\n },\n switched: \"言語を切り替えました。\",\n expired: \"この言語オプションはもう利用できません。/language を再実行してください。\",\n labels: {\n en: \"English\",\n \"zh-CN\": \"简体中文\",\n ja: \"日本語\",\n },\n },\n};\n\nexport const BOT_COPY = EN_BOT_COPY;\n\nexport function isBotLanguage(value: string): value is BotLanguage {\n return SUPPORTED_BOT_LANGUAGES.includes(value as BotLanguage);\n}\n\nexport function normalizeBotLanguage(value: string | null | undefined): BotLanguage {\n if (!value) {\n return DEFAULT_BOT_LANGUAGE;\n }\n\n const normalized = value.trim().toLowerCase();\n\n if (normalized === \"zh-cn\" || normalized === \"zh-hans\" || normalized === \"zh\") {\n return \"zh-CN\";\n }\n\n if (normalized === \"ja\" || normalized === \"ja-jp\" || normalized === \"ja_jp\") {\n return \"ja\";\n }\n\n return \"en\";\n}\n\nexport function getBotCopy(language: BotLanguage | null | undefined = DEFAULT_BOT_LANGUAGE): BotCopy {\n const normalized = normalizeBotLanguage(language);\n\n if (normalized === \"zh-CN\") {\n return ZH_CN_BOT_COPY;\n }\n\n if (normalized === \"ja\") {\n return JA_BOT_COPY;\n }\n\n return EN_BOT_COPY;\n}\n\nexport function getLanguageLabel(language: BotLanguage, copy: BotCopy = BOT_COPY): string {\n return copy.language.labels[language];\n}\n","import type { BotCommand } from \"grammy/types\";\nimport { getBotCopy, type BotLanguage } from \"../copy.js\";\n\nexport function getTelegramCommands(language: BotLanguage = \"en\"): BotCommand[] {\n const copy = getBotCopy(language);\n\n return [\n {\n command: \"start\",\n description: copy.commands.start,\n },\n {\n command: \"status\",\n description: copy.commands.status,\n },\n {\n command: \"new\",\n description: copy.commands.new,\n },\n {\n command: \"agents\",\n description: copy.commands.agents,\n },\n {\n command: \"sessions\",\n description: copy.commands.sessions,\n },\n {\n command: \"cancel\",\n description: copy.commands.cancel,\n },\n {\n command: \"model\",\n description: copy.commands.model,\n },\n {\n command: \"language\",\n description: copy.commands.language,\n },\n ];\n}\n\nexport const TELEGRAM_COMMANDS: BotCommand[] = getTelegramCommands();\n","import type { Bot } from \"grammy\";\nimport type { BotCommandScope } from \"grammy/types\";\nimport type { AppLogger } from \"../../infra/logger/index.js\";\nimport { type BotLanguage } from \"../copy.js\";\nimport { getTelegramCommands, TELEGRAM_COMMANDS } from \"./command-list.js\";\n\nexport const TELEGRAM_COMMAND_SYNC_SCOPES: ReadonlyArray<BotCommandScope> = [\n { type: \"default\" },\n { type: \"all_private_chats\" },\n];\n\nexport async function syncTelegramCommands(bot: Bot, logger: AppLogger): Promise<void> {\n await Promise.all(\n TELEGRAM_COMMAND_SYNC_SCOPES.map((scope) =>\n bot.api.setMyCommands(TELEGRAM_COMMANDS, { scope }),\n ),\n );\n\n logger.info(\n {\n commands: TELEGRAM_COMMANDS.map((command) => command.command),\n scopes: TELEGRAM_COMMAND_SYNC_SCOPES.map((scope) => scope.type),\n },\n \"telegram commands synced\",\n );\n}\n\nexport async function syncTelegramCommandsForChat(\n api: Pick<Bot[\"api\"], \"setMyCommands\">,\n chatId: number,\n language: BotLanguage,\n): Promise<void> {\n await api.setMyCommands(\n getTelegramCommands(language),\n {\n scope: {\n type: \"chat\",\n chat_id: chatId,\n },\n },\n );\n}\n","import type { SessionRepository } from \"../domain/session/session.repository.js\";\nimport {\n DEFAULT_BOT_LANGUAGE,\n getBotCopy,\n normalizeBotLanguage,\n type BotCopy,\n type BotLanguage,\n} from \"./copy.js\";\n\nexport async function getChatLanguage(\n sessionRepo: SessionRepository,\n chatId: number | null | undefined,\n): Promise<BotLanguage> {\n if (!chatId) {\n return DEFAULT_BOT_LANGUAGE;\n }\n\n const binding = await sessionRepo.getByChatId(chatId);\n\n return normalizeBotLanguage(binding?.language);\n}\n\nexport async function getChatCopy(\n sessionRepo: SessionRepository,\n chatId: number | null | undefined,\n): Promise<BotCopy> {\n return getBotCopy(await getChatLanguage(sessionRepo, chatId));\n}\n\nexport async function setChatLanguage(\n sessionRepo: SessionRepository,\n chatId: number,\n language: BotLanguage,\n): Promise<void> {\n const binding = await sessionRepo.getByChatId(chatId);\n\n await sessionRepo.setCurrent({\n chatId,\n sessionId: binding?.sessionId ?? null,\n projectId: binding?.projectId ?? null,\n directory: binding?.directory ?? null,\n agentName: binding?.agentName ?? null,\n modelProviderId: binding?.modelProviderId ?? null,\n modelId: binding?.modelId ?? null,\n modelVariant: binding?.modelVariant ?? null,\n language,\n updatedAt: new Date().toISOString(),\n });\n}\n","import { InlineKeyboard } from \"grammy\";\nimport {\n BOT_COPY,\n getLanguageLabel,\n SUPPORTED_BOT_LANGUAGES,\n type BotCopy,\n type BotLanguage,\n} from \"../copy.js\";\nimport type {\n OpenCodeAgent,\n OpenCodeConfiguredModel,\n OpenCodeSession,\n} from \"../../services/opencode/opencode.client.js\";\n\nexport const MODELS_PAGE_SIZE = 10;\nexport const AGENTS_PAGE_SIZE = 10;\nexport const SESSIONS_PAGE_SIZE = 10;\nconst NUMBERED_BUTTONS_PER_ROW = 5;\n\nexport interface ModelsPage {\n items: OpenCodeConfiguredModel[];\n page: number;\n startIndex: number;\n totalPages: number;\n}\n\nexport interface AgentsPage {\n items: OpenCodeAgent[];\n page: number;\n startIndex: number;\n totalPages: number;\n}\n\nexport interface SessionsPage {\n items: OpenCodeSession[];\n page: number;\n startIndex: number;\n totalPages: number;\n}\n\nexport function buildModelsKeyboard(\n models: OpenCodeConfiguredModel[],\n requestedPage: number,\n copy: BotCopy = BOT_COPY,\n) {\n const page = getModelsPage(models, requestedPage);\n const keyboard = buildNumberedKeyboard(\n page.items,\n page.startIndex,\n (_, index) => `model:pick:${page.startIndex + index + 1}`,\n );\n\n appendPaginationButtons(keyboard, page.page, page.totalPages, \"model:page\", copy);\n\n return { keyboard, page };\n}\n\nexport function buildAgentsKeyboard(\n agents: OpenCodeAgent[],\n requestedPage: number,\n copy: BotCopy = BOT_COPY,\n) {\n const page = getAgentsPage(agents, requestedPage);\n const keyboard = buildNumberedKeyboard(\n page.items,\n page.startIndex,\n (_, index) => `agents:select:${page.startIndex + index + 1}`,\n );\n\n appendPaginationButtons(keyboard, page.page, page.totalPages, \"agents:page\", copy);\n\n return { keyboard, page };\n}\n\nexport function buildSessionsKeyboard(\n sessions: OpenCodeSession[],\n requestedPage: number,\n copy: BotCopy = BOT_COPY,\n) {\n const page = getSessionsPage(sessions, requestedPage);\n const keyboard = buildNumberedKeyboard(\n page.items,\n page.startIndex,\n (session) => `sessions:pick:${page.page}:${session.id}`,\n );\n\n appendPaginationButtons(keyboard, page.page, page.totalPages, \"sessions:page\", copy);\n\n return { keyboard, page };\n}\n\nexport function buildSessionActionKeyboard(\n sessionId: string,\n page: number,\n copy: BotCopy = BOT_COPY,\n) {\n return new InlineKeyboard()\n .text(copy.sessions.switchAction, `sessions:switch:${page}:${sessionId}`)\n .text(copy.sessions.renameAction, `sessions:rename:${page}:${sessionId}`)\n .row()\n .text(copy.sessions.backToList, `sessions:back:${page}`);\n}\n\nexport function buildModelVariantsKeyboard(variants: string[], modelIndex: number) {\n return buildNumberedKeyboard(\n variants,\n 0,\n (_, index) => `model:variant:${modelIndex}:${index + 1}`,\n );\n}\n\nexport function buildLanguageKeyboard(currentLanguage: BotLanguage, copy: BotCopy = BOT_COPY) {\n const keyboard = new InlineKeyboard();\n\n SUPPORTED_BOT_LANGUAGES.forEach((language, index) => {\n const label = currentLanguage === language\n ? `[${getLanguageLabel(language, copy)}]`\n : getLanguageLabel(language, copy);\n\n keyboard.text(label, `language:select:${language}`);\n\n if (index !== SUPPORTED_BOT_LANGUAGES.length - 1) {\n keyboard.row();\n }\n });\n\n return keyboard;\n}\n\nexport function getModelsPage(models: OpenCodeConfiguredModel[], requestedPage: number): ModelsPage {\n return getPagedItems(models, requestedPage, MODELS_PAGE_SIZE);\n}\n\nexport function getAgentsPage(agents: OpenCodeAgent[], requestedPage: number): AgentsPage {\n return getPagedItems(agents, requestedPage, AGENTS_PAGE_SIZE);\n}\n\nexport function getSessionsPage(sessions: OpenCodeSession[], requestedPage: number): SessionsPage {\n return getPagedItems(sessions, requestedPage, SESSIONS_PAGE_SIZE);\n}\n\nfunction buildNumberedKeyboard<T>(\n items: T[],\n startIndex: number,\n buildCallbackData: (item: T, index: number) => string,\n) {\n const keyboard = new InlineKeyboard();\n\n items.forEach((item, index) => {\n const displayIndex = startIndex + index + 1;\n\n keyboard.text(`${displayIndex}`, buildCallbackData(item, index));\n\n if (\n index !== items.length - 1\n && (index + 1) % NUMBERED_BUTTONS_PER_ROW === 0\n ) {\n keyboard.row();\n }\n });\n\n return keyboard;\n}\n\nfunction appendPaginationButtons(\n keyboard: InlineKeyboard,\n page: number,\n totalPages: number,\n prefix: \"agents:page\" | \"sessions:page\" | \"model:page\",\n copy: BotCopy,\n) {\n if (totalPages <= 1) {\n return;\n }\n\n if (page > 0) {\n keyboard.text(copy.common.previousPage, `${prefix}:${page - 1}`);\n }\n\n if (page < totalPages - 1) {\n keyboard.text(copy.common.nextPage, `${prefix}:${page + 1}`);\n }\n}\n\nfunction getPagedItems<T>(items: T[], requestedPage: number, pageSize: number) {\n const totalPages = Math.max(1, Math.ceil(items.length / pageSize));\n const page = clampPage(requestedPage, totalPages);\n const startIndex = page * pageSize;\n\n return {\n items: items.slice(startIndex, startIndex + pageSize),\n page,\n startIndex,\n totalPages,\n };\n}\n\nfunction clampPage(page: number, totalPages: number): number {\n if (!Number.isInteger(page) || page < 0) {\n return 0;\n }\n\n return Math.min(page, totalPages - 1);\n}\n","import { BOT_COPY, type BotCopy } from \"../copy.js\";\n\ninterface PresentedError {\n cause: string | null;\n message: string;\n}\n\nexport function presentError(error: unknown, copy: BotCopy = BOT_COPY): string {\n const presented = normalizeError(error, copy);\n\n return presented.cause\n ? `${presented.message}\\n${copy.errors.causeLabel}: ${presented.cause}`\n : presented.message;\n}\n\nfunction normalizeError(error: unknown, copy: BotCopy): PresentedError {\n if (isNamedError(error, \"ProviderAuthError\")) {\n return {\n message: copy.errors.providerAuth,\n cause: extractMessage(error.data) ?? null,\n };\n }\n\n if (isNamedError(error, \"MessageAbortedError\")) {\n return {\n message: copy.errors.requestAborted,\n cause: extractMessage(error.data) ?? null,\n };\n }\n\n if (isNamedError(error, \"OpenCodePromptTimeoutError\")) {\n return {\n message: copy.errors.promptTimeout,\n cause: null,\n };\n }\n\n if (isNamedError(error, \"StructuredOutputError\")) {\n return {\n message: copy.errors.structuredOutput,\n cause: joinNonEmptyParts([\n extractMessage(error.data),\n extractRetries(error.data),\n ]),\n };\n }\n\n if (isNamedError(error, \"ImageFileDownloadError\")) {\n return {\n message: copy.errors.imageDownload,\n cause: extractMessage(error.data) ?? null,\n };\n }\n\n if (isNamedError(error, \"ImageMessageUnsupportedError\")) {\n return {\n message: copy.errors.imageUnsupported,\n cause: extractMessage(error.data) ?? null,\n };\n }\n\n if (isNamedError(error, \"MessageOutputLengthError\")) {\n return {\n message: copy.errors.outputLength,\n cause: extractMessage(error.data) ?? null,\n };\n }\n\n if (isNamedError(error, \"ContextOverflowError\")) {\n return {\n message: copy.errors.contextOverflow,\n cause: extractMessage(error.data) ?? null,\n };\n }\n\n if (isNamedError(error, \"APIError\")) {\n return {\n message: copy.errors.providerRequest,\n cause: joinNonEmptyParts([\n extractMessage(error.data),\n extractStatusCode(error.data, copy),\n extractRetryable(error.data, copy),\n ]),\n };\n }\n\n if (isNamedError(error, \"NotFoundError\")) {\n return {\n message: copy.errors.notFound,\n cause: extractMessage(error.data) ?? null,\n };\n }\n\n if (isBadRequestError(error)) {\n return {\n message: copy.errors.badRequest,\n cause: extractBadRequestCause(error),\n };\n }\n\n if (error instanceof Error) {\n return {\n message: error.name === \"AbortError\" ? copy.errors.requestAborted : copy.errors.unexpected,\n cause: error.message || null,\n };\n }\n\n return {\n message: copy.errors.unexpected,\n cause: extractMessage(error) ?? stringifyUnknown(error),\n };\n}\n\nfunction isBadRequestError(error: unknown): error is { data?: unknown; errors?: unknown[]; success: false } {\n return !!error\n && typeof error === \"object\"\n && \"success\" in error\n && (error as { success?: unknown }).success === false;\n}\n\nfunction isNamedError<TName extends string>(\n error: unknown,\n name: TName,\n): error is { data?: unknown; name: TName } {\n return !!error\n && typeof error === \"object\"\n && \"name\" in error\n && (error as { name?: unknown }).name === name;\n}\n\nfunction extractBadRequestCause(error: { data?: unknown; errors?: unknown[] }): string | null {\n const directMessage = extractMessage(error.data);\n\n if (directMessage) {\n return directMessage;\n }\n\n const firstValidationError = Array.isArray(error.errors) && error.errors.length > 0\n ? stringifyUnknown(error.errors[0])\n : null;\n\n return firstValidationError;\n}\n\nfunction extractMessage(value: unknown): string | null {\n if (!value) {\n return null;\n }\n\n if (typeof value === \"string\") {\n return value.trim() || null;\n }\n\n if (typeof value === \"object\" && \"message\" in value) {\n const message = (value as { message?: unknown }).message;\n\n return typeof message === \"string\" && message.trim().length > 0\n ? message.trim()\n : null;\n }\n\n return null;\n}\n\nfunction extractRetries(value: unknown): string | null {\n if (!value || typeof value !== \"object\" || !(\"retries\" in value)) {\n return null;\n }\n\n const retries = (value as { retries?: unknown }).retries;\n\n return typeof retries === \"number\" && Number.isFinite(retries)\n ? `retries: ${Math.round(retries)}`\n : null;\n}\n\nfunction extractRetryable(value: unknown, copy: BotCopy): string | null {\n if (!value || typeof value !== \"object\" || !(\"isRetryable\" in value)) {\n return null;\n }\n\n const isRetryable = (value as { isRetryable?: unknown }).isRetryable;\n\n return typeof isRetryable === \"boolean\"\n ? `${copy.errors.retryableLabel}: ${isRetryable ? \"yes\" : \"no\"}`\n : null;\n}\n\nfunction extractStatusCode(value: unknown, copy: BotCopy): string | null {\n if (!value || typeof value !== \"object\" || !(\"statusCode\" in value)) {\n return null;\n }\n\n const statusCode = (value as { statusCode?: unknown }).statusCode;\n\n return typeof statusCode === \"number\" && Number.isFinite(statusCode)\n ? `${copy.errors.statusCodeLabel}: ${Math.round(statusCode)}`\n : null;\n}\n\nfunction joinNonEmptyParts(parts: Array<string | null | undefined>): string | null {\n const filtered = parts\n .map((part) => part?.trim())\n .filter((part): part is string => !!part);\n\n return filtered.length > 0 ? filtered.join(\" | \") : null;\n}\n\nfunction stringifyUnknown(value: unknown): string | null {\n if (value === null || value === undefined) {\n return null;\n }\n\n if (typeof value === \"string\") {\n return value.trim() || null;\n }\n\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n\n try {\n const text = JSON.stringify(value);\n\n return text && text !== \"{}\" ? text : null;\n } catch {\n return null;\n }\n}\n","import type {\n OpenCodeAgent,\n OpenCodeConfiguredModel,\n OpenCodeHealth,\n OpenCodeLspStatus,\n OpenCodeMcpStatus,\n OpenCodePath,\n OpenCodeSession,\n} from \"../../services/opencode/opencode.client.js\";\nimport { OPENCODE_TBOT_VERSION } from \"../../app/package-info.js\";\nimport type { GetStatusResult } from \"../../use-cases/get-status.usecase.js\";\nimport {\n BOT_COPY,\n getLanguageLabel,\n type BotCopy,\n type BotLanguage,\n} from \"../copy.js\";\nimport { presentError } from \"./error.presenter.js\";\nimport {\n getAgentsPage,\n getModelsPage,\n getSessionsPage,\n} from \"./keyboard.presenter.js\";\n\ninterface ModelsMessageInput {\n currentModelId: string | null;\n currentModelProviderId: string | null;\n currentModelVariant: string | null;\n models: OpenCodeConfiguredModel[];\n page: number;\n}\n\ninterface AgentsMessageInput {\n agents: OpenCodeAgent[];\n currentAgentName: string | null;\n page: number;\n}\n\ninterface SessionsMessageInput {\n currentDirectory: string;\n currentSessionId: string | null;\n page: number;\n sessions: OpenCodeSession[];\n}\n\ninterface McpMessageInput {\n currentDirectory: string;\n statuses: Array<{\n name: string;\n status: OpenCodeMcpStatus;\n }>;\n}\n\ninterface LspMessageInput {\n currentDirectory: string;\n statuses: OpenCodeLspStatus[];\n}\n\ninterface StatusLayoutCopy {\n connectivityLabel: string;\n currentProjectLabel: string;\n currentSessionLabel: string;\n defaultSessionValue: string;\n detailsLabel: string;\n errorStatus: string;\n lspTitle: string;\n mcpFailedStatus: string;\n mcpNotesLabel: string;\n mcpRegistrationRequiredStatus: string;\n mcpTitle: string;\n noPluginsMessage: string;\n noneStatus: string;\n openCodeVersionLabel: string;\n overviewTitle: string;\n pluginsTitle: string;\n statusLabel: string;\n tbotVersionLabel: string;\n workspaceTitle: string;\n}\n\ninterface StatusDetailLine {\n codeValue?: boolean;\n label: string;\n value: string;\n}\n\nconst VARIANT_ORDER = [\"minimal\", \"none\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\n\nexport function presentHealthMessage(health: OpenCodeHealth, copy: BotCopy = BOT_COPY): string {\n return [\n copy.health.title,\n copy.health.status(health.healthy),\n copy.health.version(health.version),\n ].join(\"\\n\");\n}\n\nexport function presentPathMessage(path: OpenCodePath, copy: BotCopy = BOT_COPY): string {\n return copy.path.directory(path.directory);\n}\n\nexport function presentStatusMessage(input: GetStatusResult, copy: BotCopy = BOT_COPY): string {\n const layout = getStatusLayoutCopy(copy);\n const sections = [\n presentStatusPlainSection(layout.overviewTitle, presentStatusPlainOverviewLines(input, copy, layout)),\n presentStatusPlainSection(layout.workspaceTitle, presentStatusPlainWorkspaceLines(input, copy, layout)),\n presentStatusPlainSection(layout.pluginsTitle, presentStatusPlainPluginLines(input, copy, layout)),\n presentStatusPlainSection(layout.mcpTitle, presentStatusPlainMcpLines(input, copy, layout)),\n presentStatusPlainSection(layout.lspTitle, presentStatusPlainLspLines(input, copy, layout)),\n ];\n\n return presentStatusSections(sections);\n}\n\nexport function presentStatusMarkdownMessage(input: GetStatusResult, copy: BotCopy = BOT_COPY): string {\n const layout = getStatusLayoutCopy(copy);\n const sections = [\n presentStatusMarkdownSection(layout.overviewTitle, presentStatusMarkdownOverviewLines(input, copy, layout)),\n presentStatusMarkdownSection(layout.workspaceTitle, presentStatusMarkdownWorkspaceLines(input, copy, layout)),\n presentStatusMarkdownSection(layout.pluginsTitle, presentStatusMarkdownPluginLines(input, copy, layout)),\n presentStatusMarkdownSection(layout.mcpTitle, presentStatusMarkdownMcpLines(input, copy, layout)),\n presentStatusMarkdownSection(layout.lspTitle, presentStatusMarkdownLspLines(input, copy, layout)),\n ];\n\n return presentStatusSections(sections);\n}\n\nexport function presentMcpMessage(input: McpMessageInput, copy: BotCopy = BOT_COPY): string {\n const lines = [\n copy.mcp.title,\n copy.mcp.currentProject(input.currentDirectory),\n \"\",\n ];\n\n if (input.statuses.length === 0) {\n lines.push(copy.mcp.none);\n return lines.join(\"\\n\");\n }\n\n lines.push(...input.statuses.map(({ name, status }) => `${name}: ${formatMcpStatus(status, copy)}`));\n\n return lines.join(\"\\n\");\n}\n\nexport function presentLspMessage(input: LspMessageInput, copy: BotCopy = BOT_COPY): string {\n const lines = [\n copy.lsp.title,\n copy.lsp.currentProject(input.currentDirectory),\n \"\",\n ];\n\n if (input.statuses.length === 0) {\n lines.push(copy.lsp.none);\n return lines.join(\"\\n\");\n }\n\n lines.push(...input.statuses.map((status) => `${status.name}: ${formatLspStatus(status, copy)}`));\n\n return lines.join(\"\\n\");\n}\n\nfunction presentStatusSections(sections: string[]): string {\n return sections.flatMap((section, index) => index === 0 ? [section] : [\"\", section]).join(\"\\n\");\n}\n\nfunction presentStatusPlainSection(title: string, lines: string[]): string {\n return [\n title,\n ...lines,\n ].join(\"\\n\");\n}\n\nfunction presentStatusMarkdownSection(title: string, lines: string[]): string {\n return [\n `## ${title}`,\n ...lines,\n ].join(\"\\n\");\n}\n\nfunction presentStatusPlainOverviewLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n const lines = [\n presentPlainStatusBullet(\n layout.connectivityLabel,\n input.health.status === \"error\"\n ? layout.errorStatus\n : formatHealthBadge(input.health.data.healthy, layout),\n ),\n ];\n\n if (input.health.status === \"error\") {\n return [\n ...lines,\n ...presentStatusPlainErrorDetailLines(input.health.error, copy, layout),\n presentPlainStatusBullet(layout.tbotVersionLabel, OPENCODE_TBOT_VERSION),\n ];\n }\n\n return [\n ...lines,\n presentPlainStatusBullet(layout.openCodeVersionLabel, input.health.data.version),\n presentPlainStatusBullet(layout.tbotVersionLabel, OPENCODE_TBOT_VERSION),\n ];\n}\n\nfunction presentStatusMarkdownOverviewLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n const lines = [\n presentMarkdownStatusBullet(\n layout.connectivityLabel,\n input.health.status === \"error\"\n ? layout.errorStatus\n : formatHealthBadge(input.health.data.healthy, layout),\n ),\n ];\n\n if (input.health.status === \"error\") {\n return [\n ...lines,\n ...presentStatusMarkdownErrorDetailLines(input.health.error, copy, layout),\n presentMarkdownStatusBullet(layout.tbotVersionLabel, OPENCODE_TBOT_VERSION),\n ];\n }\n\n return [\n ...lines,\n presentMarkdownStatusBullet(layout.openCodeVersionLabel, input.health.data.version),\n presentMarkdownStatusBullet(layout.tbotVersionLabel, OPENCODE_TBOT_VERSION),\n ];\n}\n\nfunction presentStatusPlainWorkspaceLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.workspace.status === \"error\") {\n return presentStatusPlainErrorLines(input.workspace.error, copy, layout);\n }\n\n return [\n presentPlainStatusBullet(layout.currentProjectLabel, input.workspace.data.currentProject),\n presentPlainStatusBullet(layout.currentSessionLabel, input.workspace.data.currentSession ?? layout.defaultSessionValue),\n ];\n}\n\nfunction presentStatusMarkdownWorkspaceLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.workspace.status === \"error\") {\n return presentStatusMarkdownErrorLines(input.workspace.error, copy, layout);\n }\n\n return [\n presentMarkdownStatusBullet(layout.currentProjectLabel, input.workspace.data.currentProject, {\n codeValue: true,\n }),\n presentMarkdownStatusBullet(layout.currentSessionLabel, input.workspace.data.currentSession ?? layout.defaultSessionValue),\n ];\n}\n\nfunction presentStatusPlainPluginLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.plugins.status === \"error\") {\n return presentStatusPlainErrorLines(input.plugins.error, copy, layout);\n }\n\n if (input.plugins.data.plugins.length === 0) {\n return [\n ...presentPlainEmptyStatusLines(layout.noPluginsMessage, layout),\n ];\n }\n\n return input.plugins.data.plugins.map((plugin) => `- ${plugin}`);\n}\n\nfunction presentStatusMarkdownPluginLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.plugins.status === \"error\") {\n return presentStatusMarkdownErrorLines(input.plugins.error, copy, layout);\n }\n\n if (input.plugins.data.plugins.length === 0) {\n return [\n ...presentMarkdownEmptyStatusLines(layout.noPluginsMessage, layout),\n ];\n }\n\n return input.plugins.data.plugins.map((plugin) => `- \\`${plugin}\\``);\n}\n\nfunction presentStatusPlainLspLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.lsp.status === \"error\") {\n return presentStatusPlainErrorLines(input.lsp.error, copy, layout);\n }\n\n if (input.lsp.data.statuses.length === 0) {\n return presentPlainEmptyStatusLines(copy.lsp.none, layout);\n }\n\n return input.lsp.data.statuses.flatMap((status) => presentPlainStatusGroup(status.name, [\n {\n label: layout.statusLabel,\n value: formatLspStatusBadge(status),\n },\n ]));\n}\n\nfunction presentStatusMarkdownLspLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.lsp.status === \"error\") {\n return presentStatusMarkdownErrorLines(input.lsp.error, copy, layout);\n }\n\n if (input.lsp.data.statuses.length === 0) {\n return presentMarkdownEmptyStatusLines(copy.lsp.none, layout);\n }\n\n return input.lsp.data.statuses.flatMap((status) => presentMarkdownStatusGroup(status.name, [\n {\n label: layout.statusLabel,\n value: formatLspStatusBadge(status),\n },\n ]));\n}\n\nfunction presentStatusPlainMcpLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.mcp.status === \"error\") {\n return presentStatusPlainErrorLines(input.mcp.error, copy, layout);\n }\n\n if (input.mcp.data.statuses.length === 0) {\n return presentPlainEmptyStatusLines(copy.mcp.none, layout);\n }\n\n return input.mcp.data.statuses.flatMap(({ name, status }) =>\n presentPlainStatusGroup(name, getMcpStatusDetailLines(status, copy, layout)),\n );\n}\n\nfunction presentStatusMarkdownMcpLines(input: GetStatusResult, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n if (input.mcp.status === \"error\") {\n return presentStatusMarkdownErrorLines(input.mcp.error, copy, layout);\n }\n\n if (input.mcp.data.statuses.length === 0) {\n return presentMarkdownEmptyStatusLines(copy.mcp.none, layout);\n }\n\n return input.mcp.data.statuses.flatMap(({ name, status }) =>\n presentMarkdownStatusGroup(name, getMcpStatusDetailLines(status, copy, layout), {\n codeName: true,\n }),\n );\n}\n\nfunction presentStatusPlainErrorLines(error: unknown, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n return [\n presentPlainStatusBullet(layout.statusLabel, layout.errorStatus),\n ...presentStatusPlainErrorDetailLines(error, copy, layout),\n ];\n}\n\nfunction presentStatusPlainErrorDetailLines(error: unknown, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n const detailLines = splitStatusLines(presentError(error, copy));\n\n return detailLines.map((line) => presentPlainStatusBullet(layout.detailsLabel, line));\n}\n\nfunction presentStatusMarkdownErrorLines(error: unknown, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n return [\n presentMarkdownStatusBullet(layout.statusLabel, layout.errorStatus),\n ...presentStatusMarkdownErrorDetailLines(error, copy, layout),\n ];\n}\n\nfunction presentStatusMarkdownErrorDetailLines(error: unknown, copy: BotCopy, layout: StatusLayoutCopy): string[] {\n const detailLines = splitStatusLines(presentError(error, copy));\n\n return detailLines.map((line) => presentMarkdownStatusBullet(layout.detailsLabel, line));\n}\n\nfunction presentPlainEmptyStatusLines(message: string, layout: StatusLayoutCopy): string[] {\n return [\n presentPlainStatusBullet(layout.statusLabel, layout.noneStatus),\n presentPlainStatusBullet(layout.detailsLabel, message),\n ];\n}\n\nfunction presentMarkdownEmptyStatusLines(message: string, layout: StatusLayoutCopy): string[] {\n return [\n presentMarkdownStatusBullet(layout.statusLabel, layout.noneStatus),\n presentMarkdownStatusBullet(layout.detailsLabel, message),\n ];\n}\n\nfunction presentPlainStatusGroup(name: string, details: StatusDetailLine[]): string[] {\n return [\n `- ${normalizeStatusInlineValue(name)}`,\n ...details.map((detail) => ` - ${detail.label}: ${formatStatusValue(detail.value)}`),\n ];\n}\n\nfunction presentMarkdownStatusGroup(\n name: string,\n details: StatusDetailLine[],\n options: {\n codeName?: boolean;\n } = {},\n): string[] {\n const renderedName = options.codeName\n ? `\\`${normalizeStatusInlineValue(name)}\\``\n : `**${normalizeStatusInlineValue(name)}**`;\n\n return [\n `- ${renderedName}`,\n ...details.map((detail) => detail.codeValue\n ? ` - **${detail.label}:** \\`${formatStatusValue(detail.value)}\\``\n : ` - **${detail.label}:** ${formatStatusValue(detail.value)}`),\n ];\n}\n\nfunction presentPlainStatusBullet(label: string, value: string): string {\n return `- ${label}: ${formatStatusValue(value)}`;\n}\n\nfunction presentMarkdownStatusBullet(\n label: string,\n value: string,\n options: {\n codeValue?: boolean;\n } = {},\n): string {\n return options.codeValue\n ? `- **${label}:** \\`${formatStatusValue(value)}\\``\n : `- **${label}:** ${formatStatusValue(value)}`;\n}\n\nfunction splitStatusLines(text: string): string[] {\n return text\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n}\n\nfunction formatHealthBadge(healthy: boolean, layout: StatusLayoutCopy): string {\n return healthy ? \"\\uD83D\\uDFE2\" : layout.errorStatus;\n}\n\nfunction formatLspStatusBadge(status: OpenCodeLspStatus): string {\n switch (status.status) {\n case \"connected\":\n return \"\\uD83D\\uDFE2\";\n case \"error\":\n return \"\\uD83D\\uDD34\";\n }\n\n const exhaustiveStatus: never = status.status;\n\n return exhaustiveStatus;\n}\n\nfunction formatMcpStatusBadge(status: OpenCodeMcpStatus, layout: StatusLayoutCopy): string {\n switch (status.status) {\n case \"connected\":\n return \"\\uD83D\\uDFE2\";\n case \"disabled\":\n return \"\\u26AA\";\n case \"needs_auth\":\n return \"\\uD83D\\uDFE1\";\n case \"failed\":\n return layout.mcpFailedStatus;\n case \"needs_client_registration\":\n return layout.mcpRegistrationRequiredStatus;\n }\n\n const exhaustiveStatus: never = status;\n\n return exhaustiveStatus;\n}\n\nfunction getMcpStatusDetailLines(\n status: OpenCodeMcpStatus,\n copy: BotCopy,\n layout: StatusLayoutCopy,\n): StatusDetailLine[] {\n const notes = formatMcpStatusNotes(status, copy, layout);\n\n return notes\n ? [\n {\n label: layout.statusLabel,\n value: formatMcpStatusBadge(status, layout),\n },\n {\n label: layout.mcpNotesLabel,\n value: notes,\n },\n ]\n : [\n {\n label: layout.statusLabel,\n value: formatMcpStatusBadge(status, layout),\n },\n ];\n}\n\nfunction formatMcpStatusNotes(status: OpenCodeMcpStatus, copy: BotCopy, layout: StatusLayoutCopy): string | null {\n switch (status.status) {\n case \"connected\":\n return null;\n case \"disabled\":\n return null;\n case \"needs_auth\":\n return copy.mcp.needsAuth;\n case \"failed\":\n return status.error;\n case \"needs_client_registration\":\n return status.error;\n }\n\n const exhaustiveStatus: never = status;\n\n return exhaustiveStatus;\n}\n\nfunction formatStatusValue(value: string): string {\n const normalized = value\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n .join(\" / \");\n\n return normalized.length > 0 ? normalized : \"-\";\n}\n\nfunction normalizeStatusInlineValue(value: string): string {\n return formatStatusValue(value);\n}\n\nfunction getStatusLayoutCopy(copy: BotCopy): StatusLayoutCopy {\n if (copy.locale === \"en\") {\n return {\n connectivityLabel: \"Connectivity\",\n currentProjectLabel: \"Current Project\",\n currentSessionLabel: \"Current Session\",\n defaultSessionValue: \"OpenCode Default\",\n detailsLabel: \"Details\",\n errorStatus: \"\\uD83D\\uDD34\",\n lspTitle: \"\\uD83E\\uDDE0 LSP\",\n mcpFailedStatus: \"\\uD83D\\uDD34\",\n mcpNotesLabel: \"Notes\",\n mcpRegistrationRequiredStatus: \"\\uD83D\\uDFE1\",\n mcpTitle: \"\\uD83D\\uDD0C MCP\",\n noPluginsMessage: \"No plugins configured in the OpenCode config.\",\n noneStatus: \"\\u26AA\",\n openCodeVersionLabel: \"OpenCode Version\",\n overviewTitle: \"\\uD83D\\uDDA5\\uFE0F Overview\",\n pluginsTitle: \"\\uD83E\\uDDE9 Plugins\",\n statusLabel: \"Status\",\n tbotVersionLabel: \"opencode-tbot Version\",\n workspaceTitle: \"\\uD83D\\uDCC1 Workspace\",\n };\n }\n\n if (copy.locale === \"ja\") {\n return {\n connectivityLabel: \"接続性\",\n currentProjectLabel: \"現在のプロジェクト\",\n currentSessionLabel: \"現在のセッション\",\n defaultSessionValue: \"OpenCode のデフォルト\",\n detailsLabel: \"詳細\",\n errorStatus: \"\\uD83D\\uDD34\",\n lspTitle: \"\\uD83E\\uDDE0 LSP\",\n mcpFailedStatus: \"\\uD83D\\uDD34\",\n mcpNotesLabel: \"補足\",\n mcpRegistrationRequiredStatus: \"\\uD83D\\uDFE1\",\n mcpTitle: \"\\uD83D\\uDD0C MCP\",\n noPluginsMessage: \"現在の OpenCode 設定にはプラグインが設定されていません。\",\n noneStatus: \"\\u26AA\",\n openCodeVersionLabel: \"OpenCode バージョン\",\n overviewTitle: \"\\uD83D\\uDDA5\\uFE0F 概要\",\n pluginsTitle: \"\\uD83E\\uDDE9 プラグイン\",\n statusLabel: \"状態\",\n tbotVersionLabel: \"opencode-tbot バージョン\",\n workspaceTitle: \"\\uD83D\\uDCC1 ワークスペース\",\n };\n }\n\n return {\n connectivityLabel: \"\\u8FDE\\u901A\\u6027\",\n currentProjectLabel: \"\\u5F53\\u524D\\u9879\\u76EE\",\n currentSessionLabel: \"\\u5F53\\u524D\\u4F1A\\u8BDD\",\n defaultSessionValue: \"OpenCode \\u9ED8\\u8BA4\",\n detailsLabel: \"\\u8BE6\\u60C5\",\n errorStatus: \"\\uD83D\\uDD34\",\n lspTitle: \"\\uD83E\\uDDE0 LSP\",\n mcpFailedStatus: \"\\uD83D\\uDD34\",\n mcpNotesLabel: \"\\u8BF4\\u660E\",\n mcpRegistrationRequiredStatus: \"\\uD83D\\uDFE1\",\n mcpTitle: \"\\uD83D\\uDD0C MCP\",\n noPluginsMessage: \"\\u5F53\\u524D OpenCode \\u914D\\u7F6E\\u4E2D\\u672A\\u914D\\u7F6E\\u63D2\\u4EF6\\u3002\",\n noneStatus: \"\\u26AA\",\n openCodeVersionLabel: \"OpenCode\\u7248\\u672C\",\n overviewTitle: \"\\uD83D\\uDDA5\\uFE0F \\u6982\\u89C8\",\n pluginsTitle: \"\\uD83E\\uDDE9 \\u63D2\\u4EF6\",\n statusLabel: \"\\u72B6\\u6001\",\n tbotVersionLabel: \"opencode-tbot\\u7248\\u672C\",\n workspaceTitle: \"\\uD83D\\uDCC1 \\u5DE5\\u4F5C\\u533A\",\n };\n}\n\nexport function presentSessionsMessage(input: SessionsMessageInput, copy: BotCopy = BOT_COPY): string {\n if (input.sessions.length === 0) {\n return copy.sessions.none;\n }\n\n const page = getSessionsPage(input.sessions, input.page);\n const currentSession = input.currentSessionId\n ? input.sessions.find((session) => session.id === input.currentSessionId) ?? null\n : null;\n const lines = [\n copy.sessions.title,\n copy.sessions.currentProject(input.currentDirectory),\n copy.sessions.currentSession(currentSession ? formatSessionLabel(currentSession) : copy.common.notSelected),\n copy.common.page(page.page + 1, page.totalPages),\n \"\",\n ...page.items.map((session, index) => `${page.startIndex + index + 1}. ${formatSessionLabel(session)}`),\n ];\n\n return lines.join(\"\\n\");\n}\n\nexport function presentSessionSwitchMessage(session: OpenCodeSession, copy: BotCopy = BOT_COPY): string {\n return [\n copy.sessions.switched,\n copy.sessions.currentSession(formatSessionLabel(session)),\n ].join(\"\\n\");\n}\n\nexport function presentSessionCreatedMessage(session: OpenCodeSession, copy: BotCopy = BOT_COPY): string {\n return [\n copy.sessions.created,\n copy.sessions.currentSession(formatSessionLabel(session)),\n ].join(\"\\n\");\n}\n\nexport function presentSessionActionsMessage(\n input: { currentDirectory: string; session: OpenCodeSession },\n copy: BotCopy = BOT_COPY,\n): string {\n return [\n copy.sessions.actionTitle,\n copy.sessions.currentProject(input.currentDirectory),\n copy.sessions.selectedSession(formatSessionLabel(input.session)),\n \"\",\n copy.sessions.chooseAction,\n ].join(\"\\n\");\n}\n\nexport function presentSessionRenamePromptMessage(session: OpenCodeSession, copy: BotCopy = BOT_COPY): string {\n return copy.sessions.renamePrompt(formatSessionLabel(session));\n}\n\nexport function presentSessionRenamedMessage(session: OpenCodeSession, copy: BotCopy = BOT_COPY): string {\n return [\n copy.sessions.renamed,\n copy.sessions.currentSession(formatSessionLabel(session)),\n ].join(\"\\n\");\n}\n\nexport function presentAgentsMessage(input: AgentsMessageInput, copy: BotCopy = BOT_COPY): string {\n if (input.agents.length === 0) {\n return copy.agents.none;\n }\n\n const page = getAgentsPage(input.agents, input.page);\n const currentAgent = input.currentAgentName\n ? input.agents.find((agent) => agent.name === input.currentAgentName) ?? null\n : null;\n const lines = [\n copy.agents.title,\n copy.agents.current(currentAgent ? formatAgentLabel(currentAgent) : copy.common.openCodeDefault),\n copy.common.page(page.page + 1, page.totalPages),\n \"\",\n ...page.items.map((agent, index) => `${page.startIndex + index + 1}. ${formatAgentLabel(agent)}`),\n ];\n\n return lines.join(\"\\n\");\n}\n\nexport function presentAgentSwitchMessage(agent: OpenCodeAgent, copy: BotCopy = BOT_COPY): string {\n return [\n copy.agents.switched,\n copy.agents.current(formatAgentLabel(agent)),\n ].join(\"\\n\");\n}\n\nexport function presentModelsMessage(input: ModelsMessageInput, copy: BotCopy = BOT_COPY): string {\n if (input.models.length === 0) {\n return copy.models.none;\n }\n\n const page = getModelsPage(input.models, input.page);\n const currentModel = input.currentModelId && input.currentModelProviderId\n ? input.models.find((model) =>\n model.id === input.currentModelId && model.providerID === input.currentModelProviderId,\n ) ?? null\n : null;\n const currentModelLine = currentModel\n ? formatModelSelection(currentModel, input.currentModelVariant, copy)\n : copy.common.openCodeDefault;\n const lines = [\n copy.models.title,\n copy.models.configuredOnly,\n copy.models.current(currentModelLine),\n copy.common.page(page.page + 1, page.totalPages),\n \"\",\n ...page.items.map((model, index) => formatModelListLine(model, page.startIndex + index + 1)),\n ];\n\n return lines.join(\"\\n\");\n}\n\nexport function presentModelVariantsMessage(\n model: OpenCodeConfiguredModel,\n modelIndex: number,\n copy: BotCopy = BOT_COPY,\n): string {\n const variants = getModelVariants(model);\n\n if (variants.length === 0) {\n return [\n copy.models.noReasoningLevels,\n copy.models.model(formatModelLabel(model)),\n ].join(\"\\n\");\n }\n\n const lines = [\n copy.models.reasoningLevelsTitle,\n copy.models.model(formatModelLabel(model)),\n copy.models.modelNumber(modelIndex),\n \"\",\n ...variants.map((variant, index) => `${index + 1}. ${variant}`),\n ];\n\n return lines.join(\"\\n\");\n}\n\nexport function presentModelSwitchMessage(\n model: OpenCodeConfiguredModel,\n variant: string | null,\n copy: BotCopy = BOT_COPY,\n): string {\n return [\n copy.models.switched,\n copy.models.current(formatModelLabel(model)),\n copy.models.currentReasoningLevel(variant ?? copy.models.defaultReasoningLevel),\n ].join(\"\\n\");\n}\n\nexport function presentLanguageMessage(currentLanguage: BotLanguage, copy: BotCopy = BOT_COPY): string {\n return [\n copy.language.title,\n copy.language.current(getLanguageLabel(currentLanguage, copy)),\n \"\",\n copy.language.choose,\n ].join(\"\\n\");\n}\n\nexport function presentLanguageSwitchMessage(currentLanguage: BotLanguage, copy: BotCopy = BOT_COPY): string {\n return [\n copy.language.switched,\n copy.language.current(getLanguageLabel(currentLanguage, copy)),\n ].join(\"\\n\");\n}\n\nexport function getModelVariants(model: OpenCodeConfiguredModel): string[] {\n return Object.keys(model.variants)\n .sort((left, right) => compareVariantNames(left, right));\n}\n\nfunction compareVariantNames(left: string, right: string): number {\n const leftIndex = VARIANT_ORDER.indexOf(left);\n const rightIndex = VARIANT_ORDER.indexOf(right);\n\n if (leftIndex === -1 && rightIndex === -1) {\n return left.localeCompare(right);\n }\n\n if (leftIndex === -1) {\n return 1;\n }\n\n if (rightIndex === -1) {\n return -1;\n }\n\n return leftIndex - rightIndex;\n}\n\nfunction formatAgentLabel(agent: OpenCodeAgent): string {\n return agent.name;\n}\n\nfunction formatModelListLine(model: OpenCodeConfiguredModel, displayIndex: number): string {\n return `${displayIndex}. ${formatModelLabel(model)}`;\n}\n\nfunction formatModelSelection(\n model: OpenCodeConfiguredModel,\n variant: string | null,\n copy: BotCopy,\n): string {\n return `${formatModelLabel(model)} | ${copy.models.reasoningLevel(variant ?? copy.models.defaultReasoningLevel)}`;\n}\n\nfunction formatModelLabel(model: OpenCodeConfiguredModel): string {\n return `${model.providerName} / ${model.name}`;\n}\n\nfunction formatSessionLabel(session: OpenCodeSession): string {\n const title = session.title.trim() || session.slug || session.id;\n\n return title === session.slug\n ? title\n : `${title} [${session.slug}]`;\n}\n\nfunction formatMcpStatus(status: OpenCodeMcpStatus, copy: BotCopy): string {\n switch (status.status) {\n case \"connected\":\n return copy.mcp.connected;\n case \"disabled\":\n return copy.mcp.disabled;\n case \"needs_auth\":\n return copy.mcp.needsAuth;\n case \"failed\":\n return copy.mcp.failed(status.error);\n case \"needs_client_registration\":\n return copy.mcp.needsClientRegistration(status.error);\n }\n\n const exhaustiveStatus: never = status;\n\n return exhaustiveStatus;\n}\n\nfunction formatLspStatus(status: OpenCodeLspStatus, copy: BotCopy): string {\n switch (status.status) {\n case \"connected\":\n return copy.lsp.connected;\n case \"error\":\n return copy.lsp.error;\n }\n\n const exhaustiveStatus: never = status.status;\n\n return exhaustiveStatus;\n}\n","import type { Bot } from \"grammy\";\nimport { getChatCopy } from \"../i18n.js\";\nimport type { BotDependencies } from \"../types.js\";\nimport { buildAgentsKeyboard } from \"../presenters/keyboard.presenter.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport { presentAgentsMessage } from \"../presenters/message.presenter.js\";\n\ninterface AgentsCommandContext {\n chat: { id: number };\n reply(text: string, other?: { reply_markup?: unknown }): Promise<unknown>;\n}\n\nexport async function handleAgentsCommand(ctx: AgentsCommandContext, dependencies: BotDependencies) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n const result = await dependencies.listAgentsUseCase.execute({\n chatId: ctx.chat.id,\n });\n\n if (result.agents.length === 0) {\n await ctx.reply(copy.agents.none);\n return;\n }\n\n const { keyboard, page } = buildAgentsKeyboard(result.agents, 0, copy);\n\n await ctx.reply(\n presentAgentsMessage({\n agents: result.agents,\n currentAgentName: result.currentAgentName,\n page: page.page,\n }, copy),\n {\n reply_markup: keyboard,\n },\n );\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to list agents\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport function registerAgentsCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command([\"agents\", \"agent\"], async (ctx) => {\n await handleAgentsCommand(ctx, dependencies);\n });\n}\n","import type { OpenCodeSession } from \"../services/opencode/opencode.client.js\";\nimport { getChatCopy } from \"./i18n.js\";\nimport {\n buildSessionActionKeyboard,\n buildSessionsKeyboard,\n} from \"./presenters/keyboard.presenter.js\";\nimport {\n presentSessionActionsMessage,\n presentSessionsMessage,\n} from \"./presenters/message.presenter.js\";\nimport type { BotDependencies } from \"./types.js\";\n\ninterface SessionsMessageApi {\n editMessageText(\n chatId: number,\n messageId: number,\n text: string,\n other?: { reply_markup?: unknown },\n ): Promise<unknown>;\n}\n\ninterface SessionsListView {\n copy: Awaited<ReturnType<typeof getChatCopy>>;\n currentDirectory: string;\n currentSessionId: string | null;\n keyboard?: unknown;\n page: number;\n sessions: OpenCodeSession[];\n text: string;\n}\n\nexport async function buildSessionsListView(\n chatId: number,\n requestedPage: number,\n dependencies: BotDependencies,\n): Promise<SessionsListView> {\n const copy = await getChatCopy(dependencies.sessionRepo, chatId);\n const result = await dependencies.listSessionsUseCase.execute({\n chatId,\n });\n\n if (result.sessions.length === 0) {\n return {\n copy,\n currentDirectory: result.currentDirectory,\n currentSessionId: result.currentSessionId,\n page: 0,\n sessions: [],\n text: copy.sessions.none,\n };\n }\n\n const { keyboard, page } = buildSessionsKeyboard(result.sessions, requestedPage, copy);\n\n return {\n copy,\n currentDirectory: result.currentDirectory,\n currentSessionId: result.currentSessionId,\n keyboard,\n page: page.page,\n sessions: result.sessions,\n text: presentSessionsMessage({\n currentDirectory: result.currentDirectory,\n currentSessionId: result.currentSessionId,\n page: page.page,\n sessions: result.sessions,\n }, copy),\n };\n}\n\nexport async function buildSessionActionView(\n chatId: number,\n requestedPage: number,\n sessionId: string,\n dependencies: BotDependencies,\n) {\n const listView = await buildSessionsListView(chatId, requestedPage, dependencies);\n const session = listView.sessions.find((item) => item.id === sessionId);\n\n if (!session) {\n return {\n copy: listView.copy,\n found: false as const,\n };\n }\n\n return {\n copy: listView.copy,\n found: true as const,\n keyboard: buildSessionActionKeyboard(session.id, listView.page, listView.copy),\n page: listView.page,\n session,\n text: presentSessionActionsMessage({\n currentDirectory: listView.currentDirectory,\n session,\n }, listView.copy),\n };\n}\n\nexport async function restoreSessionsListMessage(\n api: SessionsMessageApi,\n chatId: number,\n messageId: number,\n requestedPage: number,\n dependencies: BotDependencies,\n) {\n const listView = await buildSessionsListView(chatId, requestedPage, dependencies);\n\n if (listView.keyboard) {\n await api.editMessageText(chatId, messageId, listView.text, {\n reply_markup: listView.keyboard,\n });\n } else {\n await api.editMessageText(chatId, messageId, listView.text);\n }\n\n return listView;\n}\n","import type { PendingChatAction, SessionRenamePendingAction } from \"../domain/pending-action/pending-action.entity.js\";\nimport { getChatCopy } from \"./i18n.js\";\nimport { restoreSessionsListMessage } from \"./sessions-menu.js\";\nimport { presentError } from \"./presenters/error.presenter.js\";\nimport { presentSessionRenamedMessage } from \"./presenters/message.presenter.js\";\nimport type { BotDependencies } from \"./types.js\";\n\ninterface SessionRenameApi {\n editMessageText(\n chatId: number,\n messageId: number,\n text: string,\n other?: { reply_markup?: unknown },\n ): Promise<unknown>;\n}\n\ninterface SessionRenameReplyContext {\n api: SessionRenameApi;\n chat: { id: number };\n reply(text: string): Promise<unknown>;\n}\n\ninterface SessionRenameTextContext extends SessionRenameReplyContext {\n message: { text?: string };\n}\n\nexport async function getPendingSessionRenameAction(\n dependencies: BotDependencies,\n chatId: number,\n): Promise<SessionRenamePendingAction | null> {\n const action = await dependencies.pendingActionRepo.getByChatId(chatId);\n\n return isSessionRenamePendingAction(action) ? action : null;\n}\n\nexport async function replyIfSessionRenamePending(\n ctx: Pick<SessionRenameReplyContext, \"chat\" | \"reply\">,\n dependencies: BotDependencies,\n): Promise<boolean> {\n const pendingAction = await getPendingSessionRenameAction(dependencies, ctx.chat.id);\n\n if (!pendingAction) {\n return false;\n }\n\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n await ctx.reply(copy.sessions.renamePendingInput);\n\n return true;\n}\n\nexport async function handlePendingSessionRenameText(\n ctx: SessionRenameTextContext,\n dependencies: BotDependencies,\n): Promise<boolean> {\n const pendingAction = await getPendingSessionRenameAction(dependencies, ctx.chat.id);\n\n if (!pendingAction) {\n return false;\n }\n\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n const title = ctx.message.text?.trim() ?? \"\";\n\n if (title.startsWith(\"/\")) {\n await ctx.reply(copy.sessions.renamePendingInput);\n return true;\n }\n\n if (!title) {\n await ctx.reply(copy.sessions.renameEmpty);\n return true;\n }\n\n try {\n const result = await dependencies.renameSessionUseCase.execute({\n chatId: ctx.chat.id,\n sessionId: pendingAction.sessionId,\n title,\n });\n\n await dependencies.pendingActionRepo.clear(ctx.chat.id);\n\n await bestEffortRestoreSessionsList(ctx.api, pendingAction, dependencies);\n\n if (!result.found) {\n await ctx.reply(copy.sessions.renameExpired);\n return true;\n }\n\n await ctx.reply(presentSessionRenamedMessage(result.session, copy));\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to rename session\");\n await ctx.reply(presentError(error, copy));\n }\n\n return true;\n}\n\nexport async function cancelPendingSessionRename(\n ctx: SessionRenameReplyContext,\n dependencies: BotDependencies,\n): Promise<boolean> {\n const pendingAction = await getPendingSessionRenameAction(dependencies, ctx.chat.id);\n\n if (!pendingAction) {\n return false;\n }\n\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n await dependencies.pendingActionRepo.clear(ctx.chat.id);\n await bestEffortRestoreSessionsList(ctx.api, pendingAction, dependencies);\n await ctx.reply(copy.sessions.renameCancelled);\n\n return true;\n}\n\nasync function bestEffortRestoreSessionsList(\n api: SessionRenameApi,\n pendingAction: SessionRenamePendingAction,\n dependencies: BotDependencies,\n) {\n try {\n await restoreSessionsListMessage(\n api,\n pendingAction.chatId,\n pendingAction.menuMessageId,\n pendingAction.returnPage,\n dependencies,\n );\n } catch (error) {\n dependencies.logger.warn?.({ error }, \"failed to restore sessions list message\");\n }\n}\n\nfunction isSessionRenamePendingAction(action: PendingChatAction | null): action is SessionRenamePendingAction {\n return action?.kind === \"session_rename\";\n}\n","import type { Bot } from \"grammy\";\nimport { getChatCopy } from \"../i18n.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport { cancelPendingSessionRename } from \"../session-rename.js\";\nimport type { BotDependencies } from \"../types.js\";\n\ninterface CancelCommandContext {\n api: {\n editMessageText(\n chatId: number,\n messageId: number,\n text: string,\n other?: { reply_markup?: unknown },\n ): Promise<unknown>;\n };\n chat: { id: number };\n reply(text: string): Promise<unknown>;\n}\n\nexport async function handleCancelCommand(ctx: CancelCommandContext, dependencies: BotDependencies) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n if (await cancelPendingSessionRename(ctx, dependencies)) {\n return;\n }\n\n const result = await dependencies.abortPromptUseCase.execute({\n chatId: ctx.chat.id,\n });\n\n if (result.status === \"no_session\") {\n await ctx.reply(copy.abort.noSession);\n return;\n }\n\n if (result.status === \"not_running\") {\n await ctx.reply(copy.abort.notRunning);\n return;\n }\n\n await ctx.reply(copy.abort.aborted);\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to cancel current action\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport function registerCancelCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command(\"cancel\", async (ctx) => {\n await handleCancelCommand(ctx, dependencies);\n });\n}\n","import type { Bot } from \"grammy\";\nimport { isBotLanguage } from \"../copy.js\";\nimport { getChatCopy, getChatLanguage, setChatLanguage } from \"../i18n.js\";\nimport { buildLanguageKeyboard } from \"../presenters/keyboard.presenter.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport {\n presentLanguageMessage,\n presentLanguageSwitchMessage,\n} from \"../presenters/message.presenter.js\";\nimport { syncTelegramCommandsForChat } from \"./sync-commands.js\";\nimport type { BotDependencies } from \"../types.js\";\n\ninterface LanguageCommandContext {\n chat: { id: number };\n api: Pick<Bot[\"api\"], \"setMyCommands\">;\n reply(text: string, other?: { reply_markup?: unknown }): Promise<unknown>;\n}\n\nexport async function handleLanguageCommand(ctx: LanguageCommandContext, dependencies: BotDependencies) {\n const language = await getChatLanguage(dependencies.sessionRepo, ctx.chat.id);\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n await syncTelegramCommandsForChat(ctx.api, ctx.chat.id, language);\n await ctx.reply(\n presentLanguageMessage(language, copy),\n {\n reply_markup: buildLanguageKeyboard(language, copy),\n },\n );\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to show language options\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport async function switchLanguageForChat(\n api: Pick<Bot[\"api\"], \"setMyCommands\">,\n chatId: number,\n language: string,\n dependencies: BotDependencies,\n): Promise<\n | { found: false; copy: Awaited<ReturnType<typeof getChatCopy>> }\n | {\n found: true;\n copy: Awaited<ReturnType<typeof getChatCopy>>;\n language: Awaited<ReturnType<typeof getChatLanguage>>;\n }\n> {\n const currentCopy = await getChatCopy(dependencies.sessionRepo, chatId);\n\n if (!isBotLanguage(language)) {\n return {\n found: false,\n copy: currentCopy,\n };\n }\n\n await setChatLanguage(dependencies.sessionRepo, chatId, language);\n await syncTelegramCommandsForChat(api, chatId, language);\n\n return {\n found: true,\n copy: await getChatCopy(dependencies.sessionRepo, chatId),\n language,\n };\n}\n\nexport async function presentLanguageSwitchForChat(\n chatId: number,\n api: Pick<Bot[\"api\"], \"setMyCommands\">,\n language: string,\n dependencies: BotDependencies,\n) {\n const result = await switchLanguageForChat(api, chatId, language, dependencies);\n\n if (!result.found) {\n return {\n found: false as const,\n copy: result.copy,\n text: result.copy.language.expired,\n keyboard: buildLanguageKeyboard(await getChatLanguage(dependencies.sessionRepo, chatId), result.copy),\n };\n }\n\n return {\n found: true as const,\n copy: result.copy,\n text: presentLanguageSwitchMessage(result.language, result.copy),\n keyboard: buildLanguageKeyboard(result.language, result.copy),\n };\n}\n\nexport function registerLanguageCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command(\"language\", async (ctx) => {\n await handleLanguageCommand(ctx, dependencies);\n });\n}\n","import type { Bot } from \"grammy\";\nimport { getChatCopy } from \"../i18n.js\";\nimport type { BotDependencies } from \"../types.js\";\nimport { buildModelsKeyboard } from \"../presenters/keyboard.presenter.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport { presentModelsMessage } from \"../presenters/message.presenter.js\";\n\ninterface ModelsCommandContext {\n chat: { id: number };\n reply(text: string, other?: { reply_markup?: unknown }): Promise<unknown>;\n}\n\nexport async function handleModelsCommand(ctx: ModelsCommandContext, dependencies: BotDependencies) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n const result = await dependencies.listModelsUseCase.execute({\n chatId: ctx.chat.id,\n });\n\n if (result.models.length === 0) {\n await ctx.reply(copy.models.none);\n return;\n }\n\n const { keyboard, page } = buildModelsKeyboard(result.models, 0, copy);\n\n await ctx.reply(\n presentModelsMessage({\n currentModelId: result.currentModelId,\n currentModelProviderId: result.currentModelProviderId,\n currentModelVariant: result.currentModelVariant,\n models: result.models,\n page: page.page,\n }, copy),\n {\n reply_markup: keyboard,\n },\n );\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to list models\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport function registerModelsCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command([\"model\", \"models\"], async (ctx) => {\n await handleModelsCommand(ctx, dependencies);\n });\n}\n","import type { Bot } from \"grammy\";\nimport { getChatCopy } from \"../i18n.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport { presentSessionCreatedMessage } from \"../presenters/message.presenter.js\";\nimport type { BotDependencies } from \"../types.js\";\n\ninterface NewCommandContext {\n chat: { id: number };\n message?: { text?: string };\n match?: string;\n reply(text: string): Promise<unknown>;\n}\n\nexport async function handleNewCommand(ctx: NewCommandContext, dependencies: BotDependencies) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n const title = extractSessionTitle(ctx);\n const result = await dependencies.createSessionUseCase.execute({\n chatId: ctx.chat.id,\n title,\n });\n\n await ctx.reply(presentSessionCreatedMessage(result.session, copy));\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to create new session\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport function registerNewCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command(\"new\", async (ctx) => {\n await handleNewCommand(ctx, dependencies);\n });\n}\n\nfunction extractSessionTitle(ctx: NewCommandContext): string | null {\n if (typeof ctx.match === \"string\") {\n const title = ctx.match.trim();\n\n return title ? title : null;\n }\n\n const messageText = ctx.message?.text?.trim();\n\n if (!messageText) {\n return null;\n }\n\n const match = messageText.match(/^\\/new(?:@\\S+)?(?:\\s+([\\s\\S]*))?$/i);\n const title = match?.[1]?.trim();\n\n return title ? title : null;\n}\n","import { BOT_COPY, type BotCopy } from \"../../bot/copy.js\";\nimport type {\n PromptSessionMetrics,\n PromptSessionResult,\n} from \"../opencode/opencode.client.js\";\n\nconst MAX_TELEGRAM_MESSAGE_LENGTH = 4096;\nconst TRUNCATED_SUFFIX = \"...\";\nconst MARKDOWN_SPECIAL_CHARACTERS = /([_*\\[\\]()~`>#+\\-=|{}.!\\\\])/g;\n\nexport interface TelegramReplyPayload {\n options?: {\n parse_mode: \"MarkdownV2\";\n };\n text: string;\n}\n\nexport interface TelegramPromptReply {\n fallback: TelegramReplyPayload;\n preferred: TelegramReplyPayload;\n}\n\nexport interface TelegramStaticReply {\n fallback: TelegramReplyPayload;\n preferred: TelegramReplyPayload;\n}\n\nexport function buildTelegramPromptReply(\n result: PromptSessionResult,\n copy: BotCopy = BOT_COPY,\n): TelegramPromptReply {\n const renderedMarkdown = result.bodyMd ? renderMarkdownToTelegramMarkdownV2(result.bodyMd) : null;\n const footerPlain = formatPlainMetricsFooter(result.metrics, copy);\n const plainBody = normalizePlainBody(result, renderedMarkdown !== null, copy);\n const fallback = {\n text: joinBodyAndFooter(\n truncatePlainBody(plainBody, footerPlain),\n footerPlain,\n ),\n };\n\n if (!renderedMarkdown) {\n return {\n preferred: fallback,\n fallback,\n };\n }\n\n const footerMarkdown = formatMarkdownMetricsFooter(result.metrics, copy);\n const markdownText = joinBodyAndFooter(renderedMarkdown, footerMarkdown);\n\n if (markdownText.length > MAX_TELEGRAM_MESSAGE_LENGTH) {\n return {\n preferred: fallback,\n fallback,\n };\n }\n\n return {\n preferred: {\n text: markdownText,\n options: {\n parse_mode: \"MarkdownV2\",\n },\n },\n fallback,\n };\n}\n\nexport function buildTelegramStaticReply(markdown: string): TelegramStaticReply {\n const renderedMarkdown = renderMarkdownToTelegramMarkdownV2(markdown);\n const plainText = truncateStaticText(stripMarkdownToPlainText(markdown) || markdown.trim());\n const fallback = {\n text: plainText,\n };\n\n if (!renderedMarkdown || renderedMarkdown.length > MAX_TELEGRAM_MESSAGE_LENGTH) {\n return {\n preferred: fallback,\n fallback,\n };\n }\n\n return {\n preferred: {\n text: renderedMarkdown,\n options: {\n parse_mode: \"MarkdownV2\",\n },\n },\n fallback,\n };\n}\n\nexport function renderMarkdownToTelegramMarkdownV2(markdown: string): string | null {\n const normalizedMarkdown = preprocessMarkdownForTelegram(markdown).trim();\n\n if (!normalizedMarkdown) {\n return null;\n }\n\n const lines = normalizedMarkdown.split(\"\\n\");\n const rendered: string[] = [];\n let inCodeBlock = false;\n let codeBlockLanguage = \"\";\n\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index];\n const fenceMatch = line.match(/^```([A-Za-z0-9_-]+)?\\s*$/);\n\n if (fenceMatch) {\n if (inCodeBlock) {\n rendered.push(\"```\");\n inCodeBlock = false;\n codeBlockLanguage = \"\";\n } else {\n codeBlockLanguage = fenceMatch[1] ?? \"\";\n rendered.push(`\\`\\`\\`${codeBlockLanguage}`);\n inCodeBlock = true;\n }\n\n continue;\n }\n\n if (inCodeBlock) {\n rendered.push(escapeCodeContent(line));\n continue;\n }\n\n if (line.includes(\"![\")) {\n return null;\n }\n\n const tableBlock = consumeMarkdownTable(lines, index);\n\n if (tableBlock) {\n rendered.push(renderTableAsTelegramCodeBlock(tableBlock.rows));\n index = tableBlock.nextIndex - 1;\n continue;\n }\n\n if (/<[/A-Za-z][^>]*>/.test(line)) {\n return null;\n }\n\n if (!line.trim()) {\n rendered.push(\"\");\n continue;\n }\n\n const headingMatch = line.match(/^\\s{0,3}(#{1,6})\\s+(.+)$/);\n\n if (headingMatch) {\n rendered.push(`*${escapeMarkdownText(headingMatch[2].trim())}*`);\n continue;\n }\n\n const blockquoteMatch = line.match(/^\\s*>\\s?(.*)$/);\n\n if (blockquoteMatch) {\n const quote = renderInlineMarkdown(blockquoteMatch[1]);\n\n if (quote === null) {\n return null;\n }\n\n rendered.push(`>${quote ? ` ${quote}` : \"\"}`);\n continue;\n }\n\n const unorderedListMatch = line.match(/^(\\s*)[-+*]\\s+(.+)$/);\n\n if (unorderedListMatch) {\n const item = renderInlineMarkdown(unorderedListMatch[2]);\n\n if (item === null) {\n return null;\n }\n\n rendered.push(`${unorderedListMatch[1]}\\\\- ${item}`);\n continue;\n }\n\n const orderedListMatch = line.match(/^(\\s*)(\\d+)[.)]\\s+(.+)$/);\n\n if (orderedListMatch) {\n const item = renderInlineMarkdown(orderedListMatch[3]);\n\n if (item === null) {\n return null;\n }\n\n rendered.push(`${orderedListMatch[1]}${orderedListMatch[2]}\\\\. ${item}`);\n continue;\n }\n\n const paragraph = renderInlineMarkdown(line);\n\n if (paragraph === null) {\n return null;\n }\n\n rendered.push(paragraph);\n }\n\n if (inCodeBlock) {\n return null;\n }\n\n return rendered.join(\"\\n\");\n}\n\nexport function stripMarkdownToPlainText(markdown: string): string {\n const normalizedMarkdown = preprocessMarkdownForTelegram(markdown);\n const lines = normalizedMarkdown.split(\"\\n\");\n const rendered: string[] = [];\n\n for (let index = 0; index < lines.length; index += 1) {\n const tableBlock = consumeMarkdownTable(lines, index);\n\n if (tableBlock) {\n rendered.push(renderTableAsPlainText(tableBlock.rows));\n index = tableBlock.nextIndex - 1;\n continue;\n }\n\n rendered.push(lines[index]);\n }\n\n return rendered\n .join(\"\\n\")\n .replace(/```[A-Za-z0-9_-]*\\n?/g, \"\")\n .replace(/```/g, \"\")\n .replace(/^#{1,6}\\s+/gm, \"\")\n .replace(/^\\s*>\\s?/gm, \"\")\n .replace(/^(\\s*)[-+*]\\s+/gm, \"$1- \")\n .replace(/^(\\s*)(\\d+)[.)]\\s+/gm, \"$1$2. \")\n .replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, \"$1 ($2)\")\n .replace(/(\\*\\*|__)(.*?)\\1/g, \"$2\")\n .replace(/(\\*|_)(.*?)\\1/g, \"$2\")\n .replace(/`([^`]+)`/g, \"$1\")\n .trim();\n}\n\nfunction normalizePlainBody(\n result: PromptSessionResult,\n preferStructured: boolean,\n copy: BotCopy,\n): string {\n const fromStructured = result.bodyMd ? stripMarkdownToPlainText(result.bodyMd) : \"\";\n const fromFallback = result.fallbackText.trim();\n const body = preferStructured\n ? fromStructured || fromFallback\n : fromFallback || fromStructured;\n\n return body.trim() || result.fallbackText || copy.prompt.emptyResponse;\n}\n\nfunction truncatePlainBody(body: string, footer: string): string {\n const reservedLength = footer.length + 2;\n const maxBodyLength = Math.max(0, MAX_TELEGRAM_MESSAGE_LENGTH - reservedLength);\n\n if (body.length <= maxBodyLength) {\n return body;\n }\n\n return `${body.slice(0, Math.max(0, maxBodyLength - TRUNCATED_SUFFIX.length))}${TRUNCATED_SUFFIX}`;\n}\n\nfunction truncateStaticText(text: string): string {\n if (text.length <= MAX_TELEGRAM_MESSAGE_LENGTH) {\n return text;\n }\n\n return `${text.slice(0, Math.max(0, MAX_TELEGRAM_MESSAGE_LENGTH - TRUNCATED_SUFFIX.length))}${TRUNCATED_SUFFIX}`;\n}\n\nfunction joinBodyAndFooter(body: string, footer: string): string {\n return `${body}\\n\\n${footer}`;\n}\n\nfunction preprocessMarkdownForTelegram(markdown: string): string {\n const normalizedMarkdown = markdown.replace(/\\r\\n?/g, \"\\n\");\n const lines = normalizedMarkdown.split(\"\\n\");\n const processed: string[] = [];\n let activeFence: \"markdown\" | \"plain\" | null = null;\n\n for (const line of lines) {\n const fenceMatch = line.match(/^```([A-Za-z0-9_-]+)?\\s*$/);\n\n if (fenceMatch) {\n const language = (fenceMatch[1] ?? \"\").toLowerCase();\n\n if (activeFence === \"markdown\") {\n activeFence = null;\n continue;\n }\n\n if (activeFence === \"plain\") {\n processed.push(line);\n activeFence = null;\n continue;\n }\n\n if (language === \"md\" || language === \"markdown\") {\n activeFence = \"markdown\";\n continue;\n }\n\n activeFence = \"plain\";\n processed.push(line);\n continue;\n }\n\n processed.push(line);\n }\n\n return processed.join(\"\\n\");\n}\n\nfunction formatPlainMetricsFooter(metrics: PromptSessionMetrics, copy: BotCopy): string {\n return `${copy.replyMetrics.durationLabel}: ${formatDuration(metrics.durationMs, copy)} | ${copy.replyMetrics.tokensLabel}: ${formatMetricValue(metrics.tokens.total, copy)}`;\n}\n\nfunction formatMarkdownMetricsFooter(metrics: PromptSessionMetrics, copy: BotCopy): string {\n return escapeMarkdownText(formatPlainMetricsFooter(metrics, copy));\n}\n\nfunction formatDuration(durationMs: number | null, copy: BotCopy): string {\n if (durationMs === null || !Number.isFinite(durationMs)) {\n return copy.replyMetrics.notAvailable;\n }\n\n if (durationMs < 1000) {\n return `${Math.round(durationMs)} ms`;\n }\n\n return `${(durationMs / 1000).toFixed(1)} s`;\n}\n\nfunction formatMetricValue(value: number | null, copy: BotCopy): string {\n if (value === null || !Number.isFinite(value)) {\n return copy.replyMetrics.notAvailable;\n }\n\n return `${Math.round(value)}`;\n}\n\ninterface ConsumedTable {\n nextIndex: number;\n rows: string[][];\n}\n\nfunction consumeMarkdownTable(lines: string[], startIndex: number): ConsumedTable | null {\n if (startIndex + 1 >= lines.length) {\n return null;\n }\n\n const headerCells = parseMarkdownTableRow(lines[startIndex]);\n const separatorCells = parseMarkdownTableSeparator(lines[startIndex + 1]);\n\n if (!headerCells || !separatorCells || headerCells.length !== separatorCells.length) {\n return null;\n }\n\n const rows = [headerCells];\n let index = startIndex + 2;\n\n while (index < lines.length) {\n const rowCells = parseMarkdownTableRow(lines[index]);\n\n if (!rowCells || rowCells.length !== headerCells.length) {\n break;\n }\n\n rows.push(rowCells);\n index += 1;\n }\n\n return {\n rows,\n nextIndex: index,\n };\n}\n\nfunction parseMarkdownTableRow(line: string): string[] | null {\n const trimmed = line.trim();\n\n if (!trimmed.includes(\"|\")) {\n return null;\n }\n\n const cells = splitMarkdownTableCells(trimmed)\n .map((cell) => normalizeTableCell(cell));\n\n return cells.length >= 2 ? cells : null;\n}\n\nfunction parseMarkdownTableSeparator(line: string): string[] | null {\n const cells = splitMarkdownTableCells(line.trim());\n\n if (cells.length < 2) {\n return null;\n }\n\n return cells.every((cell) => /^:?-{3,}:?$/.test(cell.trim()))\n ? cells\n : null;\n}\n\nfunction splitMarkdownTableCells(line: string): string[] {\n const content = line\n .replace(/^\\|/, \"\")\n .replace(/\\|$/, \"\");\n const cells: string[] = [];\n let current = \"\";\n let escaped = false;\n\n for (const char of content) {\n if (escaped) {\n current += char;\n escaped = false;\n continue;\n }\n\n if (char === \"\\\\\") {\n escaped = true;\n current += char;\n continue;\n }\n\n if (char === \"|\") {\n cells.push(current);\n current = \"\";\n continue;\n }\n\n current += char;\n }\n\n cells.push(current);\n return cells;\n}\n\nfunction normalizeTableCell(cell: string): string {\n return cell\n .trim()\n .replace(/\\\\\\|/g, \"|\")\n .replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, \"$1 ($2)\")\n .replace(/(\\*\\*|__)(.*?)\\1/g, \"$2\")\n .replace(/(\\*|_)(.*?)\\1/g, \"$2\")\n .replace(/`([^`]+)`/g, \"$1\");\n}\n\nfunction renderTableAsTelegramCodeBlock(rows: string[][]): string {\n return [\n \"```\",\n ...buildAlignedTableLines(rows).map((line) => escapeCodeContent(line)),\n \"```\",\n ].join(\"\\n\");\n}\n\nfunction renderTableAsPlainText(rows: string[][]): string {\n return buildAlignedTableLines(rows).join(\"\\n\");\n}\n\nfunction buildAlignedTableLines(rows: string[][]): string[] {\n const columnWidths = calculateTableColumnWidths(rows);\n const header = formatTableRow(rows[0], columnWidths);\n const separator = columnWidths.map((width) => \"-\".repeat(Math.max(3, width))).join(\"-+-\");\n const body = rows.slice(1).map((row) => formatTableRow(row, columnWidths));\n\n return [header, separator, ...body];\n}\n\nfunction calculateTableColumnWidths(rows: string[][]): number[] {\n return rows[0].map((_, columnIndex) =>\n rows.reduce((maxWidth, row) => Math.max(maxWidth, getDisplayWidth(row[columnIndex] ?? \"\")), 0),\n );\n}\n\nfunction formatTableRow(row: string[], columnWidths: number[]): string {\n return row\n .map((cell, index) => padDisplayWidth(cell, columnWidths[index] ?? 0))\n .join(\" | \");\n}\n\nfunction padDisplayWidth(value: string, targetWidth: number): string {\n const padding = Math.max(0, targetWidth - getDisplayWidth(value));\n\n return `${value}${\" \".repeat(padding)}`;\n}\n\nfunction getDisplayWidth(value: string): number {\n let width = 0;\n\n for (const char of value) {\n width += isWideCharacter(char.codePointAt(0) ?? 0) ? 2 : 1;\n }\n\n return width;\n}\n\nfunction isWideCharacter(codePoint: number): boolean {\n return codePoint >= 0x1100 && (\n codePoint <= 0x115f ||\n codePoint === 0x2329 ||\n codePoint === 0x232a ||\n (codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) ||\n (codePoint >= 0xac00 && codePoint <= 0xd7a3) ||\n (codePoint >= 0xf900 && codePoint <= 0xfaff) ||\n (codePoint >= 0xfe10 && codePoint <= 0xfe19) ||\n (codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||\n (codePoint >= 0xff00 && codePoint <= 0xff60) ||\n (codePoint >= 0xffe0 && codePoint <= 0xffe6)\n );\n}\n\nfunction renderInlineMarkdown(input: string): string | null {\n let output = \"\";\n let cursor = 0;\n\n while (cursor < input.length) {\n if (input.startsWith(\"![\", cursor)) {\n return null;\n }\n\n if (input.startsWith(\"**\", cursor) || input.startsWith(\"__\", cursor)) {\n const delimiter = input.slice(cursor, cursor + 2);\n const closingIndex = input.indexOf(delimiter, cursor + 2);\n\n if (closingIndex > cursor + 2) {\n output += `*${escapeMarkdownText(input.slice(cursor + 2, closingIndex))}*`;\n cursor = closingIndex + 2;\n continue;\n }\n }\n\n if (input[cursor] === \"*\" || input[cursor] === \"_\") {\n const delimiter = input[cursor];\n const closingIndex = input.indexOf(delimiter, cursor + 1);\n\n if (closingIndex > cursor + 1) {\n output += `_${escapeMarkdownText(input.slice(cursor + 1, closingIndex))}_`;\n cursor = closingIndex + 1;\n continue;\n }\n }\n\n if (input[cursor] === \"`\") {\n const closingIndex = input.indexOf(\"`\", cursor + 1);\n\n if (closingIndex === -1) {\n return null;\n }\n\n output += `\\`${escapeCodeContent(input.slice(cursor + 1, closingIndex))}\\``;\n cursor = closingIndex + 1;\n continue;\n }\n\n if (input[cursor] === \"[\") {\n const linkMatch = input.slice(cursor).match(/^\\[([^\\]]+)\\]\\(([^)\\s]+)\\)/);\n\n if (!linkMatch) {\n return null;\n }\n\n output += `[${escapeMarkdownText(linkMatch[1])}](${escapeLinkDestination(linkMatch[2])})`;\n cursor += linkMatch[0].length;\n continue;\n }\n\n output += escapeMarkdownText(input[cursor]);\n cursor += 1;\n }\n\n return output;\n}\n\nfunction escapeMarkdownText(text: string): string {\n return text.replace(MARKDOWN_SPECIAL_CHARACTERS, \"\\\\$1\");\n}\n\nfunction escapeCodeContent(text: string): string {\n return text\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/`/g, \"\\\\`\");\n}\n\nfunction escapeLinkDestination(url: string): string {\n return url\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\\)/g, \"\\\\)\")\n .replace(/\\(/g, \"\\\\(\");\n}\n","import type { Bot } from \"grammy\";\nimport { getChatCopy } from \"../i18n.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport {\n presentStatusMarkdownMessage,\n presentStatusMessage,\n} from \"../presenters/message.presenter.js\";\nimport { renderMarkdownToTelegramMarkdownV2 } from \"../../services/telegram/telegram-format.js\";\nimport type { BotDependencies } from \"../types.js\";\n\ninterface StatusCommandContext {\n chat?: { id: number };\n reply(\n text: string,\n options?: {\n parse_mode: \"MarkdownV2\";\n },\n ): Promise<unknown>;\n}\n\nexport async function handleStatusCommand(ctx: StatusCommandContext, dependencies: BotDependencies) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat?.id);\n\n try {\n const result = await dependencies.getStatusUseCase.execute({\n chatId: ctx.chat?.id ?? 0,\n });\n\n const renderedMarkdown = renderMarkdownToTelegramMarkdownV2(presentStatusMarkdownMessage(result, copy));\n\n if (renderedMarkdown) {\n await ctx.reply(renderedMarkdown, { parse_mode: \"MarkdownV2\" });\n return;\n }\n\n await ctx.reply(presentStatusMessage(result, copy));\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to fetch system status\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport function registerStatusCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command(\"status\", async (ctx) => {\n await handleStatusCommand(ctx, dependencies);\n });\n}\n","import type { Bot } from \"grammy\";\nimport { getChatCopy } from \"../i18n.js\";\nimport type { BotDependencies } from \"../types.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport { buildSessionsListView } from \"../sessions-menu.js\";\n\ninterface SessionsCommandContext {\n chat: { id: number };\n reply(text: string, other?: { reply_markup?: unknown }): Promise<unknown>;\n}\n\nexport async function handleSessionsCommand(ctx: SessionsCommandContext, dependencies: BotDependencies) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n await dependencies.pendingActionRepo.clear(ctx.chat.id);\n const view = await buildSessionsListView(ctx.chat.id, 0, dependencies);\n\n await ctx.reply(view.text, view.keyboard ? { reply_markup: view.keyboard } : undefined);\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to list sessions\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport function registerSessionsCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command(\"sessions\", async (ctx) => {\n await handleSessionsCommand(ctx, dependencies);\n });\n}\n","import { BOT_COPY, type BotCopy } from \"../copy.js\";\n\nexport function presentStartMarkdownMessage(copy: BotCopy = BOT_COPY): string {\n return copy.start.lines.join(\"\\n\");\n}\n","import type { Bot } from \"grammy\";\nimport { buildTelegramStaticReply } from \"../../services/telegram/telegram-format.js\";\nimport { getChatCopy } from \"../i18n.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport { presentStartMarkdownMessage } from \"../presenters/static.presenter.js\";\nimport type { BotDependencies } from \"../types.js\";\n\ninterface StartCommandContext {\n chat?: { id: number };\n reply(\n text: string,\n other?: {\n parse_mode: \"MarkdownV2\";\n },\n ): Promise<unknown>;\n}\n\nexport async function handleStartCommand(ctx: StartCommandContext, dependencies: BotDependencies) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat?.id);\n const reply = buildTelegramStaticReply(presentStartMarkdownMessage(copy));\n\n try {\n await ctx.reply(reply.preferred.text, reply.preferred.options);\n } catch (error) {\n if (reply.preferred.options) {\n dependencies.logger.error({ error }, \"failed to send start markdown reply, falling back to plain text\");\n await ctx.reply(reply.fallback.text);\n return;\n }\n\n dependencies.logger.error({ error }, \"failed to show start message\");\n await ctx.reply(presentError(error, copy));\n }\n}\n\nexport function registerStartCommand(bot: Bot, dependencies: BotDependencies) {\n bot.command(\"start\", async (ctx) => {\n await handleStartCommand(ctx, dependencies);\n });\n}\n","import type { Bot } from \"grammy\";\nimport type { PermissionApprovalRepository } from \"../../domain/permission/approval.repository.js\";\nimport { getChatCopy, getChatLanguage } from \"../i18n.js\";\nimport { presentLanguageSwitchForChat } from \"../commands/language.js\";\nimport type {\n AgentsCallbackData,\n BotDependencies,\n LanguageCallbackData,\n ModelCallbackData,\n PermissionApprovalCallbackData,\n SessionsCallbackData,\n} from \"../types.js\";\nimport {\n buildAgentsKeyboard,\n buildLanguageKeyboard,\n buildModelsKeyboard,\n buildModelVariantsKeyboard,\n} from \"../presenters/keyboard.presenter.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport {\n getModelVariants,\n presentAgentsMessage,\n presentAgentSwitchMessage,\n presentLanguageMessage,\n presentModelsMessage,\n presentModelSwitchMessage,\n presentModelVariantsMessage,\n presentSessionRenamePromptMessage,\n presentSessionSwitchMessage,\n} from \"../presenters/message.presenter.js\";\nimport {\n buildSessionActionView,\n buildSessionsListView,\n} from \"../sessions-menu.js\";\nimport type { OpenCodeClientLike } from \"../../services/opencode/opencode.client.js\";\nimport {\n buildPermissionApprovalResolvedMessage,\n parsePermissionApprovalCallbackData,\n} from \"../../services/permission/telegram-approval.js\";\n\nconst AGENTS_PAGE_PREFIX = \"agents:page:\";\nconst AGENTS_SELECT_PREFIX = \"agents:select:\";\nconst SESSIONS_PAGE_PREFIX = \"sessions:page:\";\nconst SESSIONS_PICK_PREFIX = \"sessions:pick:\";\nconst SESSIONS_SWITCH_PREFIX = \"sessions:switch:\";\nconst SESSIONS_RENAME_PREFIX = \"sessions:rename:\";\nconst SESSIONS_BACK_PREFIX = \"sessions:back:\";\nconst MODEL_PAGE_PREFIX = \"model:page:\";\nconst MODEL_PICK_PREFIX = \"model:pick:\";\nconst MODEL_VARIANT_PREFIX = \"model:variant:\";\nconst LANGUAGE_SELECT_PREFIX = \"language:select:\";\nconst PERMISSION_PREFIX = \"permission:\";\n\ntype PermissionApprovalDependencies = BotDependencies & {\n opencodeClient: OpenCodeClientLike;\n permissionApprovalRepo: PermissionApprovalRepository;\n};\n\ninterface CallbackContext {\n chat?: { id: number };\n callbackQuery: { data: string; message?: { message_id: number } };\n answerCallbackQuery(): Promise<unknown>;\n editMessageText(text: string, other?: { reply_markup?: unknown }): Promise<unknown>;\n api?: Pick<Bot[\"api\"], \"setMyCommands\">;\n}\n\nexport async function handleAgentsCallback(ctx: CallbackContext, dependencies: BotDependencies) {\n const data = ctx.callbackQuery.data as AgentsCallbackData;\n\n if (!data.startsWith(\"agents:\")) {\n return;\n }\n\n await ctx.answerCallbackQuery();\n\n if (!ctx.chat) {\n return;\n }\n\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n if (data.startsWith(AGENTS_PAGE_PREFIX)) {\n const requestedPage = Number(data.slice(AGENTS_PAGE_PREFIX.length));\n const result = await dependencies.listAgentsUseCase.execute({\n chatId: ctx.chat.id,\n });\n\n if (result.agents.length === 0) {\n await ctx.editMessageText(copy.agents.none);\n return;\n }\n\n const { keyboard, page } = buildAgentsKeyboard(result.agents, requestedPage, copy);\n\n await ctx.editMessageText(\n presentAgentsMessage({\n agents: result.agents,\n currentAgentName: result.currentAgentName,\n page: page.page,\n }, copy),\n {\n reply_markup: keyboard,\n },\n );\n\n return;\n }\n\n if (data.startsWith(AGENTS_SELECT_PREFIX)) {\n const agentIndex = Number(data.slice(AGENTS_SELECT_PREFIX.length));\n const result = await dependencies.listAgentsUseCase.execute({\n chatId: ctx.chat.id,\n });\n const agent = result.agents[agentIndex - 1];\n\n if (!agent) {\n await ctx.editMessageText(copy.agents.expired);\n return;\n }\n\n const switchResult = await dependencies.switchAgentUseCase.execute({\n chatId: ctx.chat.id,\n agentName: agent.name,\n });\n\n if (!switchResult.found) {\n await ctx.editMessageText(copy.agents.expired);\n return;\n }\n\n await ctx.editMessageText(presentAgentSwitchMessage(switchResult.agent, copy));\n }\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to handle agent callback\");\n await ctx.editMessageText(presentError(error, copy));\n }\n}\n\nexport async function handleModelsCallback(ctx: CallbackContext, dependencies: BotDependencies) {\n const data = ctx.callbackQuery.data as ModelCallbackData;\n\n if (!data.startsWith(\"model:\")) {\n return;\n }\n\n await ctx.answerCallbackQuery();\n\n if (!ctx.chat) {\n return;\n }\n\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n if (data.startsWith(MODEL_PAGE_PREFIX)) {\n const requestedPage = Number(data.slice(MODEL_PAGE_PREFIX.length));\n const result = await dependencies.listModelsUseCase.execute({\n chatId: ctx.chat.id,\n });\n\n if (result.models.length === 0) {\n await ctx.editMessageText(copy.models.none);\n return;\n }\n\n const { keyboard, page } = buildModelsKeyboard(result.models, requestedPage, copy);\n\n await ctx.editMessageText(\n presentModelsMessage({\n currentModelId: result.currentModelId,\n currentModelProviderId: result.currentModelProviderId,\n currentModelVariant: result.currentModelVariant,\n models: result.models,\n page: page.page,\n }, copy),\n {\n reply_markup: keyboard,\n },\n );\n\n return;\n }\n\n if (data.startsWith(MODEL_PICK_PREFIX)) {\n const modelIndex = Number(data.slice(MODEL_PICK_PREFIX.length));\n const result = await dependencies.listModelsUseCase.execute({\n chatId: ctx.chat.id,\n });\n const model = result.models[modelIndex - 1];\n\n if (!model) {\n await ctx.editMessageText(copy.models.expired);\n return;\n }\n\n const variants = getModelVariants(model);\n\n if (variants.length === 0) {\n const switchResult = await dependencies.switchModelUseCase.execute({\n chatId: ctx.chat.id,\n providerId: model.providerID,\n modelId: model.id,\n });\n\n if (!switchResult.found) {\n await ctx.editMessageText(copy.models.expired);\n return;\n }\n\n await ctx.editMessageText(presentModelSwitchMessage(switchResult.model, switchResult.variant, copy));\n return;\n }\n\n await ctx.editMessageText(\n presentModelVariantsMessage(model, modelIndex, copy),\n {\n reply_markup: buildModelVariantsKeyboard(variants, modelIndex),\n },\n );\n\n return;\n }\n\n if (data.startsWith(MODEL_VARIANT_PREFIX)) {\n const suffix = data.slice(MODEL_VARIANT_PREFIX.length);\n const [modelIndexRaw, variantIndexRaw] = suffix.split(\":\");\n const modelIndex = Number(modelIndexRaw);\n const variantIndex = Number(variantIndexRaw);\n const result = await dependencies.listModelsUseCase.execute({\n chatId: ctx.chat.id,\n });\n const model = result.models[modelIndex - 1];\n\n if (!model) {\n await ctx.editMessageText(copy.models.expired);\n return;\n }\n\n const variants = getModelVariants(model);\n const variant = variants[variantIndex - 1];\n\n if (!variant) {\n await ctx.editMessageText(copy.models.reasoningLevelExpired);\n return;\n }\n\n const switchResult = await dependencies.switchModelUseCase.execute({\n chatId: ctx.chat.id,\n providerId: model.providerID,\n modelId: model.id,\n variant,\n });\n\n if (!switchResult.found) {\n await ctx.editMessageText(copy.models.expired);\n return;\n }\n\n await ctx.editMessageText(presentModelSwitchMessage(switchResult.model, switchResult.variant, copy));\n }\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to handle model callback\");\n await ctx.editMessageText(presentError(error, copy));\n }\n}\n\nexport async function handleSessionsCallback(ctx: CallbackContext, dependencies: BotDependencies) {\n const data = ctx.callbackQuery.data as SessionsCallbackData;\n\n if (!data.startsWith(\"sessions:\")) {\n return;\n }\n\n await ctx.answerCallbackQuery();\n\n if (!ctx.chat) {\n return;\n }\n\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n if (data.startsWith(SESSIONS_PAGE_PREFIX)) {\n const requestedPage = Number(data.slice(SESSIONS_PAGE_PREFIX.length));\n const view = await buildSessionsListView(ctx.chat.id, requestedPage, dependencies);\n\n await ctx.editMessageText(view.text, view.keyboard ? { reply_markup: view.keyboard } : undefined);\n\n return;\n }\n\n if (data.startsWith(SESSIONS_BACK_PREFIX)) {\n const requestedPage = Number(data.slice(SESSIONS_BACK_PREFIX.length));\n const view = await buildSessionsListView(ctx.chat.id, requestedPage, dependencies);\n\n await ctx.editMessageText(view.text, view.keyboard ? { reply_markup: view.keyboard } : undefined);\n return;\n }\n\n if (data.startsWith(SESSIONS_PICK_PREFIX)) {\n const target = parseSessionActionTarget(data, SESSIONS_PICK_PREFIX);\n\n if (!target) {\n await ctx.editMessageText(copy.sessions.expired);\n return;\n }\n\n const view = await buildSessionActionView(\n ctx.chat.id,\n target.page,\n target.sessionId,\n dependencies,\n );\n\n if (!view.found) {\n await ctx.editMessageText(copy.sessions.expired);\n return;\n }\n\n await ctx.editMessageText(view.text, {\n reply_markup: view.keyboard,\n });\n return;\n }\n\n if (data.startsWith(SESSIONS_SWITCH_PREFIX)) {\n const target = parseSessionActionTarget(data, SESSIONS_SWITCH_PREFIX);\n\n if (!target) {\n await ctx.editMessageText(copy.sessions.expired);\n return;\n }\n\n const result = await dependencies.switchSessionUseCase.execute({\n chatId: ctx.chat.id,\n sessionId: target.sessionId,\n });\n\n if (!result.found) {\n await ctx.editMessageText(copy.sessions.expired);\n return;\n }\n\n await ctx.editMessageText(presentSessionSwitchMessage(result.session, copy));\n return;\n }\n\n if (data.startsWith(SESSIONS_RENAME_PREFIX)) {\n const target = parseSessionActionTarget(data, SESSIONS_RENAME_PREFIX);\n\n if (!target) {\n await ctx.editMessageText(copy.sessions.renameExpired);\n return;\n }\n\n const view = await buildSessionActionView(\n ctx.chat.id,\n target.page,\n target.sessionId,\n dependencies,\n );\n\n if (!view.found) {\n await ctx.editMessageText(copy.sessions.renameExpired);\n return;\n }\n\n const menuMessageId = ctx.callbackQuery.message?.message_id;\n\n if (typeof menuMessageId !== \"number\" || !Number.isInteger(menuMessageId)) {\n await ctx.editMessageText(copy.sessions.renameExpired);\n return;\n }\n\n await dependencies.pendingActionRepo.set({\n chatId: ctx.chat.id,\n kind: \"session_rename\",\n sessionId: view.session.id,\n menuMessageId,\n returnPage: view.page,\n updatedAt: new Date().toISOString(),\n });\n\n await ctx.editMessageText(presentSessionRenamePromptMessage(view.session, copy));\n }\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to handle session callback\");\n await ctx.editMessageText(presentError(error, copy));\n }\n}\n\nexport async function handleLanguageCallback(ctx: CallbackContext, dependencies: BotDependencies) {\n const data = ctx.callbackQuery.data as LanguageCallbackData;\n\n if (!data.startsWith(\"language:\")) {\n return;\n }\n\n await ctx.answerCallbackQuery();\n\n if (!ctx.chat || !ctx.api) {\n return;\n }\n\n const currentCopy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n try {\n if (!data.startsWith(LANGUAGE_SELECT_PREFIX)) {\n await ctx.editMessageText(\n presentLanguageMessage(await getChatLanguage(dependencies.sessionRepo, ctx.chat.id), currentCopy),\n {\n reply_markup: buildLanguageKeyboard(\n await getChatLanguage(dependencies.sessionRepo, ctx.chat.id),\n currentCopy,\n ),\n },\n );\n return;\n }\n\n const selectedLanguage = data.slice(LANGUAGE_SELECT_PREFIX.length);\n const result = await presentLanguageSwitchForChat(ctx.chat.id, ctx.api, selectedLanguage, dependencies);\n\n await ctx.editMessageText(result.text, {\n reply_markup: result.keyboard,\n });\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to handle language callback\");\n await ctx.editMessageText(presentError(error, currentCopy));\n }\n}\n\nexport async function handlePermissionApprovalCallback(\n ctx: CallbackContext,\n dependencies: PermissionApprovalDependencies,\n) {\n const data = ctx.callbackQuery.data as PermissionApprovalCallbackData;\n\n if (!data.startsWith(PERMISSION_PREFIX)) {\n return;\n }\n\n await ctx.answerCallbackQuery();\n\n if (!ctx.chat) {\n return;\n }\n\n const parsed = parsePermissionApprovalCallbackData(data);\n\n if (!parsed) {\n return;\n }\n\n try {\n const approvals = await dependencies.permissionApprovalRepo.listByRequestId(parsed.requestId);\n const approval = approvals.find((item) => item.chatId === ctx.chat?.id);\n\n await dependencies.opencodeClient.replyToPermission(\n parsed.requestId,\n parsed.reply,\n undefined,\n approval?.sessionId,\n );\n\n if (approval) {\n await dependencies.permissionApprovalRepo.set({\n ...approval,\n status: parsed.reply,\n updatedAt: new Date().toISOString(),\n });\n }\n\n await ctx.editMessageText(buildPermissionApprovalResolvedMessage(parsed.requestId, parsed.reply));\n } catch (error) {\n dependencies.logger.error({ error, requestId: parsed.requestId }, \"failed to reply to permission request\");\n await ctx.editMessageText(\"Failed to reply to the permission request.\");\n }\n}\n\nexport function registerCallbackHandler(bot: Bot, dependencies: PermissionApprovalDependencies) {\n bot.callbackQuery(/^agents:/, async (ctx) => {\n await handleAgentsCallback(ctx, dependencies);\n });\n\n bot.callbackQuery(/^sessions:/, async (ctx) => {\n await handleSessionsCallback(ctx, dependencies);\n });\n\n bot.callbackQuery(/^model:/, async (ctx) => {\n await handleModelsCallback(ctx, dependencies);\n });\n\n bot.callbackQuery(/^language:/, async (ctx) => {\n await handleLanguageCallback(ctx, dependencies);\n });\n\n bot.callbackQuery(/^permission:/, async (ctx) => {\n await handlePermissionApprovalCallback(ctx, dependencies);\n });\n}\n\nfunction parseSessionActionTarget(data: string, prefix: string) {\n const suffix = data.slice(prefix.length);\n const separatorIndex = suffix.indexOf(\":\");\n\n if (separatorIndex === -1) {\n return null;\n }\n\n const page = Number(suffix.slice(0, separatorIndex));\n const sessionId = suffix.slice(separatorIndex + 1).trim();\n\n if (!Number.isInteger(page) || page < 0 || !sessionId) {\n return null;\n }\n\n return {\n page,\n sessionId,\n };\n}\n","import { getChatCopy } from \"../i18n.js\";\nimport { presentError } from \"../presenters/error.presenter.js\";\nimport { buildTelegramPromptReply } from \"../../services/telegram/telegram-format.js\";\nimport type { BotDependencies } from \"../types.js\";\nimport type { PromptSessionFilePartInput } from \"../../services/opencode/opencode.client.js\";\n\ninterface SentMessage {\n message_id: number;\n}\n\nexport interface PromptExecutionContext {\n chat: { id: number };\n api: {\n deleteMessage(chatId: number, messageId: number): Promise<unknown>;\n };\n reply(text: string, other?: { parse_mode?: \"MarkdownV2\" }): Promise<SentMessage>;\n}\n\nexport interface PromptExecutionInput {\n files?: PromptSessionFilePartInput[];\n text?: string | null;\n}\n\nconst activePromptChats = new Set<number>();\n\nexport async function executePromptRequest(\n ctx: PromptExecutionContext,\n dependencies: BotDependencies,\n resolvePrompt: () => Promise<PromptExecutionInput>,\n) {\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n\n if (activePromptChats.has(ctx.chat.id)) {\n await ctx.reply(copy.status.alreadyProcessing);\n return;\n }\n\n let processingMessage: SentMessage | null = null;\n let sentTerminalReply = false;\n\n try {\n activePromptChats.add(ctx.chat.id);\n processingMessage = await ctx.reply(copy.status.processing);\n\n const promptInput = await resolvePrompt();\n const result = await dependencies.sendPromptUseCase.execute({\n chatId: ctx.chat.id,\n text: promptInput.text,\n files: promptInput.files,\n });\n const promptReply = normalizePromptReplyForDisplay(result.assistantReply, copy, dependencies);\n const telegramReply = buildTelegramPromptReply(promptReply, copy);\n\n try {\n await ctx.reply(telegramReply.preferred.text, telegramReply.preferred.options);\n } catch (replyError) {\n dependencies.logger.warn?.(\n { error: replyError },\n \"failed to send preferred telegram reply, falling back to plain text\",\n );\n await ctx.reply(telegramReply.fallback.text, telegramReply.fallback.options);\n }\n sentTerminalReply = true;\n } catch (error) {\n dependencies.logger.error({ error }, \"failed to handle prompt request\");\n await ctx.reply(presentError(error, copy));\n sentTerminalReply = true;\n } finally {\n activePromptChats.delete(ctx.chat.id);\n if (processingMessage && sentTerminalReply) {\n try {\n await ctx.api.deleteMessage(ctx.chat.id, processingMessage.message_id);\n } catch (error) {\n dependencies.logger.warn?.({ error }, \"failed to delete processing message\");\n }\n }\n }\n}\n\nfunction normalizePromptReplyForDisplay(\n promptReply: Awaited<ReturnType<BotDependencies[\"sendPromptUseCase\"][\"execute\"]>>[\"assistantReply\"],\n copy: Awaited<ReturnType<typeof getChatCopy>>,\n dependencies: BotDependencies,\n) {\n if (!promptReply.assistantError) {\n return promptReply;\n }\n\n if (isRecoverableStructuredOutputError(promptReply)) {\n dependencies.logger.warn?.(\n {\n error: promptReply.assistantError,\n },\n \"structured output validation failed, falling back to assistant text reply\",\n );\n\n return {\n ...promptReply,\n assistantError: null,\n };\n }\n\n return {\n ...promptReply,\n bodyMd: null,\n fallbackText: presentError(promptReply.assistantError, copy),\n };\n}\n\nfunction isRecoverableStructuredOutputError(\n promptReply: Awaited<ReturnType<BotDependencies[\"sendPromptUseCase\"][\"execute\"]>>[\"assistantReply\"],\n): boolean {\n if (promptReply.assistantError?.name !== \"StructuredOutputError\") {\n return false;\n }\n\n if (promptReply.bodyMd?.trim()) {\n return true;\n }\n\n return promptReply.parts.some((part) =>\n part.type === \"text\" && typeof part.text === \"string\" && part.text.trim().length > 0\n );\n}\n","import type { Bot } from \"grammy\";\nimport type { BotDependencies } from \"../types.js\";\nimport { executePromptRequest, type PromptExecutionContext } from \"./prompt.handler.js\";\nimport { replyIfSessionRenamePending } from \"../session-rename.js\";\nimport { ImageMessageUnsupportedError } from \"../../use-cases/upload-file.usecase.js\";\n\nconst TELEGRAM_MAX_DOWNLOAD_BYTES = 20 * 1024 * 1024;\n\ninterface TelegramPhotoSize {\n file_id: string;\n file_size?: number;\n}\n\ninterface TelegramDocument {\n file_id: string;\n file_name?: string;\n file_size?: number;\n mime_type?: string;\n}\n\ninterface FileContext extends PromptExecutionContext {\n getFile(): Promise<{ file_path?: string }>;\n message: {\n caption?: string;\n document?: TelegramDocument;\n photo?: TelegramPhotoSize[];\n };\n}\n\nexport async function handleImageMessage(ctx: FileContext, dependencies: BotDependencies) {\n const image = resolveTelegramImage(ctx.message);\n\n if (!image) {\n return;\n }\n\n if (await replyIfSessionRenamePending(ctx, dependencies)) {\n return;\n }\n\n await executePromptRequest(ctx, dependencies, async () => {\n if (typeof image.fileSize === \"number\" && image.fileSize > TELEGRAM_MAX_DOWNLOAD_BYTES) {\n throw new ImageMessageUnsupportedError(\n `Image file size ${image.fileSize} exceeds the Telegram download limit of ${TELEGRAM_MAX_DOWNLOAD_BYTES} bytes.`,\n );\n }\n\n const file = await ctx.getFile();\n const filePath = file.file_path?.trim();\n\n if (!filePath) {\n throw new ImageMessageUnsupportedError(\"Telegram did not provide a downloadable image file path.\");\n }\n\n const promptFile = await dependencies.uploadFileUseCase.execute({\n expectedType: \"image\",\n filePath,\n filename: image.filename,\n mimeType: image.mimeType,\n });\n\n return {\n files: [promptFile],\n text: ctx.message.caption?.trim() || null,\n };\n });\n}\n\nexport function registerFileHandler(bot: Bot, dependencies: BotDependencies) {\n bot.on(\"message:photo\", async (ctx) => {\n await handleImageMessage(ctx, dependencies);\n });\n\n bot.on(\"message:document\", async (ctx) => {\n await handleImageMessage(ctx, dependencies);\n });\n}\n\nfunction resolveTelegramImage(message: FileContext[\"message\"]) {\n const document = message.document;\n\n if (document && isImageDocument(document)) {\n return {\n fileId: document.file_id,\n fileSize: document.file_size,\n filename: document.file_name ?? null,\n mimeType: document.mime_type ?? null,\n };\n }\n\n const photo = pickLargestPhoto(message.photo);\n\n if (!photo) {\n return null;\n }\n\n return {\n fileId: photo.file_id,\n fileSize: photo.file_size,\n filename: null,\n mimeType: \"image/jpeg\",\n };\n}\n\nfunction isImageDocument(document: TelegramDocument): boolean {\n const mimeType = document.mime_type?.trim().toLowerCase();\n const filename = document.file_name?.trim().toLowerCase() ?? \"\";\n\n return !!mimeType?.startsWith(\"image/\")\n || /\\.(png|jpe?g|gif|webp|bmp|svg)$/i.test(filename);\n}\n\nfunction pickLargestPhoto(photos: TelegramPhotoSize[] | undefined): TelegramPhotoSize | null {\n if (!photos || photos.length === 0) {\n return null;\n }\n\n return photos.reduce<TelegramPhotoSize>((largest, current) => {\n const currentSize = current.file_size ?? 0;\n const largestSize = largest.file_size ?? 0;\n\n return currentSize >= largestSize ? current : largest;\n }, photos[0]);\n}\n","import type { Bot } from \"grammy\";\nimport type { BotDependencies } from \"../types.js\";\nimport { executePromptRequest, type PromptExecutionContext } from \"./prompt.handler.js\";\nimport { handlePendingSessionRenameText } from \"../session-rename.js\";\n\ninterface MessageContext extends PromptExecutionContext {\n api: PromptExecutionContext[\"api\"] & {\n editMessageText(\n chatId: number,\n messageId: number,\n text: string,\n other?: { reply_markup?: unknown },\n ): Promise<unknown>;\n };\n message: { text?: string };\n}\n\nexport async function handleTextMessage(ctx: MessageContext, dependencies: BotDependencies) {\n const text = ctx.message.text?.trim();\n\n if (await handlePendingSessionRenameText(ctx, dependencies)) return;\n if (!text) return;\n if (text.startsWith(\"/\")) return;\n await executePromptRequest(ctx, dependencies, async () => ({ text }));\n}\n\nexport function registerMessageHandler(bot: Bot, dependencies: BotDependencies) {\n bot.on(\"message:text\", async (ctx) => {\n await handleTextMessage(ctx, dependencies);\n });\n}\n","import type { Bot } from \"grammy\";\nimport { getChatCopy } from \"../i18n.js\";\nimport type { BotDependencies } from \"../types.js\";\nimport { replyIfSessionRenamePending } from \"../session-rename.js\";\n\ninterface VoiceContext {\n chat: { id: number };\n message: { voice?: unknown };\n reply(text: string): Promise<unknown>;\n}\n\nexport async function handleVoiceMessage(ctx: VoiceContext, dependencies: BotDependencies) {\n if (!ctx.message.voice) {\n return;\n }\n\n if (await replyIfSessionRenamePending(ctx, dependencies)) {\n return;\n }\n\n const copy = await getChatCopy(dependencies.sessionRepo, ctx.chat.id);\n await ctx.reply(copy.errors.voiceUnsupported);\n}\n\nexport function registerVoiceHandler(bot: Bot, dependencies: BotDependencies) {\n bot.on(\"message:voice\", async (ctx) => {\n await handleVoiceMessage(ctx, dependencies);\n });\n}\n","import type { MiddlewareFn } from \"grammy\";\n\nexport function createAuthMiddleware(allowedChatIds: readonly number[]): MiddlewareFn {\n return async (ctx, next) => {\n if (allowedChatIds.length === 0) {\n return next();\n }\n\n const chatId = ctx.chat?.id;\n\n if (!chatId || !allowedChatIds.includes(chatId)) {\n await ctx.reply(\"Unauthorized chat.\");\n return;\n }\n\n return next();\n };\n}\n","import type { MiddlewareFn } from \"grammy\";\nimport type { AppLogger } from \"../../infra/logger/index.js\";\nimport { createRedactedPreview } from \"../../infra/utils/redact.js\";\n\ninterface LoggingContextLike {\n chat?: { id?: number };\n from?: { id?: number };\n msg?: { text?: string } | null;\n update: { update_id: number };\n}\n\nexport function buildIncomingUpdateLogFields(ctx: LoggingContextLike) {\n const messageText = ctx.msg && \"text\" in ctx.msg\n ? ctx.msg.text\n : undefined;\n\n return {\n updateId: ctx.update.update_id,\n chatId: ctx.chat?.id,\n fromId: ctx.from?.id,\n hasText: typeof messageText === \"string\" && messageText.length > 0,\n textLength: typeof messageText === \"string\" ? messageText.length : 0,\n textPreview: typeof messageText === \"string\" && messageText.length > 0\n ? createRedactedPreview(messageText)\n : undefined,\n };\n}\n\nexport function createLoggingMiddleware(logger: Pick<AppLogger, \"info\">): MiddlewareFn {\n return async (ctx, next) => {\n logger.info(\n buildIncomingUpdateLogFields(ctx),\n \"incoming update\",\n );\n\n return next();\n };\n}\n","import type { Bot } from \"grammy\";\nimport type { PermissionApprovalRepository } from \"../domain/permission/approval.repository.js\";\nimport type { OpenCodeClientLike } from \"../services/opencode/opencode.client.js\";\nimport { registerAgentsCommand } from \"./commands/agents.js\";\nimport { registerCancelCommand } from \"./commands/cancel.js\";\nimport { registerLanguageCommand } from \"./commands/language.js\";\nimport { registerModelsCommand } from \"./commands/models.js\";\nimport { registerNewCommand } from \"./commands/new.js\";\nimport { registerStatusCommand } from \"./commands/status.js\";\nimport type { BotDependencies } from \"./types.js\";\nimport { registerSessionsCommand } from \"./commands/sessions.js\";\nimport { registerStartCommand } from \"./commands/start.js\";\nimport { registerCallbackHandler } from \"./handlers/callback.handler.js\";\nimport { registerFileHandler } from \"./handlers/file.handler.js\";\nimport { registerMessageHandler } from \"./handlers/message.handler.js\";\nimport { registerVoiceHandler } from \"./handlers/voice.handler.js\";\nimport { createAuthMiddleware } from \"./middlewares/auth.js\";\nimport { createLoggingMiddleware } from \"./middlewares/logging.js\";\n\nexport interface RegisterBotOptions {\n telegramAllowedChatIds: number[];\n}\n\nexport function registerBot(\n bot: Bot,\n container: BotDependencies & {\n opencodeClient: OpenCodeClientLike;\n permissionApprovalRepo: PermissionApprovalRepository;\n },\n options: RegisterBotOptions,\n) {\n bot.use(createLoggingMiddleware(container.logger));\n bot.use(createAuthMiddleware(options.telegramAllowedChatIds));\n\n registerStartCommand(bot, container);\n registerStatusCommand(bot, container);\n registerNewCommand(bot, container);\n registerAgentsCommand(bot, container);\n registerSessionsCommand(bot, container);\n registerCancelCommand(bot, container);\n registerModelsCommand(bot, container);\n registerLanguageCommand(bot, container);\n registerCallbackHandler(bot, container);\n registerFileHandler(bot, container);\n registerMessageHandler(bot, container);\n registerVoiceHandler(bot, container);\n}\n","import { run } from \"@grammyjs/runner\";\nimport { Bot } from \"grammy\";\nimport { syncTelegramCommands } from \"../bot/commands/sync-commands.js\";\nimport { registerBot } from \"../bot/index.js\";\nimport type { AppConfig } from \"./config.js\";\nimport type { AppContainer } from \"./container.js\";\n\nexport interface StartTelegramBotRuntimeInput {\n config: AppConfig;\n container: AppContainer;\n syncCommands?: boolean;\n}\n\nexport interface TelegramBotRuntime {\n bot: Bot;\n container: AppContainer;\n stop(): void;\n dispose(): Promise<void>;\n}\n\nexport async function startTelegramBotRuntime(\n input: StartTelegramBotRuntimeInput,\n): Promise<TelegramBotRuntime> {\n const bot = new Bot(input.config.telegramBotToken, {\n client: {\n apiRoot: input.config.telegramApiRoot,\n },\n });\n\n registerBot(bot, input.container, {\n telegramAllowedChatIds: input.config.telegramAllowedChatIds,\n });\n\n bot.catch((error) => {\n input.container.logger.error(\n {\n error: error.error,\n update: error.ctx.update,\n },\n \"telegram bot update failed\",\n );\n });\n\n input.container.logger.info(\"bot starting...\");\n\n if (input.syncCommands ?? true) {\n await syncTelegramCommands(bot, input.container.logger);\n }\n\n const runner = run(bot);\n let stopped = false;\n let disposed = false;\n const stop = () => {\n if (stopped) {\n return;\n }\n\n stopped = true;\n runner.stop();\n };\n const dispose = async () => {\n if (disposed) {\n return;\n }\n\n disposed = true;\n stop();\n await input.container.dispose();\n };\n\n return {\n bot,\n container: input.container,\n stop,\n dispose,\n };\n}\n","import type { Hooks, Plugin, PluginInput } from \"@opencode-ai/plugin\";\nimport { bootstrapPluginApp } from \"./app/bootstrap.js\";\nimport { handleTelegramBotPluginEvent } from \"./app/plugin-events.js\";\nimport {\n preparePluginConfiguration,\n type PreparedPluginConfiguration,\n} from \"./app/plugin-config.js\";\nimport {\n startTelegramBotRuntime,\n type StartTelegramBotRuntimeInput,\n type TelegramBotRuntime,\n} from \"./app/runtime.js\";\nimport type { PluginConfigSource } from \"./app/config.js\";\nimport type { OpenCodeSdkClientLike } from \"./services/opencode/opencode.client.js\";\n\nexport interface OpenCodePluginContextLike {\n client: OpenCodeSdkClientLike;\n directory?: string;\n worktree?: string;\n}\n\nexport interface EnsureTelegramBotPluginRuntimeOptions {\n context: OpenCodePluginContextLike;\n config?: PluginConfigSource;\n bootstrapApp?: typeof bootstrapPluginApp;\n prepareConfiguration?: (input: {\n cwd: string;\n config?: PluginConfigSource;\n }) => Promise<PreparedPluginConfiguration>;\n startRuntime?: (input: StartTelegramBotRuntimeInput) => Promise<TelegramBotRuntime>;\n}\n\ninterface TelegramBotPluginRuntimeState {\n cwd: string;\n runtime: TelegramBotRuntime | null;\n runtimePromise: Promise<TelegramBotRuntime>;\n}\n\nlet runtimeState: TelegramBotPluginRuntimeState | null = null;\n\nexport async function ensureTelegramBotPluginRuntime(\n options: EnsureTelegramBotPluginRuntimeOptions,\n): Promise<TelegramBotRuntime> {\n const cwd = resolvePluginRuntimeCwd(options.context);\n\n if (runtimeState && runtimeState.cwd !== cwd) {\n const activeState = runtimeState;\n runtimeState = null;\n await disposeTelegramBotPluginRuntimeState(activeState);\n }\n\n if (!runtimeState) {\n const runtimePromise = startPluginRuntime(options, cwd)\n .then((runtime) => {\n if (runtimeState?.runtimePromise === runtimePromise) {\n runtimeState.runtime = runtime;\n }\n\n return runtime;\n })\n .catch((error) => {\n if (runtimeState?.runtimePromise === runtimePromise) {\n runtimeState = null;\n }\n\n throw error;\n });\n\n runtimeState = {\n cwd,\n runtime: null,\n runtimePromise,\n };\n }\n\n return runtimeState.runtimePromise;\n}\n\nexport const TelegramBotPlugin: Plugin = async (context) => {\n const runtime = await ensureTelegramBotPluginRuntime({\n context: context as unknown as OpenCodePluginContextLike,\n });\n\n return createHooks(runtime);\n};\n\nexport async function resetTelegramBotPluginRuntimeForTests() {\n if (!runtimeState) {\n return;\n }\n\n const activeState = runtimeState;\n runtimeState = null;\n await disposeTelegramBotPluginRuntimeState(activeState);\n}\n\nexport default TelegramBotPlugin;\n\nasync function startPluginRuntime(\n options: EnsureTelegramBotPluginRuntimeOptions,\n cwd: string,\n): Promise<TelegramBotRuntime> {\n const bootstrapApp = options.bootstrapApp ?? bootstrapPluginApp;\n const prepareConfiguration = options.prepareConfiguration ?? preparePluginConfiguration;\n const startRuntime = options.startRuntime ?? startTelegramBotRuntime;\n const preparedConfiguration = await prepareConfiguration({\n cwd,\n config: options.config,\n });\n const { config, container } = bootstrapApp(\n options.context.client,\n preparedConfiguration.config,\n { cwd: preparedConfiguration.cwd },\n );\n\n try {\n if (preparedConfiguration.ignoredProjectConfigFilePath) {\n container.logger.warn(\n {\n cwd: preparedConfiguration.cwd,\n ignoredProjectConfigFilePath: preparedConfiguration.ignoredProjectConfigFilePath,\n globalConfigFilePath: preparedConfiguration.globalConfigFilePath,\n },\n \"legacy worktree plugin config is ignored; migrate settings to the global opencode-tbot config\",\n );\n }\n\n const runtime = await startRuntime({ config, container });\n\n container.logger.info(\n {\n cwd: preparedConfiguration.cwd,\n globalConfigFilePath: preparedConfiguration.globalConfigFilePath,\n ignoredProjectConfigFilePath: preparedConfiguration.ignoredProjectConfigFilePath,\n configFilePath: preparedConfiguration.configFilePath,\n mode: \"plugin\",\n },\n \"telegram bot plugin runtime started\",\n );\n\n return runtime;\n } catch (error) {\n await container.dispose();\n throw error;\n }\n}\n\nfunction resolvePluginRuntimeCwd(context: OpenCodePluginContextLike): string {\n return context.worktree ?? context.directory ?? process.cwd();\n}\n\nasync function disposeTelegramBotPluginRuntimeState(\n state: TelegramBotPluginRuntimeState,\n): Promise<void> {\n const runtime = state.runtime ?? await state.runtimePromise.catch(() => null);\n\n await runtime?.dispose();\n}\n\nfunction createHooks(runtime: TelegramBotRuntime): Hooks {\n return {\n async event({ event }) {\n await handleTelegramBotPluginEvent(runtime, event as never);\n },\n async \"permission.ask\"(input, output) {\n const bindings = await runtime.container.sessionRepo.listBySessionId(input.sessionID);\n const foregroundChatIds = runtime.container.foregroundSessionTracker.listChatIds(input.sessionID);\n\n if (bindings.length > 0 || foregroundChatIds.length > 0) {\n output.status = \"ask\";\n }\n },\n };\n}\n"],"mappings":"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"}