opencode-tbot 0.1.9 → 0.1.11
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/cli.js +3 -7
- package/dist/cli.js.map +1 -1
- package/dist/plugin.js +269 -86
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -212,16 +212,15 @@ function buildOpenCodeSdkConfig(options) {
|
|
|
212
212
|
};
|
|
213
213
|
}
|
|
214
214
|
var EMPTY_RESPONSE_TEXT = "OpenCode returned empty response.";
|
|
215
|
-
var
|
|
215
|
+
var PROMPT_MESSAGE_POLL_INITIAL_DELAYS_MS = [
|
|
216
216
|
0,
|
|
217
217
|
100,
|
|
218
218
|
250,
|
|
219
219
|
500,
|
|
220
|
-
1e3
|
|
221
|
-
2e3,
|
|
222
|
-
4e3,
|
|
223
|
-
8e3
|
|
220
|
+
1e3
|
|
224
221
|
];
|
|
222
|
+
var PROMPT_MESSAGE_POLL_INTERVAL_MS = 2e3;
|
|
223
|
+
var PROMPT_MESSAGE_POLL_TIMEOUT_MS = 6e4;
|
|
225
224
|
var PROMPT_MESSAGE_POLL_LIMIT = 20;
|
|
226
225
|
var STRUCTURED_REPLY_SCHEMA = {
|
|
227
226
|
type: "json_schema",
|
|
@@ -255,58 +254,96 @@ var OpenCodeClient = class {
|
|
|
255
254
|
this.fetchFn = fetchFn;
|
|
256
255
|
}
|
|
257
256
|
async getHealth() {
|
|
257
|
+
const rawClient = getRawSdkClient(this.client);
|
|
258
|
+
if (rawClient?.get) return await this.requestRaw("get", { url: "/global/health" });
|
|
258
259
|
const healthEndpoint = this.client.global?.health;
|
|
259
260
|
if (typeof healthEndpoint === "function") return unwrapSdkData(await healthEndpoint.call(this.client.global, SDK_OPTIONS));
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
return unwrapSdkData(await rawClient.get({
|
|
263
|
-
url: "/global/health",
|
|
264
|
-
...SDK_OPTIONS
|
|
265
|
-
}));
|
|
261
|
+
if (!rawClient?.get) throw new Error("OpenCode SDK client does not expose a compatible health endpoint.");
|
|
262
|
+
return this.requestRaw("get", { url: "/global/health" });
|
|
266
263
|
}
|
|
267
264
|
async abortSession(sessionId) {
|
|
265
|
+
if (hasRawSdkMethod(this.client, "post")) return this.requestRaw("post", {
|
|
266
|
+
url: "/session/{sessionID}/abort",
|
|
267
|
+
path: { sessionID: sessionId }
|
|
268
|
+
});
|
|
268
269
|
return unwrapSdkData(await this.client.session.abort({ sessionID: sessionId }, SDK_OPTIONS));
|
|
269
270
|
}
|
|
270
271
|
async getPath() {
|
|
272
|
+
if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", { url: "/path" });
|
|
271
273
|
return unwrapSdkData(await this.client.path.get(void 0, SDK_OPTIONS));
|
|
272
274
|
}
|
|
273
275
|
async listLspStatuses(directory) {
|
|
276
|
+
if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", {
|
|
277
|
+
url: "/lsp",
|
|
278
|
+
...directory ? { query: { directory } } : {}
|
|
279
|
+
});
|
|
274
280
|
return unwrapSdkData(await this.client.lsp.status(directory ? { directory } : void 0, SDK_OPTIONS));
|
|
275
281
|
}
|
|
276
282
|
async listMcpStatuses(directory) {
|
|
283
|
+
if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", {
|
|
284
|
+
url: "/mcp",
|
|
285
|
+
...directory ? { query: { directory } } : {}
|
|
286
|
+
});
|
|
277
287
|
return unwrapSdkData(await this.client.mcp.status(directory ? { directory } : void 0, SDK_OPTIONS));
|
|
278
288
|
}
|
|
279
289
|
async getSessionStatuses() {
|
|
290
|
+
if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", { url: "/session/status" });
|
|
280
291
|
return unwrapSdkData(await this.client.session.status(void 0, SDK_OPTIONS));
|
|
281
292
|
}
|
|
282
293
|
async listProjects() {
|
|
294
|
+
if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", { url: "/project" });
|
|
283
295
|
return unwrapSdkData(await this.client.project.list(void 0, SDK_OPTIONS));
|
|
284
296
|
}
|
|
285
297
|
async listSessions() {
|
|
298
|
+
if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", { url: "/session" });
|
|
286
299
|
return unwrapSdkData(await this.client.session.list(void 0, SDK_OPTIONS));
|
|
287
300
|
}
|
|
288
301
|
async getCurrentProject() {
|
|
302
|
+
if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", { url: "/project/current" });
|
|
289
303
|
return unwrapSdkData(await this.client.project.current(void 0, SDK_OPTIONS));
|
|
290
304
|
}
|
|
291
305
|
async createSessionForDirectory(directory, title) {
|
|
306
|
+
if (hasRawSdkMethod(this.client, "post")) return this.requestRaw("post", {
|
|
307
|
+
url: "/session",
|
|
308
|
+
query: { directory },
|
|
309
|
+
...title ? { body: { title } } : {}
|
|
310
|
+
});
|
|
292
311
|
return unwrapSdkData(await this.client.session.create(title ? {
|
|
293
312
|
directory,
|
|
294
313
|
title
|
|
295
314
|
} : { directory }, SDK_OPTIONS));
|
|
296
315
|
}
|
|
297
316
|
async renameSession(sessionId, title) {
|
|
317
|
+
if (hasRawSdkMethod(this.client, "patch")) return this.requestRaw("patch", {
|
|
318
|
+
url: "/session/{sessionID}",
|
|
319
|
+
path: { sessionID: sessionId },
|
|
320
|
+
body: { title }
|
|
321
|
+
});
|
|
298
322
|
return unwrapSdkData(await this.client.session.update({
|
|
299
323
|
sessionID: sessionId,
|
|
300
324
|
title
|
|
301
325
|
}, SDK_OPTIONS));
|
|
302
326
|
}
|
|
303
327
|
async listAgents() {
|
|
328
|
+
if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", { url: "/agent" });
|
|
304
329
|
return unwrapSdkData(await this.client.app.agents(void 0, SDK_OPTIONS));
|
|
305
330
|
}
|
|
306
331
|
async listPendingPermissions(directory) {
|
|
332
|
+
if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", {
|
|
333
|
+
url: "/permission",
|
|
334
|
+
...directory ? { query: { directory } } : {}
|
|
335
|
+
});
|
|
307
336
|
return unwrapSdkData(await this.client.permission.list(directory ? { directory } : void 0, SDK_OPTIONS));
|
|
308
337
|
}
|
|
309
338
|
async replyToPermission(requestId, reply, message) {
|
|
339
|
+
if (hasRawSdkMethod(this.client, "post")) return this.requestRaw("post", {
|
|
340
|
+
url: "/permission/{requestID}/reply",
|
|
341
|
+
path: { requestID: requestId },
|
|
342
|
+
body: {
|
|
343
|
+
reply,
|
|
344
|
+
...message?.trim() ? { message: message.trim() } : {}
|
|
345
|
+
}
|
|
346
|
+
});
|
|
310
347
|
return unwrapSdkData(await this.client.permission.reply({
|
|
311
348
|
requestID: requestId,
|
|
312
349
|
reply,
|
|
@@ -337,72 +374,72 @@ var OpenCodeClient = class {
|
|
|
337
374
|
}))];
|
|
338
375
|
if (parts.length === 0) throw new Error("Prompt requires text or file attachments.");
|
|
339
376
|
const knownMessageIds = await this.captureKnownMessageIds(input.sessionId);
|
|
340
|
-
const initialData =
|
|
341
|
-
|
|
342
|
-
...input.agent ? { agent: input.agent } : {},
|
|
343
|
-
...input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {},
|
|
344
|
-
...input.model ? { model: input.model } : {},
|
|
345
|
-
...input.variant ? { variant: input.variant } : {},
|
|
346
|
-
parts
|
|
347
|
-
}, SDK_OPTIONS));
|
|
348
|
-
return buildPromptSessionResult(await this.resolvePromptResponse(input, initialData, knownMessageIds), {
|
|
377
|
+
const initialData = await this.sendPromptRequest(input, parts);
|
|
378
|
+
return buildPromptSessionResult(await this.resolvePromptResponse(input, initialData, knownMessageIds, startedAt), {
|
|
349
379
|
emptyResponseText: EMPTY_RESPONSE_TEXT,
|
|
350
380
|
finishedAt: Date.now(),
|
|
351
381
|
startedAt,
|
|
352
382
|
structured: input.structured ?? false
|
|
353
383
|
});
|
|
354
384
|
}
|
|
355
|
-
async resolvePromptResponse(input, data, knownMessageIds) {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
initialParentId: expectedParentId,
|
|
385
|
+
async resolvePromptResponse(input, data, knownMessageIds, startedAt) {
|
|
386
|
+
const structured = input.structured ?? false;
|
|
387
|
+
if (!shouldPollPromptMessage(data, structured)) return data;
|
|
388
|
+
const messageId = extractMessageId(data.info);
|
|
389
|
+
const candidateOptions = {
|
|
390
|
+
initialMessageId: messageId,
|
|
391
|
+
initialParentId: toAssistantMessage(data.info)?.parentID ?? null,
|
|
363
392
|
knownMessageIds,
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (
|
|
377
|
-
|
|
378
|
-
|
|
393
|
+
requestStartedAt: resolvePromptCandidateStartTime(startedAt, data),
|
|
394
|
+
structured
|
|
395
|
+
};
|
|
396
|
+
let bestCandidate = selectPromptResponseCandidate([data], candidateOptions) ?? data;
|
|
397
|
+
const deadlineAt = Date.now() + PROMPT_MESSAGE_POLL_TIMEOUT_MS;
|
|
398
|
+
let idleStatusSeen = false;
|
|
399
|
+
let attempt = 0;
|
|
400
|
+
while (true) {
|
|
401
|
+
const delayMs = getPromptMessagePollDelayMs(attempt);
|
|
402
|
+
attempt += 1;
|
|
403
|
+
if (delayMs > 0) {
|
|
404
|
+
const remainingMs = deadlineAt - Date.now();
|
|
405
|
+
if (remainingMs <= 0) break;
|
|
406
|
+
await delay(Math.min(delayMs, remainingMs));
|
|
407
|
+
}
|
|
408
|
+
if (messageId) {
|
|
409
|
+
const next = await this.fetchPromptMessage(input.sessionId, messageId);
|
|
410
|
+
if (next) {
|
|
411
|
+
bestCandidate = selectPromptResponseCandidate([bestCandidate, next], candidateOptions) ?? bestCandidate;
|
|
412
|
+
if (!shouldPollPromptMessage(next, structured)) return bestCandidate;
|
|
379
413
|
}
|
|
380
|
-
continue;
|
|
381
414
|
}
|
|
382
|
-
|
|
383
|
-
bestCandidate = next;
|
|
384
|
-
expectedParentId = toAssistantMessage(next.info)?.parentID ?? expectedParentId;
|
|
385
|
-
if (!shouldPollPromptMessage(data, input.structured ?? false)) return data;
|
|
386
|
-
const latest = await this.findLatestPromptResponse(input.sessionId, {
|
|
387
|
-
initialMessageId: messageId,
|
|
388
|
-
initialParentId: expectedParentId,
|
|
389
|
-
knownMessageIds,
|
|
390
|
-
structured: input.structured ?? false
|
|
391
|
-
});
|
|
415
|
+
const latest = await this.findLatestPromptResponse(input.sessionId, candidateOptions);
|
|
392
416
|
if (latest) {
|
|
393
|
-
bestCandidate = latest;
|
|
394
|
-
if (!shouldPollPromptMessage(bestCandidate,
|
|
417
|
+
bestCandidate = selectPromptResponseCandidate([bestCandidate, latest], candidateOptions) ?? bestCandidate;
|
|
418
|
+
if (!shouldPollPromptMessage(bestCandidate, structured)) return bestCandidate;
|
|
419
|
+
}
|
|
420
|
+
if ((await this.fetchPromptSessionStatus(input.sessionId))?.type === "idle") {
|
|
421
|
+
if (idleStatusSeen) break;
|
|
422
|
+
idleStatusSeen = true;
|
|
395
423
|
}
|
|
424
|
+
if (Date.now() >= deadlineAt) break;
|
|
396
425
|
}
|
|
397
|
-
|
|
426
|
+
const latest = await this.findLatestPromptResponse(input.sessionId, candidateOptions);
|
|
427
|
+
return selectPromptResponseCandidate([bestCandidate, latest], candidateOptions) ?? bestCandidate;
|
|
398
428
|
}
|
|
399
429
|
async fetchPromptMessage(sessionId, messageId) {
|
|
400
|
-
if (typeof this.client.session.message !== "function") return null;
|
|
401
430
|
try {
|
|
402
|
-
return
|
|
431
|
+
if (hasRawSdkMethod(this.client, "get")) return normalizePromptResponse(await this.requestRaw("get", {
|
|
432
|
+
url: "/session/{sessionID}/message/{messageID}",
|
|
433
|
+
path: {
|
|
434
|
+
sessionID: sessionId,
|
|
435
|
+
messageID: messageId
|
|
436
|
+
}
|
|
437
|
+
}));
|
|
438
|
+
if (typeof this.client.session.message !== "function") return null;
|
|
439
|
+
return normalizePromptResponse(unwrapSdkData(await this.client.session.message({
|
|
403
440
|
sessionID: sessionId,
|
|
404
441
|
messageID: messageId
|
|
405
|
-
}, SDK_OPTIONS));
|
|
442
|
+
}, SDK_OPTIONS)));
|
|
406
443
|
} catch {
|
|
407
444
|
return null;
|
|
408
445
|
}
|
|
@@ -410,16 +447,27 @@ var OpenCodeClient = class {
|
|
|
410
447
|
async captureKnownMessageIds(sessionId) {
|
|
411
448
|
const messages = await this.fetchRecentPromptMessages(sessionId);
|
|
412
449
|
if (!messages) return /* @__PURE__ */ new Set();
|
|
413
|
-
return new Set(messages.map((message) => message.info
|
|
450
|
+
return new Set(messages.map((message) => extractMessageId(message.info)).filter((id) => typeof id === "string" && id.length > 0));
|
|
414
451
|
}
|
|
415
452
|
async fetchRecentPromptMessages(sessionId) {
|
|
416
|
-
if (typeof this.client.session.messages !== "function") return null;
|
|
417
453
|
try {
|
|
418
|
-
|
|
454
|
+
if (hasRawSdkMethod(this.client, "get")) return normalizePromptResponses(await this.requestRaw("get", {
|
|
455
|
+
url: "/session/{sessionID}/message",
|
|
456
|
+
path: { sessionID: sessionId },
|
|
457
|
+
query: { limit: PROMPT_MESSAGE_POLL_LIMIT }
|
|
458
|
+
}));
|
|
459
|
+
if (typeof this.client.session.messages !== "function") return null;
|
|
460
|
+
return normalizePromptResponses(unwrapSdkData(await this.client.session.messages({
|
|
419
461
|
sessionID: sessionId,
|
|
420
462
|
limit: PROMPT_MESSAGE_POLL_LIMIT
|
|
421
|
-
}, SDK_OPTIONS));
|
|
422
|
-
|
|
463
|
+
}, SDK_OPTIONS)));
|
|
464
|
+
} catch {
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
async fetchPromptSessionStatus(sessionId) {
|
|
469
|
+
try {
|
|
470
|
+
return (await this.getSessionStatuses())[sessionId] ?? null;
|
|
423
471
|
} catch {
|
|
424
472
|
return null;
|
|
425
473
|
}
|
|
@@ -427,25 +475,10 @@ var OpenCodeClient = class {
|
|
|
427
475
|
async findLatestPromptResponse(sessionId, options) {
|
|
428
476
|
const messages = await this.fetchRecentPromptMessages(sessionId);
|
|
429
477
|
if (!messages || messages.length === 0) return null;
|
|
430
|
-
|
|
431
|
-
const assistant = toAssistantMessage(message.info);
|
|
432
|
-
const id = assistant?.id ?? null;
|
|
433
|
-
return {
|
|
434
|
-
createdAt: typeof assistant?.time?.created === "number" && Number.isFinite(assistant.time.created) ? assistant.time.created : 0,
|
|
435
|
-
id,
|
|
436
|
-
isInitial: !!id && id === options.initialMessageId,
|
|
437
|
-
isNew: !!id && !options.knownMessageIds.has(id),
|
|
438
|
-
isUsable: !shouldPollPromptMessage(message, options.structured),
|
|
439
|
-
sharesParent: !!assistant?.parentID && assistant.parentID === options.initialParentId,
|
|
440
|
-
message
|
|
441
|
-
};
|
|
442
|
-
}).sort((left, right) => Number(right.isUsable) - Number(left.isUsable) || Number(right.isInitial) - Number(left.isInitial) || Number(right.sharesParent) - Number(left.sharesParent) || Number(right.isNew) - Number(left.isNew) || right.createdAt - left.createdAt);
|
|
443
|
-
return (candidates.find((candidate) => candidate.isUsable && candidate.isInitial) ?? candidates.find((candidate) => candidate.isUsable && candidate.sharesParent && candidate.isNew) ?? candidates.find((candidate) => candidate.isUsable && candidate.isNew) ?? candidates.find((candidate) => candidate.isInitial) ?? candidates.find((candidate) => candidate.sharesParent && candidate.isNew) ?? candidates.find((candidate) => candidate.isNew) ?? null)?.message ?? null;
|
|
478
|
+
return selectPromptResponseCandidate(messages, options);
|
|
444
479
|
}
|
|
445
480
|
async loadModels() {
|
|
446
|
-
const [
|
|
447
|
-
const config = unwrapSdkData(configResponse);
|
|
448
|
-
const providerCatalog = unwrapSdkData(providersResponse);
|
|
481
|
+
const [config, providerCatalog] = await Promise.all([this.loadConfig(), this.loadProviderCatalog()]);
|
|
449
482
|
const providerAvailability = await resolveProviderAvailability(config, this.fetchFn);
|
|
450
483
|
const models = buildSelectableModels(config, providerCatalog.providers, providerAvailability);
|
|
451
484
|
this.modelCache = {
|
|
@@ -455,6 +488,43 @@ var OpenCodeClient = class {
|
|
|
455
488
|
};
|
|
456
489
|
return models;
|
|
457
490
|
}
|
|
491
|
+
async loadConfig() {
|
|
492
|
+
if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", { url: "/config" });
|
|
493
|
+
return unwrapSdkData(await this.client.config.get(void 0, SDK_OPTIONS));
|
|
494
|
+
}
|
|
495
|
+
async loadProviderCatalog() {
|
|
496
|
+
if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", { url: "/config/providers" });
|
|
497
|
+
return unwrapSdkData(await this.client.config.providers(void 0, SDK_OPTIONS));
|
|
498
|
+
}
|
|
499
|
+
async sendPromptRequest(input, parts) {
|
|
500
|
+
if (hasRawSdkMethod(this.client, "post")) return normalizePromptResponse(await this.requestRaw("post", {
|
|
501
|
+
url: "/session/{sessionID}/message",
|
|
502
|
+
path: { sessionID: input.sessionId },
|
|
503
|
+
body: {
|
|
504
|
+
...input.agent ? { agent: input.agent } : {},
|
|
505
|
+
...input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {},
|
|
506
|
+
...input.model ? { model: input.model } : {},
|
|
507
|
+
...input.variant ? { variant: input.variant } : {},
|
|
508
|
+
parts
|
|
509
|
+
}
|
|
510
|
+
}));
|
|
511
|
+
return normalizePromptResponse(unwrapSdkData(await this.client.session.prompt({
|
|
512
|
+
sessionID: input.sessionId,
|
|
513
|
+
...input.agent ? { agent: input.agent } : {},
|
|
514
|
+
...input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {},
|
|
515
|
+
...input.model ? { model: input.model } : {},
|
|
516
|
+
...input.variant ? { variant: input.variant } : {},
|
|
517
|
+
parts
|
|
518
|
+
}, SDK_OPTIONS)));
|
|
519
|
+
}
|
|
520
|
+
async requestRaw(method, options) {
|
|
521
|
+
const handler = getRawSdkClient(this.client)?.[method];
|
|
522
|
+
if (typeof handler !== "function") throw new Error(`OpenCode SDK client does not expose a compatible raw ${method.toUpperCase()} method.`);
|
|
523
|
+
return unwrapSdkData(await handler({
|
|
524
|
+
...SDK_OPTIONS,
|
|
525
|
+
...options
|
|
526
|
+
}));
|
|
527
|
+
}
|
|
458
528
|
};
|
|
459
529
|
function createOpenCodeClientFromSdkClient(client, fetchFn = fetch) {
|
|
460
530
|
return new OpenCodeClient(void 0, client, fetchFn);
|
|
@@ -545,7 +615,8 @@ function extractTextFromParts(parts) {
|
|
|
545
615
|
}
|
|
546
616
|
function buildPromptSessionResult(data, options) {
|
|
547
617
|
const assistantInfo = toAssistantMessage(data.info);
|
|
548
|
-
const
|
|
618
|
+
const structuredPayload = extractStructuredPayload(assistantInfo);
|
|
619
|
+
const bodyMd = options.structured ? extractStructuredMarkdown(structuredPayload) : null;
|
|
549
620
|
const responseParts = Array.isArray(data.parts) ? data.parts : [];
|
|
550
621
|
const fallbackText = extractTextFromParts(responseParts) || bodyMd || options.emptyResponseText;
|
|
551
622
|
return {
|
|
@@ -555,23 +626,79 @@ function buildPromptSessionResult(data, options) {
|
|
|
555
626
|
info: assistantInfo,
|
|
556
627
|
metrics: extractPromptMetrics(assistantInfo, options.startedAt, options.finishedAt),
|
|
557
628
|
parts: responseParts,
|
|
558
|
-
structured:
|
|
629
|
+
structured: structuredPayload ?? null
|
|
559
630
|
};
|
|
560
631
|
}
|
|
561
632
|
function shouldPollPromptMessage(data, structured) {
|
|
562
633
|
const assistantInfo = toAssistantMessage(data.info);
|
|
563
|
-
const bodyMd = structured ? extractStructuredMarkdown(assistantInfo
|
|
634
|
+
const bodyMd = structured ? extractStructuredMarkdown(extractStructuredPayload(assistantInfo)) : null;
|
|
564
635
|
const hasText = extractTextFromParts(Array.isArray(data.parts) ? data.parts : []).length > 0;
|
|
565
636
|
const hasAssistantError = !!assistantInfo?.error;
|
|
566
637
|
return !hasText && !bodyMd && !hasAssistantError;
|
|
567
638
|
}
|
|
639
|
+
function normalizePromptResponse(response) {
|
|
640
|
+
return {
|
|
641
|
+
info: isPlainRecord(response?.info) ? response.info : null,
|
|
642
|
+
parts: normalizePromptParts(response?.parts)
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
function normalizePromptResponses(responses) {
|
|
646
|
+
if (!Array.isArray(responses)) return null;
|
|
647
|
+
return responses.map((response) => normalizePromptResponse(response));
|
|
648
|
+
}
|
|
649
|
+
function normalizePromptParts(parts) {
|
|
650
|
+
return Array.isArray(parts) ? parts : [];
|
|
651
|
+
}
|
|
568
652
|
function toAssistantMessage(message) {
|
|
569
653
|
if (!message || typeof message !== "object") return null;
|
|
570
|
-
|
|
654
|
+
if ("role" in message && message.role !== "assistant") return null;
|
|
655
|
+
const normalized = {};
|
|
656
|
+
if ("agent" in message && typeof message.agent === "string" && message.agent.trim().length > 0) normalized.agent = message.agent;
|
|
657
|
+
if ("cost" in message && typeof message.cost === "number" && Number.isFinite(message.cost)) normalized.cost = message.cost;
|
|
658
|
+
const error = normalizeAssistantError("error" in message ? message.error : void 0);
|
|
659
|
+
if (error) normalized.error = error;
|
|
660
|
+
if ("finish" in message && typeof message.finish === "string" && message.finish.trim().length > 0) normalized.finish = message.finish;
|
|
661
|
+
if ("id" in message && typeof message.id === "string" && message.id.trim().length > 0) normalized.id = message.id;
|
|
662
|
+
if ("mode" in message && typeof message.mode === "string" && message.mode.trim().length > 0) normalized.mode = message.mode;
|
|
663
|
+
if ("modelID" in message && typeof message.modelID === "string" && message.modelID.trim().length > 0) normalized.modelID = message.modelID;
|
|
664
|
+
if ("parentID" in message && typeof message.parentID === "string" && message.parentID.trim().length > 0) normalized.parentID = message.parentID;
|
|
665
|
+
if ("path" in message && isPlainRecord(message.path)) normalized.path = {
|
|
666
|
+
...typeof message.path.cwd === "string" && message.path.cwd.trim().length > 0 ? { cwd: message.path.cwd } : {},
|
|
667
|
+
...typeof message.path.root === "string" && message.path.root.trim().length > 0 ? { root: message.path.root } : {}
|
|
668
|
+
};
|
|
669
|
+
if ("providerID" in message && typeof message.providerID === "string" && message.providerID.trim().length > 0) normalized.providerID = message.providerID;
|
|
670
|
+
if ("role" in message && message.role === "assistant") normalized.role = "assistant";
|
|
671
|
+
if ("sessionID" in message && typeof message.sessionID === "string" && message.sessionID.trim().length > 0) normalized.sessionID = message.sessionID;
|
|
672
|
+
const structuredPayload = extractStructuredPayload(message);
|
|
673
|
+
if (structuredPayload !== null) normalized.structured = structuredPayload;
|
|
674
|
+
if ("summary" in message && typeof message.summary === "boolean") normalized.summary = message.summary;
|
|
675
|
+
if ("time" in message && isPlainRecord(message.time)) normalized.time = {
|
|
676
|
+
...typeof message.time.created === "number" && Number.isFinite(message.time.created) ? { created: message.time.created } : {},
|
|
677
|
+
...typeof message.time.completed === "number" && Number.isFinite(message.time.completed) ? { completed: message.time.completed } : {}
|
|
678
|
+
};
|
|
679
|
+
if ("tokens" in message && isPlainRecord(message.tokens)) normalized.tokens = {
|
|
680
|
+
...typeof message.tokens.input === "number" && Number.isFinite(message.tokens.input) ? { input: message.tokens.input } : {},
|
|
681
|
+
...typeof message.tokens.output === "number" && Number.isFinite(message.tokens.output) ? { output: message.tokens.output } : {},
|
|
682
|
+
...typeof message.tokens.reasoning === "number" && Number.isFinite(message.tokens.reasoning) ? { reasoning: message.tokens.reasoning } : {},
|
|
683
|
+
...typeof message.tokens.total === "number" && Number.isFinite(message.tokens.total) ? { total: message.tokens.total } : {},
|
|
684
|
+
...isPlainRecord(message.tokens.cache) ? { cache: {
|
|
685
|
+
...typeof message.tokens.cache.read === "number" && Number.isFinite(message.tokens.cache.read) ? { read: message.tokens.cache.read } : {},
|
|
686
|
+
...typeof message.tokens.cache.write === "number" && Number.isFinite(message.tokens.cache.write) ? { write: message.tokens.cache.write } : {}
|
|
687
|
+
} } : {}
|
|
688
|
+
};
|
|
689
|
+
if ("variant" in message && typeof message.variant === "string" && message.variant.trim().length > 0) normalized.variant = message.variant;
|
|
690
|
+
return normalized;
|
|
691
|
+
}
|
|
692
|
+
function extractMessageId(message) {
|
|
693
|
+
if (!isPlainRecord(message)) return null;
|
|
694
|
+
return typeof message.id === "string" && message.id.trim().length > 0 ? message.id : null;
|
|
571
695
|
}
|
|
572
696
|
function delay(ms) {
|
|
573
697
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
574
698
|
}
|
|
699
|
+
function getPromptMessagePollDelayMs(attempt) {
|
|
700
|
+
return PROMPT_MESSAGE_POLL_INITIAL_DELAYS_MS[attempt] ?? PROMPT_MESSAGE_POLL_INTERVAL_MS;
|
|
701
|
+
}
|
|
575
702
|
function extractStructuredMarkdown(structured) {
|
|
576
703
|
const parsed = StructuredReplySchema.safeParse(structured);
|
|
577
704
|
if (!parsed.success) return null;
|
|
@@ -627,6 +754,62 @@ function unwrapSdkData(response) {
|
|
|
627
754
|
function getRawSdkClient(client) {
|
|
628
755
|
return client.client ?? client._client ?? null;
|
|
629
756
|
}
|
|
757
|
+
function hasRawSdkMethod(client, method) {
|
|
758
|
+
return typeof getRawSdkClient(client)?.[method] === "function";
|
|
759
|
+
}
|
|
760
|
+
function normalizeAssistantError(value) {
|
|
761
|
+
if (!isPlainRecord(value) || typeof value.name !== "string" || value.name.trim().length === 0) return;
|
|
762
|
+
return {
|
|
763
|
+
...value,
|
|
764
|
+
name: value.name,
|
|
765
|
+
...isPlainRecord(value.data) ? { data: value.data } : {}
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
function extractStructuredPayload(message) {
|
|
769
|
+
if (!isPlainRecord(message)) return null;
|
|
770
|
+
if ("structured" in message && message.structured !== void 0) return message.structured;
|
|
771
|
+
if ("structured_output" in message && message.structured_output !== void 0) return message.structured_output;
|
|
772
|
+
return null;
|
|
773
|
+
}
|
|
774
|
+
function selectPromptResponseCandidate(candidates, options) {
|
|
775
|
+
const availableCandidates = candidates.filter((candidate) => !!candidate).filter((candidate) => toAssistantMessage(candidate.info) !== null);
|
|
776
|
+
if (availableCandidates.length === 0) return null;
|
|
777
|
+
return [...availableCandidates].sort((left, right) => comparePromptResponseCandidates(left, right, options))[0] ?? null;
|
|
778
|
+
}
|
|
779
|
+
function comparePromptResponseCandidates(left, right, options) {
|
|
780
|
+
const leftRank = getPromptResponseCandidateRank(left, options);
|
|
781
|
+
const rightRank = getPromptResponseCandidateRank(right, options);
|
|
782
|
+
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;
|
|
783
|
+
}
|
|
784
|
+
function getPromptResponseCandidateRank(message, options) {
|
|
785
|
+
const assistant = toAssistantMessage(message.info);
|
|
786
|
+
const id = assistant?.id ?? null;
|
|
787
|
+
const createdAt = typeof assistant?.time?.created === "number" && Number.isFinite(assistant.time.created) ? assistant.time.created : 0;
|
|
788
|
+
return {
|
|
789
|
+
createdAt,
|
|
790
|
+
isInitial: !!id && id === options.initialMessageId,
|
|
791
|
+
isNewSinceRequestStart: isPromptResponseNewSinceRequestStart(id, createdAt, options.knownMessageIds, options.requestStartedAt),
|
|
792
|
+
isUsable: !shouldPollPromptMessage(message, options.structured),
|
|
793
|
+
sharesParent: !!assistant?.parentID && assistant.parentID === options.initialParentId
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
function resolvePromptCandidateStartTime(startedAt, initialMessage) {
|
|
797
|
+
const initialCreatedAt = coerceFiniteNumber(toAssistantMessage(initialMessage.info)?.time?.created);
|
|
798
|
+
if (initialCreatedAt === null) return startedAt;
|
|
799
|
+
return areComparablePromptTimestamps(startedAt, initialCreatedAt) ? startedAt : initialCreatedAt;
|
|
800
|
+
}
|
|
801
|
+
function isPromptResponseNewSinceRequestStart(messageId, createdAt, knownMessageIds, requestStartedAt) {
|
|
802
|
+
if (!messageId || knownMessageIds.has(messageId)) return false;
|
|
803
|
+
if (requestStartedAt === null) return true;
|
|
804
|
+
return createdAt >= requestStartedAt;
|
|
805
|
+
}
|
|
806
|
+
function areComparablePromptTimestamps(left, right) {
|
|
807
|
+
const epochThresholdMs = 0xe8d4a51000;
|
|
808
|
+
return left >= epochThresholdMs && right >= epochThresholdMs;
|
|
809
|
+
}
|
|
810
|
+
function isPlainRecord(value) {
|
|
811
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
812
|
+
}
|
|
630
813
|
async function resolveProviderAvailability(config, fetchFn) {
|
|
631
814
|
const configuredProviders = Object.entries(config.provider ?? {});
|
|
632
815
|
const availabilityEntries = await Promise.all(configuredProviders.map(async ([providerId, providerConfig]) => [providerId, await fetchProviderAvailableModelIds(providerConfig, fetchFn)]));
|