opencode-tbot 0.1.10 → 0.1.12
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/dist/plugin.js +270 -100
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -211,18 +211,28 @@ function buildOpenCodeSdkConfig(options) {
|
|
|
211
211
|
...apiKey ? { auth: apiKey } : {}
|
|
212
212
|
};
|
|
213
213
|
}
|
|
214
|
+
var OpenCodePromptTimeoutError = class extends Error {
|
|
215
|
+
data;
|
|
216
|
+
constructor(data) {
|
|
217
|
+
super(data.message ?? "OpenCode prompt timed out.");
|
|
218
|
+
this.name = "OpenCodePromptTimeoutError";
|
|
219
|
+
this.data = data;
|
|
220
|
+
}
|
|
221
|
+
};
|
|
214
222
|
var EMPTY_RESPONSE_TEXT = "OpenCode returned empty response.";
|
|
215
|
-
var
|
|
223
|
+
var PROMPT_MESSAGE_POLL_INITIAL_DELAYS_MS = [
|
|
216
224
|
0,
|
|
217
225
|
100,
|
|
218
226
|
250,
|
|
219
227
|
500,
|
|
220
|
-
1e3
|
|
221
|
-
2e3,
|
|
222
|
-
4e3,
|
|
223
|
-
8e3
|
|
228
|
+
1e3
|
|
224
229
|
];
|
|
230
|
+
var PROMPT_MESSAGE_POLL_INTERVAL_MS = 2e3;
|
|
231
|
+
var PROMPT_POLL_REQUEST_TIMEOUT_MS = 15e3;
|
|
232
|
+
var PROMPT_SEND_TIMEOUT_MS = 3e4;
|
|
233
|
+
var PROMPT_MESSAGE_POLL_TIMEOUT_MS = 6e4;
|
|
225
234
|
var PROMPT_MESSAGE_POLL_LIMIT = 20;
|
|
235
|
+
var PROMPT_LOG_SERVICE = "opencode-tbot";
|
|
226
236
|
var STRUCTURED_REPLY_SCHEMA = {
|
|
227
237
|
type: "json_schema",
|
|
228
238
|
retryCount: 2,
|
|
@@ -244,6 +254,11 @@ var StructuredReplySchema = z.object({ body_md: z.string() });
|
|
|
244
254
|
var OpenCodeClient = class {
|
|
245
255
|
client;
|
|
246
256
|
fetchFn;
|
|
257
|
+
promptRequestTimeouts = {
|
|
258
|
+
pollRequestMs: PROMPT_POLL_REQUEST_TIMEOUT_MS,
|
|
259
|
+
sendMs: PROMPT_SEND_TIMEOUT_MS,
|
|
260
|
+
totalPollMs: PROMPT_MESSAGE_POLL_TIMEOUT_MS
|
|
261
|
+
};
|
|
247
262
|
modelCache = {
|
|
248
263
|
expiresAt: 0,
|
|
249
264
|
promise: null,
|
|
@@ -288,8 +303,7 @@ var OpenCodeClient = class {
|
|
|
288
303
|
return unwrapSdkData(await this.client.mcp.status(directory ? { directory } : void 0, SDK_OPTIONS));
|
|
289
304
|
}
|
|
290
305
|
async getSessionStatuses() {
|
|
291
|
-
|
|
292
|
-
return unwrapSdkData(await this.client.session.status(void 0, SDK_OPTIONS));
|
|
306
|
+
return this.loadSessionStatuses();
|
|
293
307
|
}
|
|
294
308
|
async listProjects() {
|
|
295
309
|
if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", { url: "/project" });
|
|
@@ -376,117 +390,162 @@ var OpenCodeClient = class {
|
|
|
376
390
|
if (parts.length === 0) throw new Error("Prompt requires text or file attachments.");
|
|
377
391
|
const knownMessageIds = await this.captureKnownMessageIds(input.sessionId);
|
|
378
392
|
const initialData = await this.sendPromptRequest(input, parts);
|
|
379
|
-
return buildPromptSessionResult(await this.resolvePromptResponse(input, initialData, knownMessageIds), {
|
|
393
|
+
return buildPromptSessionResult(await this.resolvePromptResponse(input, initialData, knownMessageIds, startedAt), {
|
|
380
394
|
emptyResponseText: EMPTY_RESPONSE_TEXT,
|
|
381
395
|
finishedAt: Date.now(),
|
|
382
396
|
startedAt,
|
|
383
397
|
structured: input.structured ?? false
|
|
384
398
|
});
|
|
385
399
|
}
|
|
386
|
-
async resolvePromptResponse(input, data, knownMessageIds) {
|
|
400
|
+
async resolvePromptResponse(input, data, knownMessageIds, startedAt) {
|
|
387
401
|
const structured = input.structured ?? false;
|
|
388
402
|
if (!shouldPollPromptMessage(data, structured)) return data;
|
|
389
403
|
const messageId = extractMessageId(data.info);
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
404
|
+
const candidateOptions = {
|
|
405
|
+
initialMessageId: messageId,
|
|
406
|
+
initialParentId: toAssistantMessage(data.info)?.parentID ?? null,
|
|
407
|
+
knownMessageIds,
|
|
408
|
+
requestStartedAt: resolvePromptCandidateStartTime(startedAt, data),
|
|
409
|
+
structured
|
|
410
|
+
};
|
|
411
|
+
let bestCandidate = selectPromptResponseCandidate([data], candidateOptions) ?? data;
|
|
412
|
+
const deadlineAt = Date.now() + this.promptRequestTimeouts.totalPollMs;
|
|
413
|
+
let idleStatusSeen = false;
|
|
414
|
+
let attempt = 0;
|
|
415
|
+
while (true) {
|
|
416
|
+
const delayMs = getPromptMessagePollDelayMs(attempt);
|
|
417
|
+
attempt += 1;
|
|
394
418
|
if (delayMs > 0) {
|
|
395
|
-
|
|
396
|
-
|
|
419
|
+
const remainingMs = deadlineAt - Date.now();
|
|
420
|
+
if (remainingMs <= 0) break;
|
|
421
|
+
await delay(Math.min(delayMs, remainingMs));
|
|
397
422
|
}
|
|
398
423
|
if (messageId) {
|
|
399
424
|
const next = await this.fetchPromptMessage(input.sessionId, messageId);
|
|
400
425
|
if (next) {
|
|
401
|
-
bestCandidate = selectPromptResponseCandidate([bestCandidate, next],
|
|
402
|
-
|
|
403
|
-
initialParentId: expectedParentId,
|
|
404
|
-
knownMessageIds,
|
|
405
|
-
structured
|
|
406
|
-
}) ?? bestCandidate;
|
|
407
|
-
expectedParentId = toAssistantMessage(next.info)?.parentID ?? expectedParentId;
|
|
408
|
-
if (!shouldPollPromptMessage(next, structured)) return next;
|
|
426
|
+
bestCandidate = selectPromptResponseCandidate([bestCandidate, next], candidateOptions) ?? bestCandidate;
|
|
427
|
+
if (!shouldPollPromptMessage(next, structured)) return bestCandidate;
|
|
409
428
|
}
|
|
410
429
|
}
|
|
411
|
-
const latest = await this.findLatestPromptResponse(input.sessionId,
|
|
412
|
-
initialMessageId: messageId,
|
|
413
|
-
initialParentId: expectedParentId,
|
|
414
|
-
knownMessageIds,
|
|
415
|
-
structured
|
|
416
|
-
});
|
|
430
|
+
const latest = await this.findLatestPromptResponse(input.sessionId, candidateOptions, "poll-messages");
|
|
417
431
|
if (latest) {
|
|
418
|
-
bestCandidate = selectPromptResponseCandidate([bestCandidate, latest],
|
|
419
|
-
|
|
420
|
-
initialParentId: expectedParentId,
|
|
421
|
-
knownMessageIds,
|
|
422
|
-
structured
|
|
423
|
-
}) ?? bestCandidate;
|
|
424
|
-
expectedParentId = toAssistantMessage(latest.info)?.parentID ?? expectedParentId;
|
|
425
|
-
if (!shouldPollPromptMessage(latest, structured)) return latest;
|
|
432
|
+
bestCandidate = selectPromptResponseCandidate([bestCandidate, latest], candidateOptions) ?? bestCandidate;
|
|
433
|
+
if (!shouldPollPromptMessage(bestCandidate, structured)) return bestCandidate;
|
|
426
434
|
}
|
|
427
|
-
if ((await this.fetchPromptSessionStatus(input.sessionId))?.type === "idle"
|
|
435
|
+
if ((await this.fetchPromptSessionStatus(input.sessionId))?.type === "idle") {
|
|
436
|
+
if (idleStatusSeen) break;
|
|
437
|
+
idleStatusSeen = true;
|
|
438
|
+
}
|
|
439
|
+
if (Date.now() >= deadlineAt) break;
|
|
428
440
|
}
|
|
429
|
-
const latest = await this.findLatestPromptResponse(input.sessionId,
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
+
const latest = await this.findLatestPromptResponse(input.sessionId, candidateOptions, "final-scan");
|
|
442
|
+
const resolved = selectPromptResponseCandidate([bestCandidate, latest], candidateOptions) ?? bestCandidate;
|
|
443
|
+
if (shouldPollPromptMessage(resolved, structured)) {
|
|
444
|
+
const error = createOpenCodePromptTimeoutError({
|
|
445
|
+
sessionId: input.sessionId,
|
|
446
|
+
stage: "final-scan",
|
|
447
|
+
timeoutMs: this.promptRequestTimeouts.totalPollMs,
|
|
448
|
+
messageId: messageId ?? void 0
|
|
449
|
+
});
|
|
450
|
+
this.logPromptRequestFailure(error, {
|
|
451
|
+
sessionId: input.sessionId,
|
|
452
|
+
stage: "final-scan",
|
|
453
|
+
timeoutMs: this.promptRequestTimeouts.totalPollMs,
|
|
454
|
+
messageId
|
|
455
|
+
});
|
|
456
|
+
throw error;
|
|
457
|
+
}
|
|
458
|
+
return resolved;
|
|
441
459
|
}
|
|
442
460
|
async fetchPromptMessage(sessionId, messageId) {
|
|
443
461
|
try {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
462
|
+
return await this.runPromptRequestWithTimeout({
|
|
463
|
+
sessionId,
|
|
464
|
+
stage: "poll-message",
|
|
465
|
+
timeoutMs: this.promptRequestTimeouts.pollRequestMs,
|
|
466
|
+
messageId
|
|
467
|
+
}, async (signal) => {
|
|
468
|
+
if (hasRawSdkMethod(this.client, "get")) return normalizePromptResponse(await this.requestRaw("get", {
|
|
469
|
+
url: "/session/{sessionID}/message/{messageID}",
|
|
470
|
+
path: {
|
|
471
|
+
sessionID: sessionId,
|
|
472
|
+
messageID: messageId
|
|
473
|
+
},
|
|
474
|
+
signal
|
|
475
|
+
}));
|
|
476
|
+
if (typeof this.client.session.message !== "function") return null;
|
|
477
|
+
return normalizePromptResponse(unwrapSdkData(await this.client.session.message({
|
|
447
478
|
sessionID: sessionId,
|
|
448
479
|
messageID: messageId
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
480
|
+
}, {
|
|
481
|
+
...SDK_OPTIONS,
|
|
482
|
+
signal
|
|
483
|
+
})));
|
|
484
|
+
});
|
|
485
|
+
} catch (error) {
|
|
486
|
+
this.logPromptRequestFailure(error, {
|
|
487
|
+
sessionId,
|
|
488
|
+
stage: "poll-message",
|
|
489
|
+
timeoutMs: this.promptRequestTimeouts.pollRequestMs,
|
|
490
|
+
messageId
|
|
491
|
+
});
|
|
457
492
|
return null;
|
|
458
493
|
}
|
|
459
494
|
}
|
|
460
495
|
async captureKnownMessageIds(sessionId) {
|
|
461
|
-
const messages = await this.fetchRecentPromptMessages(sessionId);
|
|
496
|
+
const messages = await this.fetchRecentPromptMessages(sessionId, "capture-known-messages");
|
|
462
497
|
if (!messages) return /* @__PURE__ */ new Set();
|
|
463
498
|
return new Set(messages.map((message) => extractMessageId(message.info)).filter((id) => typeof id === "string" && id.length > 0));
|
|
464
499
|
}
|
|
465
|
-
async fetchRecentPromptMessages(sessionId) {
|
|
500
|
+
async fetchRecentPromptMessages(sessionId, stage) {
|
|
466
501
|
try {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
})
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
502
|
+
return await this.runPromptRequestWithTimeout({
|
|
503
|
+
sessionId,
|
|
504
|
+
stage,
|
|
505
|
+
timeoutMs: this.promptRequestTimeouts.pollRequestMs
|
|
506
|
+
}, async (signal) => {
|
|
507
|
+
if (hasRawSdkMethod(this.client, "get")) return normalizePromptResponses(await this.requestRaw("get", {
|
|
508
|
+
url: "/session/{sessionID}/message",
|
|
509
|
+
path: { sessionID: sessionId },
|
|
510
|
+
query: { limit: PROMPT_MESSAGE_POLL_LIMIT },
|
|
511
|
+
signal
|
|
512
|
+
}));
|
|
513
|
+
if (typeof this.client.session.messages !== "function") return null;
|
|
514
|
+
return normalizePromptResponses(unwrapSdkData(await this.client.session.messages({
|
|
515
|
+
sessionID: sessionId,
|
|
516
|
+
limit: PROMPT_MESSAGE_POLL_LIMIT
|
|
517
|
+
}, {
|
|
518
|
+
...SDK_OPTIONS,
|
|
519
|
+
signal
|
|
520
|
+
})));
|
|
521
|
+
});
|
|
522
|
+
} catch (error) {
|
|
523
|
+
this.logPromptRequestFailure(error, {
|
|
524
|
+
sessionId,
|
|
525
|
+
stage,
|
|
526
|
+
timeoutMs: this.promptRequestTimeouts.pollRequestMs
|
|
527
|
+
});
|
|
478
528
|
return null;
|
|
479
529
|
}
|
|
480
530
|
}
|
|
481
531
|
async fetchPromptSessionStatus(sessionId) {
|
|
482
532
|
try {
|
|
483
|
-
return (await this.
|
|
484
|
-
|
|
533
|
+
return (await this.runPromptRequestWithTimeout({
|
|
534
|
+
sessionId,
|
|
535
|
+
stage: "poll-status",
|
|
536
|
+
timeoutMs: this.promptRequestTimeouts.pollRequestMs
|
|
537
|
+
}, async (signal) => this.loadSessionStatuses(signal)))[sessionId] ?? null;
|
|
538
|
+
} catch (error) {
|
|
539
|
+
this.logPromptRequestFailure(error, {
|
|
540
|
+
sessionId,
|
|
541
|
+
stage: "poll-status",
|
|
542
|
+
timeoutMs: this.promptRequestTimeouts.pollRequestMs
|
|
543
|
+
});
|
|
485
544
|
return null;
|
|
486
545
|
}
|
|
487
546
|
}
|
|
488
|
-
async findLatestPromptResponse(sessionId, options) {
|
|
489
|
-
const messages = await this.fetchRecentPromptMessages(sessionId);
|
|
547
|
+
async findLatestPromptResponse(sessionId, options, stage) {
|
|
548
|
+
const messages = await this.fetchRecentPromptMessages(sessionId, stage);
|
|
490
549
|
if (!messages || messages.length === 0) return null;
|
|
491
550
|
return selectPromptResponseCandidate(messages, options);
|
|
492
551
|
}
|
|
@@ -510,25 +569,44 @@ var OpenCodeClient = class {
|
|
|
510
569
|
return unwrapSdkData(await this.client.config.providers(void 0, SDK_OPTIONS));
|
|
511
570
|
}
|
|
512
571
|
async sendPromptRequest(input, parts) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
572
|
+
try {
|
|
573
|
+
return await this.runPromptRequestWithTimeout({
|
|
574
|
+
sessionId: input.sessionId,
|
|
575
|
+
stage: "send-prompt",
|
|
576
|
+
timeoutMs: this.promptRequestTimeouts.sendMs
|
|
577
|
+
}, async (signal) => {
|
|
578
|
+
if (hasRawSdkMethod(this.client, "post")) return normalizePromptResponse(await this.requestRaw("post", {
|
|
579
|
+
url: "/session/{sessionID}/message",
|
|
580
|
+
path: { sessionID: input.sessionId },
|
|
581
|
+
body: {
|
|
582
|
+
...input.agent ? { agent: input.agent } : {},
|
|
583
|
+
...input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {},
|
|
584
|
+
...input.model ? { model: input.model } : {},
|
|
585
|
+
...input.variant ? { variant: input.variant } : {},
|
|
586
|
+
parts
|
|
587
|
+
},
|
|
588
|
+
signal
|
|
589
|
+
}));
|
|
590
|
+
return normalizePromptResponse(unwrapSdkData(await this.client.session.prompt({
|
|
591
|
+
sessionID: input.sessionId,
|
|
592
|
+
...input.agent ? { agent: input.agent } : {},
|
|
593
|
+
...input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {},
|
|
594
|
+
...input.model ? { model: input.model } : {},
|
|
595
|
+
...input.variant ? { variant: input.variant } : {},
|
|
596
|
+
parts
|
|
597
|
+
}, {
|
|
598
|
+
...SDK_OPTIONS,
|
|
599
|
+
signal
|
|
600
|
+
})));
|
|
601
|
+
});
|
|
602
|
+
} catch (error) {
|
|
603
|
+
this.logPromptRequestFailure(error, {
|
|
604
|
+
sessionId: input.sessionId,
|
|
605
|
+
stage: "send-prompt",
|
|
606
|
+
timeoutMs: this.promptRequestTimeouts.sendMs
|
|
607
|
+
});
|
|
608
|
+
throw error;
|
|
609
|
+
}
|
|
532
610
|
}
|
|
533
611
|
async requestRaw(method, options) {
|
|
534
612
|
const handler = getRawSdkClient(this.client)?.[method];
|
|
@@ -538,6 +616,67 @@ var OpenCodeClient = class {
|
|
|
538
616
|
...options
|
|
539
617
|
}));
|
|
540
618
|
}
|
|
619
|
+
async loadSessionStatuses(signal) {
|
|
620
|
+
if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", {
|
|
621
|
+
url: "/session/status",
|
|
622
|
+
...signal ? { signal } : {}
|
|
623
|
+
});
|
|
624
|
+
return unwrapSdkData(await this.client.session.status(void 0, {
|
|
625
|
+
...SDK_OPTIONS,
|
|
626
|
+
...signal ? { signal } : {}
|
|
627
|
+
}));
|
|
628
|
+
}
|
|
629
|
+
async runPromptRequestWithTimeout(input, operation) {
|
|
630
|
+
const startedAt = Date.now();
|
|
631
|
+
const controller = new AbortController();
|
|
632
|
+
let timeoutHandle = null;
|
|
633
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
634
|
+
timeoutHandle = setTimeout(() => {
|
|
635
|
+
reject(createOpenCodePromptTimeoutError({
|
|
636
|
+
sessionId: input.sessionId,
|
|
637
|
+
stage: input.stage,
|
|
638
|
+
timeoutMs: input.timeoutMs,
|
|
639
|
+
messageId: input.messageId ?? void 0,
|
|
640
|
+
elapsedMs: Date.now() - startedAt
|
|
641
|
+
}));
|
|
642
|
+
controller.abort();
|
|
643
|
+
}, input.timeoutMs);
|
|
644
|
+
});
|
|
645
|
+
try {
|
|
646
|
+
return await Promise.race([operation(controller.signal), timeoutPromise]);
|
|
647
|
+
} finally {
|
|
648
|
+
if (timeoutHandle !== null) clearTimeout(timeoutHandle);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
logPromptRequestFailure(error, input) {
|
|
652
|
+
if (error instanceof OpenCodePromptTimeoutError) {
|
|
653
|
+
this.logPromptRequest("warn", {
|
|
654
|
+
elapsedMs: error.data.elapsedMs,
|
|
655
|
+
messageId: error.data.messageId,
|
|
656
|
+
sessionId: error.data.sessionId,
|
|
657
|
+
stage: error.data.stage,
|
|
658
|
+
timeoutMs: error.data.timeoutMs
|
|
659
|
+
}, "OpenCode prompt request timed out");
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
this.logPromptRequest("warn", {
|
|
663
|
+
error,
|
|
664
|
+
messageId: input.messageId ?? void 0,
|
|
665
|
+
sessionId: input.sessionId,
|
|
666
|
+
stage: input.stage,
|
|
667
|
+
timeoutMs: input.timeoutMs
|
|
668
|
+
}, "OpenCode prompt request failed");
|
|
669
|
+
}
|
|
670
|
+
logPromptRequest(level, extra, message) {
|
|
671
|
+
const log = this.client.app?.log;
|
|
672
|
+
if (typeof log !== "function") return;
|
|
673
|
+
log.call(this.client.app, {
|
|
674
|
+
service: PROMPT_LOG_SERVICE,
|
|
675
|
+
level,
|
|
676
|
+
message,
|
|
677
|
+
extra
|
|
678
|
+
}).catch(() => void 0);
|
|
679
|
+
}
|
|
541
680
|
};
|
|
542
681
|
function createOpenCodeClientFromSdkClient(client, fetchFn = fetch) {
|
|
543
682
|
return new OpenCodeClient(void 0, client, fetchFn);
|
|
@@ -709,6 +848,16 @@ function extractMessageId(message) {
|
|
|
709
848
|
function delay(ms) {
|
|
710
849
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
711
850
|
}
|
|
851
|
+
function createOpenCodePromptTimeoutError(input) {
|
|
852
|
+
return new OpenCodePromptTimeoutError({
|
|
853
|
+
...input,
|
|
854
|
+
elapsedMs: input.elapsedMs ?? input.timeoutMs,
|
|
855
|
+
message: input.message ?? "The OpenCode host did not finish this request in time."
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
function getPromptMessagePollDelayMs(attempt) {
|
|
859
|
+
return PROMPT_MESSAGE_POLL_INITIAL_DELAYS_MS[attempt] ?? PROMPT_MESSAGE_POLL_INTERVAL_MS;
|
|
860
|
+
}
|
|
712
861
|
function extractStructuredMarkdown(structured) {
|
|
713
862
|
const parsed = StructuredReplySchema.safeParse(structured);
|
|
714
863
|
if (!parsed.success) return null;
|
|
@@ -789,19 +938,34 @@ function selectPromptResponseCandidate(candidates, options) {
|
|
|
789
938
|
function comparePromptResponseCandidates(left, right, options) {
|
|
790
939
|
const leftRank = getPromptResponseCandidateRank(left, options);
|
|
791
940
|
const rightRank = getPromptResponseCandidateRank(right, options);
|
|
792
|
-
return Number(rightRank.isUsable) - Number(leftRank.isUsable) || Number(rightRank.isInitial) - Number(leftRank.isInitial) || Number(rightRank.sharesParent) - Number(leftRank.sharesParent) || Number(rightRank.
|
|
941
|
+
return Number(rightRank.isUsable) - Number(leftRank.isUsable) || Number(rightRank.isInitial) - Number(leftRank.isInitial) || Number(rightRank.sharesParent) - Number(leftRank.sharesParent) || Number(rightRank.isNewSinceRequestStart) - Number(leftRank.isNewSinceRequestStart) || rightRank.createdAt - leftRank.createdAt;
|
|
793
942
|
}
|
|
794
943
|
function getPromptResponseCandidateRank(message, options) {
|
|
795
944
|
const assistant = toAssistantMessage(message.info);
|
|
796
945
|
const id = assistant?.id ?? null;
|
|
946
|
+
const createdAt = typeof assistant?.time?.created === "number" && Number.isFinite(assistant.time.created) ? assistant.time.created : 0;
|
|
797
947
|
return {
|
|
798
|
-
createdAt
|
|
948
|
+
createdAt,
|
|
799
949
|
isInitial: !!id && id === options.initialMessageId,
|
|
800
|
-
|
|
950
|
+
isNewSinceRequestStart: isPromptResponseNewSinceRequestStart(id, createdAt, options.knownMessageIds, options.requestStartedAt),
|
|
801
951
|
isUsable: !shouldPollPromptMessage(message, options.structured),
|
|
802
952
|
sharesParent: !!assistant?.parentID && assistant.parentID === options.initialParentId
|
|
803
953
|
};
|
|
804
954
|
}
|
|
955
|
+
function resolvePromptCandidateStartTime(startedAt, initialMessage) {
|
|
956
|
+
const initialCreatedAt = coerceFiniteNumber(toAssistantMessage(initialMessage.info)?.time?.created);
|
|
957
|
+
if (initialCreatedAt === null) return startedAt;
|
|
958
|
+
return areComparablePromptTimestamps(startedAt, initialCreatedAt) ? startedAt : initialCreatedAt;
|
|
959
|
+
}
|
|
960
|
+
function isPromptResponseNewSinceRequestStart(messageId, createdAt, knownMessageIds, requestStartedAt) {
|
|
961
|
+
if (!messageId || knownMessageIds.has(messageId)) return false;
|
|
962
|
+
if (requestStartedAt === null) return true;
|
|
963
|
+
return createdAt >= requestStartedAt;
|
|
964
|
+
}
|
|
965
|
+
function areComparablePromptTimestamps(left, right) {
|
|
966
|
+
const epochThresholdMs = 0xe8d4a51000;
|
|
967
|
+
return left >= epochThresholdMs && right >= epochThresholdMs;
|
|
968
|
+
}
|
|
805
969
|
function isPlainRecord(value) {
|
|
806
970
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
807
971
|
}
|
|
@@ -2331,6 +2495,7 @@ var EN_BOT_COPY = {
|
|
|
2331
2495
|
unexpected: "Unexpected error.",
|
|
2332
2496
|
providerAuth: "Provider authentication failed.",
|
|
2333
2497
|
requestAborted: "Request was aborted.",
|
|
2498
|
+
promptTimeout: "OpenCode request timed out.",
|
|
2334
2499
|
structuredOutput: "Structured output validation failed.",
|
|
2335
2500
|
voiceNotConfigured: "Voice transcription is not configured.",
|
|
2336
2501
|
voiceDownload: "Failed to download the Telegram voice file.",
|
|
@@ -2566,6 +2731,7 @@ var ZH_CN_BOT_COPY = {
|
|
|
2566
2731
|
unexpected: "发生未知错误。",
|
|
2567
2732
|
providerAuth: "Provider 认证失败。",
|
|
2568
2733
|
requestAborted: "请求已中止。",
|
|
2734
|
+
promptTimeout: "OpenCode 响应超时。",
|
|
2569
2735
|
structuredOutput: "结构化输出校验失败。",
|
|
2570
2736
|
voiceNotConfigured: "未配置语音转写服务。",
|
|
2571
2737
|
voiceDownload: "下载 Telegram 语音文件失败。",
|
|
@@ -2913,6 +3079,10 @@ function normalizeError(error, copy) {
|
|
|
2913
3079
|
message: copy.errors.requestAborted,
|
|
2914
3080
|
cause: extractMessage(error.data) ?? null
|
|
2915
3081
|
};
|
|
3082
|
+
if (isNamedError(error, "OpenCodePromptTimeoutError")) return {
|
|
3083
|
+
message: copy.errors.promptTimeout,
|
|
3084
|
+
cause: null
|
|
3085
|
+
};
|
|
2916
3086
|
if (isNamedError(error, "StructuredOutputError")) return {
|
|
2917
3087
|
message: copy.errors.structuredOutput,
|
|
2918
3088
|
cause: joinNonEmptyParts([extractMessage(error.data), extractRetries(error.data)])
|