opencode-tbot 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,7 +22,7 @@ A Telegram plugin for driving [OpenCode](https://opencode.ai) from chat.
22
22
  Run:
23
23
 
24
24
  ```bash
25
- npx opencode-tbot@latest install
25
+ npm install opencode-tbot@latest
26
26
  ```
27
27
 
28
28
  The installer registers the plugin globally and writes the default runtime config.
package/README.zh-CN.md CHANGED
@@ -22,7 +22,7 @@
22
22
  执行:
23
23
 
24
24
  ```bash
25
- npx opencode-tbot@latest install
25
+ npm install opencode-tbot@latest
26
26
  ```
27
27
 
28
28
  安装器会注册全局插件并写入默认运行时配置。
package/dist/plugin.js CHANGED
@@ -214,6 +214,15 @@ function buildOpenCodeSdkConfig(options) {
214
214
  };
215
215
  }
216
216
  var EMPTY_RESPONSE_TEXT = "OpenCode returned empty response.";
217
+ var PROMPT_MESSAGE_POLL_DELAYS_MS = [
218
+ 0,
219
+ 150,
220
+ 300,
221
+ 600,
222
+ 1200,
223
+ 2e3,
224
+ 3200
225
+ ];
217
226
  var STRUCTURED_REPLY_SCHEMA = {
218
227
  type: "json_schema",
219
228
  retryCount: 2,
@@ -327,7 +336,7 @@ var OpenCodeClient = class {
327
336
  url: file.url
328
337
  }))];
329
338
  if (parts.length === 0) throw new Error("Prompt requires text or file attachments.");
330
- const data = unwrapSdkData(await this.client.session.prompt({
339
+ const initialData = unwrapSdkData(await this.client.session.prompt({
331
340
  sessionID: input.sessionId,
332
341
  ...input.agent ? { agent: input.agent } : {},
333
342
  ...input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {},
@@ -335,19 +344,36 @@ var OpenCodeClient = class {
335
344
  ...input.variant ? { variant: input.variant } : {},
336
345
  parts
337
346
  }, SDK_OPTIONS));
338
- const finishedAt = Date.now();
339
- const bodyMd = input.structured ? extractStructuredMarkdown(data.info?.structured) : null;
340
- const responseParts = Array.isArray(data.parts) ? data.parts : [];
341
- const fallbackText = extractTextFromParts(responseParts) || bodyMd || EMPTY_RESPONSE_TEXT;
342
- return {
343
- assistantError: data.info?.error ?? null,
344
- bodyMd,
345
- fallbackText,
346
- info: data.info ?? null,
347
- metrics: extractPromptMetrics(data.info, startedAt, finishedAt),
348
- parts: responseParts,
349
- structured: data.info?.structured ?? null
350
- };
347
+ return buildPromptSessionResult(await this.resolvePromptResponse(input, initialData), {
348
+ emptyResponseText: EMPTY_RESPONSE_TEXT,
349
+ finishedAt: Date.now(),
350
+ startedAt,
351
+ structured: input.structured ?? false
352
+ });
353
+ }
354
+ async resolvePromptResponse(input, data) {
355
+ if (!shouldPollPromptMessage(data, input.structured ?? false)) return data;
356
+ const messageId = data.info?.id;
357
+ if (!messageId) return data;
358
+ for (const delayMs of PROMPT_MESSAGE_POLL_DELAYS_MS) {
359
+ if (delayMs > 0) await delay(delayMs);
360
+ const next = await this.fetchPromptMessage(input.sessionId, messageId);
361
+ if (!next) continue;
362
+ data = next;
363
+ if (!shouldPollPromptMessage(data, input.structured ?? false)) return data;
364
+ }
365
+ return data;
366
+ }
367
+ async fetchPromptMessage(sessionId, messageId) {
368
+ if (typeof this.client.session.message !== "function") return null;
369
+ try {
370
+ return unwrapSdkData(await this.client.session.message({
371
+ sessionID: sessionId,
372
+ messageID: messageId
373
+ }, SDK_OPTIONS));
374
+ } catch {
375
+ return null;
376
+ }
351
377
  }
352
378
  async loadModels() {
353
379
  const [configResponse, providersResponse] = await Promise.all([this.client.config.get(void 0, SDK_OPTIONS), this.client.config.providers(void 0, SDK_OPTIONS)]);
@@ -450,6 +476,36 @@ function extractTextFromParts(parts) {
450
476
  if (!Array.isArray(parts)) return "";
451
477
  return parts.filter((part) => part.type === "text").map((part) => part.text).join("").trim();
452
478
  }
479
+ function buildPromptSessionResult(data, options) {
480
+ const assistantInfo = toAssistantMessage(data.info);
481
+ const bodyMd = options.structured ? extractStructuredMarkdown(assistantInfo?.structured) : null;
482
+ const responseParts = Array.isArray(data.parts) ? data.parts : [];
483
+ const fallbackText = extractTextFromParts(responseParts) || bodyMd || options.emptyResponseText;
484
+ return {
485
+ assistantError: assistantInfo?.error ?? null,
486
+ bodyMd,
487
+ fallbackText,
488
+ info: assistantInfo,
489
+ metrics: extractPromptMetrics(assistantInfo, options.startedAt, options.finishedAt),
490
+ parts: responseParts,
491
+ structured: assistantInfo?.structured ?? null
492
+ };
493
+ }
494
+ function shouldPollPromptMessage(data, structured) {
495
+ const assistantInfo = toAssistantMessage(data.info);
496
+ const bodyMd = structured ? extractStructuredMarkdown(assistantInfo?.structured) : null;
497
+ const hasText = extractTextFromParts(Array.isArray(data.parts) ? data.parts : []).length > 0;
498
+ const hasAssistantError = !!assistantInfo?.error;
499
+ const isCompleted = typeof assistantInfo?.time?.completed === "number" && Number.isFinite(assistantInfo.time.completed);
500
+ return !hasText && !bodyMd && !hasAssistantError && !isCompleted;
501
+ }
502
+ function toAssistantMessage(message) {
503
+ if (!message || typeof message !== "object") return null;
504
+ return !("role" in message) || message.role === "assistant" ? message : null;
505
+ }
506
+ function delay(ms) {
507
+ return new Promise((resolve) => setTimeout(resolve, ms));
508
+ }
453
509
  function extractStructuredMarkdown(structured) {
454
510
  const parsed = StructuredReplySchema.safeParse(structured);
455
511
  if (!parsed.success) return null;