opencode-tbot 0.1.23 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ja.md CHANGED
@@ -62,18 +62,13 @@ npm exec --package opencode-tbot@latest opencode-tbot -- update
62
62
 
63
63
  ## 設定
64
64
 
65
- ランタイム設定は次の順序で読み込まれます。
65
+ ランタイム設定は `~/.config/opencode/opencode-tbot/config.json` からのみ読み込まれます。
66
66
 
67
- 1. `~/.config/opencode/opencode-tbot/config.json` のグローバルデフォルト
68
- 2. `<worktree>/tbot.config.json` のプロジェクト上書き設定
69
-
70
- プロジェクト設定はグローバル設定に上書きマージされます。`telegram` と `state` はセクション単位でディープマージされます。
67
+ 古い `<worktree>/tbot.config.json` はランタイムでは無視されます。検出された場合は、値をグローバル設定へ移行できるように警告ログを出します。
71
68
 
72
69
  古い `openrouter` 音声転写設定はランタイムでは無視され、インストーラーが設定を書き直す際にも削除されます。
73
70
 
74
- リポジトリには最小構成の参考として [tbot.config.example.json](./tbot.config.example.json) も含まれています。
75
-
76
- ### `tbot.config.json` の例
71
+ ### グローバル `config.json` の例
77
72
 
78
73
  ```json
79
74
  {
@@ -109,7 +104,7 @@ npm exec --package opencode-tbot@latest opencode-tbot -- update
109
104
  ## クイックスタート
110
105
 
111
106
  1. `npm exec --package opencode-tbot@latest opencode-tbot -- install` でプラグインをインストールします。
112
- 2. 特定の chat のみ許可したい場合は、`tbot.config.json` で `telegram.allowedChatIds` を設定します。
107
+ 2. 特定の chat のみ許可したい場合は、`~/.config/opencode/opencode-tbot/config.json` で `telegram.allowedChatIds` を設定します。
113
108
  3. 対象の worktree で OpenCode を起動し、プラグインランタイムを読み込ませます。
114
109
  4. Telegram で `/status` を実行し、接続を確認します。
115
110
  5. `/new [title]` を実行するか、テキストメッセージを直接送信して使い始めます。
package/README.md CHANGED
@@ -62,18 +62,13 @@ npm exec --package opencode-tbot@latest opencode-tbot -- update
62
62
 
63
63
  ## Configuration
64
64
 
65
- Runtime config is loaded in this order:
65
+ Runtime config is loaded from `~/.config/opencode/opencode-tbot/config.json`.
66
66
 
67
- 1. Global defaults from `~/.config/opencode/opencode-tbot/config.json`
68
- 2. Project overrides from `<worktree>/tbot.config.json`
69
-
70
- Project config is merged on top of the global config. `telegram` and `state` are deep-merged by section.
67
+ Legacy `<worktree>/tbot.config.json` files are ignored at runtime. If one is present, the plugin logs a warning so you can migrate its values into the global config.
71
68
 
72
69
  Legacy `openrouter` voice-transcription settings are ignored at runtime. When the installer rewrites the config, it removes them.
73
70
 
74
- The repository also includes [tbot.config.example.json](./tbot.config.example.json) as a minimal reference.
75
-
76
- ### Example `tbot.config.json`
71
+ ### Example Global `config.json`
77
72
 
78
73
  ```json
79
74
  {
@@ -109,7 +104,7 @@ The repository also includes [tbot.config.example.json](./tbot.config.example.js
109
104
  ## Quick Start
110
105
 
111
106
  1. Install the plugin with `npm exec --package opencode-tbot@latest opencode-tbot -- install`.
112
- 2. Set `telegram.allowedChatIds` in `tbot.config.json` if you want to restrict the bot to specific chats.
107
+ 2. Set `telegram.allowedChatIds` in `~/.config/opencode/opencode-tbot/config.json` if you want to restrict the bot to specific chats.
113
108
  3. Start OpenCode in the target worktree so the plugin runtime can load.
114
109
  4. In Telegram, run `/status` to verify the connection.
115
110
  5. Run `/new [title]` or send a text message directly to start working.
package/README.zh-CN.md CHANGED
@@ -62,18 +62,13 @@ npm exec --package opencode-tbot@latest opencode-tbot -- update
62
62
 
63
63
  ## 配置
64
64
 
65
- 运行时配置按以下优先级加载:
65
+ 运行时配置只会从 `~/.config/opencode/opencode-tbot/config.json` 加载。
66
66
 
67
- 1. 全局默认配置 `~/.config/opencode/opencode-tbot/config.json`
68
- 2. 项目覆盖配置 `<worktree>/tbot.config.json`
69
-
70
- 项目配置会覆盖全局默认值;`telegram` 和 `state` 会按分段进行深合并。
67
+ 遗留的 `<worktree>/tbot.config.json` 会在运行时被忽略;如果检测到该文件,插件会记录一条警告,提示你把其中的值迁移到全局配置。
71
68
 
72
69
  遗留的 `openrouter` 语音转写配置在运行时会被忽略;安装器重写配置时也会自动移除这些字段。
73
70
 
74
- 仓库内还提供了 [tbot.config.example.json](./tbot.config.example.json) 作为最小配置参考。
75
-
76
- ### `tbot.config.json` 示例
71
+ ### 全局 `config.json` 示例
77
72
 
78
73
  ```json
79
74
  {
@@ -109,7 +104,7 @@ npm exec --package opencode-tbot@latest opencode-tbot -- update
109
104
  ## 快速开始
110
105
 
111
106
  1. 使用 `npm exec --package opencode-tbot@latest opencode-tbot -- install` 安装插件。
112
- 2. 如果你只想允许特定聊天使用 bot,请在 `tbot.config.json` 中设置 `telegram.allowedChatIds`。
107
+ 2. 如果你只想允许特定聊天使用 bot,请在 `~/.config/opencode/opencode-tbot/config.json` 中设置 `telegram.allowedChatIds`。
113
108
  3. 在目标 worktree 中启动 OpenCode,让插件运行时被加载。
114
109
  4. 在 Telegram 中执行 `/status` 验证连接是否正常。
115
110
  5. 执行 `/new [title]`,或者直接发送文本消息开始使用。
@@ -69,14 +69,16 @@ var OPENCODE_CONFIG_FILE_NAME = "opencode.json";
69
69
  async function preparePluginConfiguration(options) {
70
70
  const globalConfigFilePath = getGlobalPluginConfigFilePath(options.homeDir ?? homedir());
71
71
  const projectConfigFilePath = await resolveProjectPluginConfigFilePath(options.cwd);
72
- const [globalConfig, projectConfig] = await Promise.all([loadPluginConfigFile(globalConfigFilePath), loadPluginConfigFile(projectConfigFilePath)]);
73
- const config = stripLegacyVoiceConfig(mergePluginConfigSources(globalConfig, projectConfig, options.config));
74
- const configFilePath = await pathExists(projectConfigFilePath) ? projectConfigFilePath : globalConfigFilePath;
72
+ const [globalConfig, hasIgnoredProjectConfig] = await Promise.all([loadPluginConfigFile(globalConfigFilePath), pathExists(projectConfigFilePath)]);
73
+ const config = stripLegacyVoiceConfig(mergePluginConfigSources(globalConfig, options.config));
74
+ const ignoredProjectConfigFilePath = hasIgnoredProjectConfig ? projectConfigFilePath : void 0;
75
+ const configFilePath = globalConfigFilePath;
75
76
  return {
76
77
  cwd: options.cwd,
77
78
  config,
78
79
  globalConfigFilePath,
79
80
  projectConfigFilePath,
81
+ ...ignoredProjectConfigFilePath ? { ignoredProjectConfigFilePath } : {},
80
82
  configFilePath
81
83
  };
82
84
  }
@@ -170,4 +172,4 @@ function stripLegacyVoiceConfig(config) {
170
172
  //#endregion
171
173
  export { writePluginConfigFile as a, loadAppConfig as c, preparePluginConfiguration as i, getOpenCodeConfigFilePath as n, OPENCODE_TBOT_VERSION as o, mergePluginConfigSources as r, DEFAULT_TELEGRAM_API_ROOT as s, getGlobalPluginConfigFilePath as t };
172
174
 
173
- //# sourceMappingURL=plugin-config-B8ginwol.js.map
175
+ //# sourceMappingURL=plugin-config-CCeFjxSf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-config-CCeFjxSf.js","names":[],"sources":["../../src/app/config.ts","../../src/app/package-info.ts","../../src/app/plugin-config.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { z } from \"zod\";\n\nexport const DEFAULT_STATE_FILE_PATH = \"./data/opencode-tbot.state.json\";\nexport const DEFAULT_TELEGRAM_API_ROOT = \"https://api.telegram.org\";\n\nconst AllowedChatIdSchema = z.union([\n z.number().int(),\n z.string().regex(/^-?\\d+$/u).transform((value) => Number(value)),\n]);\n\nconst TelegramConfigSchema = z.preprocess(\n (value) => value ?? {},\n z.object({\n botToken: z.string().trim().min(1),\n allowedChatIds: z.array(AllowedChatIdSchema).default([]),\n apiRoot: z.string().trim().url().default(DEFAULT_TELEGRAM_API_ROOT),\n }),\n);\n\nconst StateConfigSchema = z.preprocess(\n (value) => value ?? {},\n z.object({\n path: z.string().trim().min(1).default(DEFAULT_STATE_FILE_PATH),\n }),\n);\n\nconst AppConfigSchema = z.object({\n telegram: TelegramConfigSchema,\n state: StateConfigSchema,\n logLevel: z.string().default(\"info\"),\n});\n\nexport interface PluginConfigSource {\n telegram?: {\n botToken?: string;\n allowedChatIds?: Array<number | string>;\n apiRoot?: string;\n [key: string]: unknown;\n };\n state?: {\n path?: string;\n [key: string]: unknown;\n };\n logLevel?: string;\n [key: string]: unknown;\n}\n\nexport interface AppConfig {\n telegramBotToken: string;\n telegramAllowedChatIds: number[];\n telegramApiRoot: string;\n logLevel: string;\n stateFilePath: string;\n}\n\nexport interface LoadAppConfigOptions {\n cwd?: string;\n}\n\nexport function loadAppConfig(\n configSource: PluginConfigSource | undefined = {},\n options: LoadAppConfigOptions = {},\n): AppConfig {\n const parsed = parseConfig(AppConfigSchema, configSource);\n\n return buildAppConfig(parsed, options);\n}\n\nexport const loadPluginConfig = loadAppConfig;\n\nfunction buildAppConfig(\n data: z.infer<typeof AppConfigSchema>,\n options: LoadAppConfigOptions,\n): AppConfig {\n return {\n telegramBotToken: data.telegram.botToken,\n telegramAllowedChatIds: data.telegram.allowedChatIds,\n telegramApiRoot: normalizeApiRoot(data.telegram.apiRoot),\n logLevel: data.logLevel,\n stateFilePath: resolveStatePath(data, options.cwd ?? process.cwd()),\n };\n}\n\nfunction resolveStatePath(\n data: z.infer<typeof AppConfigSchema>,\n cwd: string,\n): string {\n return resolve(cwd, data.state.path || DEFAULT_STATE_FILE_PATH);\n}\n\nfunction normalizeApiRoot(value: string): string {\n const normalized = value.trim();\n\n return normalized.endsWith(\"/\")\n ? normalized.slice(0, -1)\n : normalized;\n}\n\nfunction parseConfig<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n configSource: PluginConfigSource | undefined,\n): z.infer<TSchema> {\n const parsed = schema.safeParse(configSource ?? {});\n\n if (parsed.success) {\n return parsed.data;\n }\n\n throw new Error(\n `Invalid plugin configuration: ${JSON.stringify(parsed.error.flatten())}`,\n );\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport const OPENCODE_TBOT_VERSION = resolvePackageVersion();\n\nfunction resolvePackageVersion(): string {\n let directory = dirname(fileURLToPath(import.meta.url));\n\n while (true) {\n const packageFilePath = join(directory, \"package.json\");\n\n if (existsSync(packageFilePath)) {\n try {\n const parsed = JSON.parse(readFileSync(packageFilePath, \"utf8\")) as {\n version?: unknown;\n };\n\n if (typeof parsed.version === \"string\" && parsed.version.trim().length > 0) {\n return parsed.version;\n }\n } catch {\n // Fall through and continue searching parent directories.\n }\n }\n\n const parentDirectory = dirname(directory);\n\n if (parentDirectory === directory) {\n break;\n }\n\n directory = parentDirectory;\n }\n\n return \"unknown\";\n}\n","import { access, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { PluginConfigSource } from \"./config.js\";\n\nexport const PLUGIN_CONFIG_FILE_NAME = \"tbot.config.json\";\nexport const GLOBAL_PLUGIN_DIRECTORY_NAME = \"opencode-tbot\";\nexport const GLOBAL_PLUGIN_CONFIG_FILE_NAME = \"config.json\";\nexport const OPENCODE_CONFIG_FILE_NAME = \"opencode.json\";\n\nexport interface PreparedPluginConfiguration {\n cwd: string;\n config: PluginConfigSource;\n globalConfigFilePath: string;\n projectConfigFilePath: string;\n ignoredProjectConfigFilePath?: string;\n configFilePath: string;\n}\n\nexport interface PreparePluginConfigurationOptions {\n cwd: string;\n config?: PluginConfigSource;\n homeDir?: string;\n}\n\nexport async function preparePluginConfiguration(\n options: PreparePluginConfigurationOptions,\n): Promise<PreparedPluginConfiguration> {\n const homeDir = options.homeDir ?? homedir();\n const globalConfigFilePath = getGlobalPluginConfigFilePath(homeDir);\n const projectConfigFilePath = await resolveProjectPluginConfigFilePath(options.cwd);\n const [globalConfig, hasIgnoredProjectConfig] = await Promise.all([\n loadPluginConfigFile(globalConfigFilePath),\n pathExists(projectConfigFilePath),\n ]);\n const config = stripLegacyVoiceConfig(mergePluginConfigSources(globalConfig, options.config));\n const ignoredProjectConfigFilePath = hasIgnoredProjectConfig\n ? projectConfigFilePath\n : undefined;\n const configFilePath = globalConfigFilePath;\n\n return {\n cwd: options.cwd,\n config,\n globalConfigFilePath,\n projectConfigFilePath,\n ...(ignoredProjectConfigFilePath ? { ignoredProjectConfigFilePath } : {}),\n configFilePath,\n };\n}\n\nexport function getOpenCodeConfigDirectory(homeDir: string = homedir()): string {\n return join(homeDir, \".config\", \"opencode\");\n}\n\nexport function getOpenCodeConfigFilePath(homeDir: string = homedir()): string {\n return join(getOpenCodeConfigDirectory(homeDir), OPENCODE_CONFIG_FILE_NAME);\n}\n\nexport function getGlobalPluginConfigFilePath(homeDir: string = homedir()): string {\n return join(\n getOpenCodeConfigDirectory(homeDir),\n GLOBAL_PLUGIN_DIRECTORY_NAME,\n GLOBAL_PLUGIN_CONFIG_FILE_NAME,\n );\n}\n\nexport async function writePluginConfigFile(\n configFilePath: string,\n config: PluginConfigSource,\n): Promise<void> {\n await mkdir(dirname(configFilePath), { recursive: true });\n await writeFile(configFilePath, serializePluginConfig(config), \"utf8\");\n}\n\nexport function mergePluginConfigSources(\n ...sources: Array<PluginConfigSource | undefined>\n): PluginConfigSource {\n const merged: PluginConfigSource = {};\n\n for (const source of sources) {\n if (!source) {\n continue;\n }\n\n const normalized = source;\n const previousTelegram = merged.telegram;\n const previousState = merged.state;\n\n Object.assign(merged, normalized);\n\n if (normalized.telegram) {\n merged.telegram = {\n ...(previousTelegram ?? {}),\n ...normalized.telegram,\n };\n }\n\n if (normalized.state) {\n merged.state = {\n ...(previousState ?? {}),\n ...normalized.state,\n };\n }\n }\n\n return merged;\n}\n\nexport function serializePluginConfig(config: PluginConfigSource): string {\n return `${JSON.stringify(orderPluginConfig(config), null, 2)}\\n`;\n}\n\nasync function loadPluginConfigFile(configFilePath: string): Promise<PluginConfigSource> {\n try {\n const content = await readFile(configFilePath, \"utf8\");\n\n return parsePluginConfigText(content, configFilePath);\n } catch (error) {\n if (isMissingFileError(error)) {\n return {};\n }\n\n throw error;\n }\n}\n\nfunction parsePluginConfigText(\n content: string,\n configFilePath: string,\n): PluginConfigSource {\n try {\n const parsed = JSON.parse(content) as unknown;\n\n if (!isPlainObject(parsed)) {\n throw new Error(\"Config root must be a JSON object.\");\n }\n\n return parsed as PluginConfigSource;\n } catch (error) {\n throw new Error(\n [\n `Failed to parse ${configFilePath} as JSON.`,\n error instanceof Error ? error.message : String(error),\n ].join(\" \"),\n );\n }\n}\n\nfunction orderPluginConfig(config: PluginConfigSource): PluginConfigSource {\n const prioritizedKeys = new Set([\n \"telegram\",\n \"state\",\n \"logLevel\",\n ]);\n const ordered: PluginConfigSource = {};\n\n if (config.telegram) {\n ordered.telegram = config.telegram;\n }\n\n if (config.state) {\n ordered.state = config.state;\n }\n\n if (config.logLevel !== undefined) {\n ordered.logLevel = config.logLevel;\n }\n\n for (const [key, value] of Object.entries(config)) {\n if (!prioritizedKeys.has(key)) {\n ordered[key] = value;\n }\n }\n\n return ordered;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isMissingFileError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n}\n\nasync function resolveProjectPluginConfigFilePath(cwd: string): Promise<string> {\n const preferredPath = join(cwd, PLUGIN_CONFIG_FILE_NAME);\n\n return preferredPath;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch (error) {\n if (isMissingFileError(error)) {\n return false;\n }\n\n throw error;\n }\n}\n\nfunction stripLegacyVoiceConfig(config: PluginConfigSource): PluginConfigSource {\n const { openrouter: _openrouter, ...rest } = config as PluginConfigSource & {\n openrouter?: unknown;\n };\n\n return rest;\n}\n"],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as writePluginConfigFile, n as getOpenCodeConfigFilePath, o as OPENCODE_TBOT_VERSION, r as mergePluginConfigSources, t as getGlobalPluginConfigFilePath } from "./assets/plugin-config-B8ginwol.js";
1
+ import { a as writePluginConfigFile, n as getOpenCodeConfigFilePath, o as OPENCODE_TBOT_VERSION, r as mergePluginConfigSources, t as getGlobalPluginConfigFilePath } from "./assets/plugin-config-CCeFjxSf.js";
2
2
  import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
4
  import { dirname, join, resolve } from "node:path";
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import "./assets/plugin-config-B8ginwol.js";
1
+ import "./assets/plugin-config-CCeFjxSf.js";
2
2
  import { TelegramBotPlugin, ensureTelegramBotPluginRuntime, resetTelegramBotPluginRuntimeForTests } from "./plugin.js";
3
3
  export { TelegramBotPlugin, TelegramBotPlugin as default, ensureTelegramBotPluginRuntime, resetTelegramBotPluginRuntimeForTests };
package/dist/plugin.js CHANGED
@@ -1,4 +1,4 @@
1
- import { c as loadAppConfig, i as preparePluginConfiguration, o as OPENCODE_TBOT_VERSION } from "./assets/plugin-config-B8ginwol.js";
1
+ import { c as loadAppConfig, i as preparePluginConfiguration, o as OPENCODE_TBOT_VERSION } from "./assets/plugin-config-CCeFjxSf.js";
2
2
  import { mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
3
3
  import { dirname, isAbsolute, join } from "node:path";
4
4
  import { parse, printParseErrorCode } from "jsonc-parser";
@@ -398,16 +398,16 @@ var OpenCodeClient = class {
398
398
  }
399
399
  async resolvePromptResponse(input, data, knownMessageIds, startedAt) {
400
400
  const structured = input.structured ?? false;
401
- if (!shouldPollPromptMessage(data, structured)) return data;
402
- const messageId = extractMessageId(data.info);
401
+ if (data && !shouldPollPromptMessage(data, structured)) return data;
402
+ const messageId = data ? extractMessageId(data.info) : null;
403
403
  const candidateOptions = {
404
404
  initialMessageId: messageId,
405
- initialParentId: toAssistantMessage(data.info)?.parentID ?? null,
405
+ initialParentId: data ? toAssistantMessage(data.info)?.parentID ?? null : null,
406
406
  knownMessageIds,
407
407
  requestStartedAt: resolvePromptCandidateStartTime(startedAt, data),
408
408
  structured
409
409
  };
410
- let bestCandidate = selectPromptResponseCandidate([data], candidateOptions) ?? data;
410
+ let bestCandidate = selectPromptResponseCandidate(data ? [data] : [], candidateOptions);
411
411
  const deadlineAt = Date.now() + this.promptRequestTimeouts.totalPollMs;
412
412
  let idleStatusSeen = false;
413
413
  let attempt = 0;
@@ -422,14 +422,16 @@ var OpenCodeClient = class {
422
422
  if (messageId) {
423
423
  const next = await this.fetchPromptMessage(input.sessionId, messageId);
424
424
  if (next) {
425
- bestCandidate = selectPromptResponseCandidate([bestCandidate, next], candidateOptions) ?? bestCandidate;
426
- if (!shouldPollPromptMessage(next, structured)) return bestCandidate;
425
+ const nextCandidate = selectPromptResponseCandidate([bestCandidate, next], candidateOptions);
426
+ if (nextCandidate) bestCandidate = nextCandidate;
427
+ if (bestCandidate && !shouldPollPromptMessage(bestCandidate, structured)) return bestCandidate;
427
428
  }
428
429
  }
429
430
  const latest = await this.findLatestPromptResponse(input.sessionId, candidateOptions, "poll-messages");
430
431
  if (latest) {
431
- bestCandidate = selectPromptResponseCandidate([bestCandidate, latest], candidateOptions) ?? bestCandidate;
432
- if (!shouldPollPromptMessage(bestCandidate, structured)) return bestCandidate;
432
+ const nextCandidate = selectPromptResponseCandidate([bestCandidate, latest], candidateOptions);
433
+ if (nextCandidate) bestCandidate = nextCandidate;
434
+ if (bestCandidate && !shouldPollPromptMessage(bestCandidate, structured)) return bestCandidate;
433
435
  }
434
436
  if ((await this.fetchPromptSessionStatus(input.sessionId))?.type === "idle") {
435
437
  if (idleStatusSeen) break;
@@ -438,8 +440,8 @@ var OpenCodeClient = class {
438
440
  if (Date.now() >= deadlineAt) break;
439
441
  }
440
442
  const latest = await this.findLatestPromptResponse(input.sessionId, candidateOptions, "final-scan");
441
- const resolved = selectPromptResponseCandidate([bestCandidate, latest], candidateOptions) ?? bestCandidate;
442
- if (shouldPollPromptMessage(resolved, structured)) {
443
+ const resolved = selectPromptResponseCandidate([bestCandidate, latest], candidateOptions);
444
+ if (!resolved || shouldPollPromptMessage(resolved, structured)) {
443
445
  const error = createOpenCodePromptTimeoutError({
444
446
  sessionId: input.sessionId,
445
447
  stage: "final-scan",
@@ -568,7 +570,31 @@ var OpenCodeClient = class {
568
570
  return unwrapSdkData(await this.client.config.providers(void 0, SDK_OPTIONS));
569
571
  }
570
572
  async sendPromptRequest(input, parts) {
573
+ const requestBody = {
574
+ ...input.agent ? { agent: input.agent } : {},
575
+ ...input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {},
576
+ ...input.model ? { model: input.model } : {},
577
+ ...input.variant ? { variant: input.variant } : {},
578
+ parts
579
+ };
580
+ const requestParameters = {
581
+ sessionID: input.sessionId,
582
+ ...requestBody
583
+ };
571
584
  try {
585
+ if (typeof this.client.session?.promptAsync === "function") {
586
+ await this.runPromptRequestWithTimeout({
587
+ sessionId: input.sessionId,
588
+ stage: "send-prompt",
589
+ timeoutMs: this.promptRequestTimeouts.sendMs
590
+ }, async (signal) => {
591
+ await this.client.session.promptAsync(requestParameters, {
592
+ ...SDK_OPTIONS,
593
+ signal
594
+ });
595
+ });
596
+ return null;
597
+ }
572
598
  return await this.runPromptRequestWithTimeout({
573
599
  sessionId: input.sessionId,
574
600
  stage: "send-prompt",
@@ -577,23 +603,10 @@ var OpenCodeClient = class {
577
603
  if (hasRawSdkMethod(this.client, "post")) return normalizePromptResponse(await this.requestRaw("post", {
578
604
  url: "/session/{sessionID}/message",
579
605
  path: { sessionID: input.sessionId },
580
- body: {
581
- ...input.agent ? { agent: input.agent } : {},
582
- ...input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {},
583
- ...input.model ? { model: input.model } : {},
584
- ...input.variant ? { variant: input.variant } : {},
585
- parts
586
- },
606
+ body: requestBody,
587
607
  signal
588
608
  }));
589
- return normalizePromptResponse(unwrapSdkData(await this.client.session.prompt({
590
- sessionID: input.sessionId,
591
- ...input.agent ? { agent: input.agent } : {},
592
- ...input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {},
593
- ...input.model ? { model: input.model } : {},
594
- ...input.variant ? { variant: input.variant } : {},
595
- parts
596
- }, {
609
+ return normalizePromptResponse(unwrapSdkData(await this.client.session.prompt(requestParameters, {
597
610
  ...SDK_OPTIONS,
598
611
  signal
599
612
  })));
@@ -952,6 +965,7 @@ function getPromptResponseCandidateRank(message, options) {
952
965
  };
953
966
  }
954
967
  function resolvePromptCandidateStartTime(startedAt, initialMessage) {
968
+ if (!initialMessage) return null;
955
969
  const initialCreatedAt = coerceFiniteNumber(toAssistantMessage(initialMessage.info)?.time?.created);
956
970
  if (initialCreatedAt === null) return startedAt;
957
971
  return areComparablePromptTimestamps(startedAt, initialCreatedAt) ? startedAt : initialCreatedAt;
@@ -4679,6 +4693,11 @@ async function startPluginRuntime(options, cwd) {
4679
4693
  });
4680
4694
  const { config, container } = bootstrapApp(options.context.client, preparedConfiguration.config, { cwd: preparedConfiguration.cwd });
4681
4695
  try {
4696
+ if (preparedConfiguration.ignoredProjectConfigFilePath) container.logger.warn({
4697
+ cwd: preparedConfiguration.cwd,
4698
+ ignoredProjectConfigFilePath: preparedConfiguration.ignoredProjectConfigFilePath,
4699
+ globalConfigFilePath: preparedConfiguration.globalConfigFilePath
4700
+ }, "legacy worktree plugin config is ignored; migrate settings to the global opencode-tbot config");
4682
4701
  const runtime = await startRuntime({
4683
4702
  config,
4684
4703
  container
@@ -4686,7 +4705,7 @@ async function startPluginRuntime(options, cwd) {
4686
4705
  container.logger.info({
4687
4706
  cwd: preparedConfiguration.cwd,
4688
4707
  globalConfigFilePath: preparedConfiguration.globalConfigFilePath,
4689
- projectConfigFilePath: preparedConfiguration.projectConfigFilePath,
4708
+ ignoredProjectConfigFilePath: preparedConfiguration.ignoredProjectConfigFilePath,
4690
4709
  configFilePath: preparedConfiguration.configFilePath,
4691
4710
  mode: "plugin"
4692
4711
  }, "telegram bot plugin runtime started");