@cydm/pie 2.7.0 → 2.7.1
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 +17 -1
- package/dist/builtin/extensions/ask-user/index.js +3 -3
- package/dist/builtin/extensions/plan-mode/index.js +3 -3
- package/dist/builtin/extensions/subagent/index.js +15 -5
- package/dist/builtin/extensions/todo/index.js +3 -3
- package/dist/builtin/skills/computer-use-axi/SKILL.md +72 -3
- package/dist/builtin/skills/computer-use-axi/computer-use-axi-broker.js +10846 -0
- package/dist/builtin/skills/computer-use-axi/computer-use-axi-runtime.js +440 -9256
- package/dist/chunks/{chunk-6RBSTII7.js → chunk-M44CWVDZ.js} +666 -232
- package/dist/chunks/{chunk-TWILEFLD.js → chunk-NJFPBU36.js} +1 -1
- package/dist/chunks/{chunk-IPNAFMOJ.js → chunk-W74NRSRV.js} +69 -3
- package/dist/chunks/{chunk-R5I2QLF2.js → chunk-XUROMYTT.js} +564 -96
- package/dist/chunks/{src-E7NCXMU5.js → src-C4KA4G75.js} +2 -2
- package/dist/chunks/{test-stream-BWBJTFNI.js → test-stream-A5ZELKNG.js} +1 -1
- package/dist/cli.js +449 -122
- package/models.schema.json +5 -0
- package/package.json +2 -2
|
@@ -475,6 +475,7 @@ function calculateCost(model, usage) {
|
|
|
475
475
|
const cacheRead = model.cost.cacheRead / 1e6 * usage.cacheRead;
|
|
476
476
|
const cacheWrite = model.cost.cacheWrite / 1e6 * usage.cacheWrite;
|
|
477
477
|
const cacheWriteLongTokens = usage.cacheWriteLong ?? 0;
|
|
478
|
+
const hasExplicitLongRate = model.cost.cacheWriteLong !== void 0;
|
|
478
479
|
const cacheWriteLongRate = model.cost.cacheWriteLong ?? model.cost.input * 2;
|
|
479
480
|
const cacheWriteLong = cacheWriteLongTokens > 0 ? cacheWriteLongRate / 1e6 * cacheWriteLongTokens : 0;
|
|
480
481
|
return {
|
|
@@ -482,7 +483,10 @@ function calculateCost(model, usage) {
|
|
|
482
483
|
output,
|
|
483
484
|
cacheRead,
|
|
484
485
|
cacheWrite,
|
|
485
|
-
...cacheWriteLongTokens > 0 ? {
|
|
486
|
+
...cacheWriteLongTokens > 0 ? {
|
|
487
|
+
cacheWriteLong,
|
|
488
|
+
...hasExplicitLongRate ? {} : { cacheWriteLongEstimated: true }
|
|
489
|
+
} : {},
|
|
486
490
|
total: input + output + cacheRead + cacheWrite + cacheWriteLong
|
|
487
491
|
};
|
|
488
492
|
}
|
|
@@ -1872,6 +1876,8 @@ function getHttpAgent() {
|
|
|
1872
1876
|
return globalHttpAgent;
|
|
1873
1877
|
}
|
|
1874
1878
|
var NodeHttpClient = class {
|
|
1879
|
+
/** The Node client never re-sends a request: one `request()` call is one submission. */
|
|
1880
|
+
transportCapabilities = { singleAttempt: true };
|
|
1875
1881
|
async request(url, options = {}) {
|
|
1876
1882
|
const requestStartTime = Date.now();
|
|
1877
1883
|
const requestId = `${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
@@ -2194,6 +2200,8 @@ var NodeHttpClient = class {
|
|
|
2194
2200
|
}
|
|
2195
2201
|
};
|
|
2196
2202
|
var PuerTSHttpClient = class {
|
|
2203
|
+
/** No internal re-send: each call starts exactly one bridge request. */
|
|
2204
|
+
transportCapabilities = { singleAttempt: true };
|
|
2197
2205
|
async request(url, options = {}) {
|
|
2198
2206
|
logHttp("DEBUG", `PuerTS Request started`, {
|
|
2199
2207
|
url: url.slice(0, 100),
|
|
@@ -3316,6 +3324,9 @@ function getHttpClient() {
|
|
|
3316
3324
|
}
|
|
3317
3325
|
|
|
3318
3326
|
// ../../packages/ai/src/utils/http.ts
|
|
3327
|
+
function getHttpTransport() {
|
|
3328
|
+
return getHttpClient();
|
|
3329
|
+
}
|
|
3319
3330
|
function httpRequest(url, options = {}) {
|
|
3320
3331
|
const client = getHttpClient();
|
|
3321
3332
|
return client.request(url, options);
|
|
@@ -3359,6 +3370,7 @@ function buildBaseOptions(model, options, apiKey) {
|
|
|
3359
3370
|
timeoutMs: options?.timeoutMs,
|
|
3360
3371
|
onPayload: options?.onPayload,
|
|
3361
3372
|
onTransportDiagnostic: options?.onTransportDiagnostic,
|
|
3373
|
+
onTransportLifecycle: options?.onTransportLifecycle,
|
|
3362
3374
|
maxRetryDelayMs: options?.maxRetryDelayMs,
|
|
3363
3375
|
metadata: options?.metadata
|
|
3364
3376
|
};
|
|
@@ -4411,6 +4423,35 @@ import Anthropic from "@anthropic-ai/sdk";
|
|
|
4411
4423
|
function sanitizeSurrogates(text) {
|
|
4412
4424
|
return text.replace(/[\uD800-\uDFFF]/g, "");
|
|
4413
4425
|
}
|
|
4426
|
+
function applyAnthropicUsageUpdate(model, usage, payload) {
|
|
4427
|
+
if (payload.input_tokens != null) usage.input = Math.max(0, payload.input_tokens);
|
|
4428
|
+
if (payload.output_tokens != null) usage.output = Math.max(0, payload.output_tokens);
|
|
4429
|
+
if (payload.cache_read_input_tokens != null) usage.cacheRead = Math.max(0, payload.cache_read_input_tokens);
|
|
4430
|
+
const reportedWriteTotal = payload.cache_creation_input_tokens;
|
|
4431
|
+
const breakdown = payload.cache_creation ?? void 0;
|
|
4432
|
+
const breakdownTotal = (breakdown?.ephemeral_5m_input_tokens ?? 0) + (breakdown?.ephemeral_1h_input_tokens ?? 0);
|
|
4433
|
+
if (reportedWriteTotal == null && breakdown !== void 0 && breakdownTotal === 0) {
|
|
4434
|
+
usage.cacheWrite = Math.max(0, usage.cacheWrite);
|
|
4435
|
+
} else if (reportedWriteTotal != null || breakdown !== void 0) {
|
|
4436
|
+
const previousLong = usage.cacheWriteLong ?? 0;
|
|
4437
|
+
const previousTotal = usage.cacheWrite + previousLong;
|
|
4438
|
+
const writeTotal = Math.max(
|
|
4439
|
+
0,
|
|
4440
|
+
reportedWriteTotal != null ? reportedWriteTotal : breakdownTotal > 0 ? breakdownTotal : previousTotal
|
|
4441
|
+
);
|
|
4442
|
+
const longTokens = breakdown?.ephemeral_1h_input_tokens ?? 0;
|
|
4443
|
+
if (longTokens > 0) {
|
|
4444
|
+
const long = Math.min(longTokens, writeTotal);
|
|
4445
|
+
usage.cacheWriteLong = long;
|
|
4446
|
+
usage.cacheWrite = writeTotal - long;
|
|
4447
|
+
} else {
|
|
4448
|
+
delete usage.cacheWriteLong;
|
|
4449
|
+
usage.cacheWrite = writeTotal;
|
|
4450
|
+
}
|
|
4451
|
+
}
|
|
4452
|
+
usage.totalTokens = usage.input + usage.output + usage.cacheRead + usage.cacheWrite + (usage.cacheWriteLong ?? 0);
|
|
4453
|
+
usage.cost = calculateCost(model, usage);
|
|
4454
|
+
}
|
|
4414
4455
|
var ANTHROPIC_PROVIDER_HEADERS = {
|
|
4415
4456
|
accept: "application/json",
|
|
4416
4457
|
"anthropic-dangerous-direct-browser-access": "true",
|
|
@@ -4696,17 +4737,7 @@ var streamAnthropic = (model, context, options) => {
|
|
|
4696
4737
|
const blocks = output.content;
|
|
4697
4738
|
for await (const event of anthropicStream) {
|
|
4698
4739
|
if (event.type === "message_start") {
|
|
4699
|
-
output.usage
|
|
4700
|
-
output.usage.output = event.message.usage.output_tokens || 0;
|
|
4701
|
-
output.usage.cacheRead = event.message.usage.cache_read_input_tokens || 0;
|
|
4702
|
-
output.usage.cacheWrite = event.message.usage.cache_creation_input_tokens || 0;
|
|
4703
|
-
const creation = event.message.usage.cache_creation;
|
|
4704
|
-
if (creation?.ephemeral_1h_input_tokens) {
|
|
4705
|
-
output.usage.cacheWriteLong = creation.ephemeral_1h_input_tokens;
|
|
4706
|
-
output.usage.cacheWrite = Math.max(0, output.usage.cacheWrite - creation.ephemeral_1h_input_tokens);
|
|
4707
|
-
}
|
|
4708
|
-
output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite + (output.usage.cacheWriteLong ?? 0);
|
|
4709
|
-
calculateCost(model, output.usage);
|
|
4740
|
+
applyAnthropicUsageUpdate(model, output.usage, event.message.usage);
|
|
4710
4741
|
} else if (event.type === "content_block_start") {
|
|
4711
4742
|
if (event.content_block.type === "text") {
|
|
4712
4743
|
const block = { type: "text", text: "", index: event.index };
|
|
@@ -4810,19 +4841,7 @@ var streamAnthropic = (model, context, options) => {
|
|
|
4810
4841
|
if (event.delta.stop_reason) {
|
|
4811
4842
|
output.stopReason = mapStopReason(event.delta.stop_reason);
|
|
4812
4843
|
}
|
|
4813
|
-
|
|
4814
|
-
if (event.usage.output_tokens != null) output.usage.output = event.usage.output_tokens;
|
|
4815
|
-
if (event.usage.cache_read_input_tokens != null) output.usage.cacheRead = event.usage.cache_read_input_tokens;
|
|
4816
|
-
if (event.usage.cache_creation_input_tokens != null) {
|
|
4817
|
-
output.usage.cacheWrite = event.usage.cache_creation_input_tokens;
|
|
4818
|
-
const creationDelta = event.usage.cache_creation;
|
|
4819
|
-
if (creationDelta?.ephemeral_1h_input_tokens) {
|
|
4820
|
-
output.usage.cacheWriteLong = creationDelta.ephemeral_1h_input_tokens;
|
|
4821
|
-
output.usage.cacheWrite = Math.max(0, output.usage.cacheWrite - creationDelta.ephemeral_1h_input_tokens);
|
|
4822
|
-
}
|
|
4823
|
-
}
|
|
4824
|
-
output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite + (output.usage.cacheWriteLong ?? 0);
|
|
4825
|
-
calculateCost(model, output.usage);
|
|
4844
|
+
applyAnthropicUsageUpdate(model, output.usage, event.usage);
|
|
4826
4845
|
}
|
|
4827
4846
|
}
|
|
4828
4847
|
if (options?.signal?.aborted) {
|
|
@@ -5567,6 +5586,22 @@ function reportSubmission(options, body, details) {
|
|
|
5567
5586
|
} catch {
|
|
5568
5587
|
}
|
|
5569
5588
|
}
|
|
5589
|
+
function reportLifecycle(options, event) {
|
|
5590
|
+
if (!options?.onTransportLifecycle) return;
|
|
5591
|
+
try {
|
|
5592
|
+
options.onTransportLifecycle({ ...event });
|
|
5593
|
+
} catch {
|
|
5594
|
+
}
|
|
5595
|
+
}
|
|
5596
|
+
var localRequestCounter = 0;
|
|
5597
|
+
function nextLocalRequestId(prefix = "req") {
|
|
5598
|
+
localRequestCounter += 1;
|
|
5599
|
+
return `${prefix}-${localRequestCounter}-${Math.random().toString(36).slice(2, 8)}`;
|
|
5600
|
+
}
|
|
5601
|
+
function monotonicNow() {
|
|
5602
|
+
const perf = globalThis.performance;
|
|
5603
|
+
return typeof perf?.now === "function" ? perf.now() : Date.now();
|
|
5604
|
+
}
|
|
5570
5605
|
|
|
5571
5606
|
// ../../packages/ai/src/providers/responses/responses-sse.ts
|
|
5572
5607
|
var DEFAULT_RESPONSES_STREAM_IDLE_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
@@ -5578,15 +5613,134 @@ function parseSSEDataLine2(line) {
|
|
|
5578
5613
|
const data = trimmed.slice(5);
|
|
5579
5614
|
return data.startsWith(" ") ? data.slice(1) : data;
|
|
5580
5615
|
}
|
|
5616
|
+
function singleAttemptSupport() {
|
|
5617
|
+
let capabilities;
|
|
5618
|
+
try {
|
|
5619
|
+
capabilities = getHttpTransport().transportCapabilities;
|
|
5620
|
+
} catch {
|
|
5621
|
+
capabilities = void 0;
|
|
5622
|
+
}
|
|
5623
|
+
return capabilities?.singleAttempt === true ? "verified" : "unverified";
|
|
5624
|
+
}
|
|
5625
|
+
function assertSingleAttempt() {
|
|
5626
|
+
if (singleAttemptSupport() === "verified") return;
|
|
5627
|
+
throw new ResponsesProtocolError(
|
|
5628
|
+
"Responses SSE requires the platform HTTP adapter to declare transportCapabilities.singleAttempt; refusing to submit because a submitted generation must not be re-sent by the transport."
|
|
5629
|
+
);
|
|
5630
|
+
}
|
|
5631
|
+
function explicitBudget(value, field) {
|
|
5632
|
+
if (value === void 0) return void 0;
|
|
5633
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
5634
|
+
throw new ResponsesProtocolError(`Invalid ${field}: expected a finite positive number of milliseconds.`);
|
|
5635
|
+
}
|
|
5636
|
+
return value;
|
|
5637
|
+
}
|
|
5638
|
+
function resolveSSEBudgets(options) {
|
|
5639
|
+
const firstResponse = explicitBudget(options?.firstResponseTimeoutMs, "firstResponseTimeoutMs");
|
|
5640
|
+
const streamIdle = explicitBudget(options?.streamIdleTimeoutMs, "streamIdleTimeoutMs");
|
|
5641
|
+
const requestTimeoutMs = explicitBudget(options?.requestTimeoutMs, "requestTimeoutMs");
|
|
5642
|
+
explicitBudget(options?.connectionIdleTimeoutMs, "connectionIdleTimeoutMs");
|
|
5643
|
+
if (firstResponse !== void 0 || streamIdle !== void 0) {
|
|
5644
|
+
return {
|
|
5645
|
+
http: {
|
|
5646
|
+
firstByteTimeoutMs: firstResponse ?? DEFAULT_RESPONSES_FIRST_BYTE_TIMEOUT_MS,
|
|
5647
|
+
streamIdleTimeoutMs: streamIdle ?? DEFAULT_RESPONSES_STREAM_IDLE_TIMEOUT_MS
|
|
5648
|
+
},
|
|
5649
|
+
requestTimeoutMs
|
|
5650
|
+
};
|
|
5651
|
+
}
|
|
5652
|
+
if (options?.timeoutMs !== void 0) return { http: { timeoutMs: options.timeoutMs }, requestTimeoutMs };
|
|
5653
|
+
return {
|
|
5654
|
+
http: {
|
|
5655
|
+
firstByteTimeoutMs: DEFAULT_RESPONSES_FIRST_BYTE_TIMEOUT_MS,
|
|
5656
|
+
streamIdleTimeoutMs: DEFAULT_RESPONSES_STREAM_IDLE_TIMEOUT_MS
|
|
5657
|
+
},
|
|
5658
|
+
requestTimeoutMs
|
|
5659
|
+
};
|
|
5660
|
+
}
|
|
5581
5661
|
function streamResponsesSSE(model, context, options) {
|
|
5582
5662
|
const stream = new AssistantMessageEventStream();
|
|
5583
5663
|
void (async () => {
|
|
5584
5664
|
const reducer = new ResponsesEventReducer(model);
|
|
5665
|
+
const localRequestId = nextLocalRequestId("sse");
|
|
5666
|
+
const startedAt = monotonicNow();
|
|
5667
|
+
let lastReceiveAt = startedAt;
|
|
5668
|
+
let receivedMessageCount = 0;
|
|
5669
|
+
let sawModelContent = false;
|
|
5670
|
+
let submitted = false;
|
|
5671
|
+
let terminalAccepted = false;
|
|
5672
|
+
let phase = "request_start";
|
|
5673
|
+
let budgets = {};
|
|
5674
|
+
let totalTimer;
|
|
5675
|
+
let internalTimeout;
|
|
5676
|
+
let localFailure = false;
|
|
5677
|
+
const requestAbort = new AbortController();
|
|
5678
|
+
const onUserAbort = () => requestAbort.abort();
|
|
5679
|
+
const clearTotalTimer = () => {
|
|
5680
|
+
if (totalTimer !== void 0) {
|
|
5681
|
+
clearTimeout(totalTimer);
|
|
5682
|
+
totalTimer = void 0;
|
|
5683
|
+
}
|
|
5684
|
+
};
|
|
5685
|
+
const lifecycle = (extra = {}) => reportLifecycle(options, {
|
|
5686
|
+
...extra,
|
|
5687
|
+
transport: "sse",
|
|
5688
|
+
localRequestId,
|
|
5689
|
+
requestGeneration: 1,
|
|
5690
|
+
reusedConnection: false,
|
|
5691
|
+
phase,
|
|
5692
|
+
requestAgeMs: Math.round(monotonicNow() - startedAt),
|
|
5693
|
+
lastReceiveAgeMs: Math.round(monotonicNow() - lastReceiveAt),
|
|
5694
|
+
budgets,
|
|
5695
|
+
receivedMessageCount,
|
|
5696
|
+
sawModelContent,
|
|
5697
|
+
submissionAttempted: submitted,
|
|
5698
|
+
terminalAccepted,
|
|
5699
|
+
singleAttempt: singleAttemptSupport()
|
|
5700
|
+
});
|
|
5585
5701
|
try {
|
|
5702
|
+
let resolved;
|
|
5703
|
+
try {
|
|
5704
|
+
resolved = resolveSSEBudgets(options);
|
|
5705
|
+
} catch (error) {
|
|
5706
|
+
localFailure = true;
|
|
5707
|
+
throw error;
|
|
5708
|
+
}
|
|
5709
|
+
budgets = {
|
|
5710
|
+
firstResponseTimeoutMs: resolved.http.firstByteTimeoutMs ?? resolved.http.timeoutMs,
|
|
5711
|
+
streamIdleTimeoutMs: resolved.http.streamIdleTimeoutMs ?? resolved.http.timeoutMs,
|
|
5712
|
+
requestTimeoutMs: resolved.requestTimeoutMs
|
|
5713
|
+
};
|
|
5714
|
+
options?.signal?.addEventListener("abort", onUserAbort, { once: true });
|
|
5715
|
+
if (options?.signal?.aborted) requestAbort.abort();
|
|
5716
|
+
if (resolved.requestTimeoutMs !== void 0) {
|
|
5717
|
+
totalTimer = setTimeout(() => {
|
|
5718
|
+
totalTimer = void 0;
|
|
5719
|
+
if (terminalAccepted || requestAbort.signal.aborted) return;
|
|
5720
|
+
internalTimeout = "request_total";
|
|
5721
|
+
phase = "timeout";
|
|
5722
|
+
lifecycle({
|
|
5723
|
+
timeoutKind: "request_total",
|
|
5724
|
+
terminalAccepted: false,
|
|
5725
|
+
cancelSource: "internal_timeout",
|
|
5726
|
+
retryAllowed: !submitted,
|
|
5727
|
+
retryDecision: submitted ? "not_retryable_after_submission" : "not_submitted"
|
|
5728
|
+
});
|
|
5729
|
+
requestAbort.abort();
|
|
5730
|
+
}, resolved.requestTimeoutMs);
|
|
5731
|
+
totalTimer.unref?.();
|
|
5732
|
+
}
|
|
5733
|
+
lifecycle();
|
|
5586
5734
|
const apiKey = options?.apiKey || getEnvApiKey(model.provider, model.apiKeyEnv) || "";
|
|
5587
5735
|
if (!apiKey) {
|
|
5588
5736
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
5589
5737
|
}
|
|
5738
|
+
try {
|
|
5739
|
+
assertSingleAttempt();
|
|
5740
|
+
} catch (error) {
|
|
5741
|
+
localFailure = true;
|
|
5742
|
+
throw error;
|
|
5743
|
+
}
|
|
5590
5744
|
const payload = buildResponsesSSEPayload(model, context, options);
|
|
5591
5745
|
options?.onPayload?.(payload);
|
|
5592
5746
|
const headers = {
|
|
@@ -5598,15 +5752,17 @@ function streamResponsesSSE(model, context, options) {
|
|
|
5598
5752
|
};
|
|
5599
5753
|
const body = JSON.stringify(payload);
|
|
5600
5754
|
reportSubmission(options, body, { transport: "sse", reusedConnection: false, continuation: false, attempt: 1 });
|
|
5755
|
+
submitted = true;
|
|
5756
|
+
phase = "submission_attempted";
|
|
5757
|
+
lifecycle();
|
|
5601
5758
|
const response = await httpRequest(`${model.baseUrl}/responses`, {
|
|
5602
5759
|
method: "POST",
|
|
5603
5760
|
headers,
|
|
5604
5761
|
body,
|
|
5605
|
-
signal:
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
},
|
|
5762
|
+
signal: requestAbort.signal,
|
|
5763
|
+
// The transport must not re-send a submitted generation.
|
|
5764
|
+
retryPolicy: "never",
|
|
5765
|
+
...resolved.http,
|
|
5610
5766
|
label: `${model.provider}/${model.id}`
|
|
5611
5767
|
});
|
|
5612
5768
|
if (!response.ok) {
|
|
@@ -5616,9 +5772,15 @@ function streamResponsesSSE(model, context, options) {
|
|
|
5616
5772
|
if (!response.readSSELines) {
|
|
5617
5773
|
throw new Error("SSE streaming not supported on this platform");
|
|
5618
5774
|
}
|
|
5619
|
-
for await (const line of response.readSSELines(
|
|
5775
|
+
for await (const line of response.readSSELines(requestAbort.signal)) {
|
|
5776
|
+
receivedMessageCount += 1;
|
|
5777
|
+
lastReceiveAt = monotonicNow();
|
|
5620
5778
|
const data = parseSSEDataLine2(line);
|
|
5621
5779
|
if (data === null) continue;
|
|
5780
|
+
if (phase === "submission_attempted") {
|
|
5781
|
+
phase = "first_application_message";
|
|
5782
|
+
lifecycle();
|
|
5783
|
+
}
|
|
5622
5784
|
if (data === "[DONE]") {
|
|
5623
5785
|
continue;
|
|
5624
5786
|
}
|
|
@@ -5632,35 +5794,51 @@ function streamResponsesSSE(model, context, options) {
|
|
|
5632
5794
|
if (!isResponsesEventLike(event)) {
|
|
5633
5795
|
throw new ResponsesProtocolError("Responses SSE payload is not an event object with a string type.");
|
|
5634
5796
|
}
|
|
5797
|
+
if (typeof event.type === "string" && (event.type.startsWith("response.output") || event.type.startsWith("response.content") || event.type.startsWith("response.reasoning"))) {
|
|
5798
|
+
sawModelContent = true;
|
|
5799
|
+
}
|
|
5635
5800
|
for (const assistantEvent of reducer.feed(event)) {
|
|
5636
5801
|
stream.push(assistantEvent);
|
|
5637
5802
|
}
|
|
5638
|
-
if (reducer.isTerminal)
|
|
5803
|
+
if (reducer.isTerminal) {
|
|
5804
|
+
terminalAccepted = true;
|
|
5805
|
+
phase = "terminal_accepted";
|
|
5806
|
+
clearTotalTimer();
|
|
5807
|
+
lifecycle({ retryAllowed: reducer.message.retryable !== false, retryDecision: "terminal" });
|
|
5808
|
+
break;
|
|
5809
|
+
}
|
|
5639
5810
|
}
|
|
5640
5811
|
if (reducer.isTerminal) {
|
|
5641
5812
|
stream.end();
|
|
5642
5813
|
return;
|
|
5643
5814
|
}
|
|
5644
|
-
if (
|
|
5645
|
-
throw new Error("Request was aborted");
|
|
5646
|
-
}
|
|
5647
|
-
if (!reducer.isTerminal) {
|
|
5648
|
-
throw reducer.finishWithoutTerminal();
|
|
5815
|
+
if (requestAbort.signal.aborted) {
|
|
5816
|
+
throw new Error(internalTimeout ? `Responses request timeout (${internalTimeout})` : "Request was aborted");
|
|
5649
5817
|
}
|
|
5650
|
-
|
|
5651
|
-
throw new Error(reducer.message.errorMessage ?? "An unknown error occurred");
|
|
5652
|
-
}
|
|
5653
|
-
stream.end();
|
|
5818
|
+
throw reducer.finishWithoutTerminal();
|
|
5654
5819
|
} catch (error) {
|
|
5655
5820
|
if (reducer.isTerminal) {
|
|
5656
5821
|
stream.end();
|
|
5657
5822
|
return;
|
|
5658
5823
|
}
|
|
5659
5824
|
const message = reducer.message;
|
|
5660
|
-
|
|
5661
|
-
message.
|
|
5825
|
+
const userAborted = options?.signal?.aborted === true;
|
|
5826
|
+
message.stopReason = userAborted ? "aborted" : "error";
|
|
5827
|
+
message.errorMessage = internalTimeout ? `Responses request timeout (${internalTimeout}) after ${Math.round(monotonicNow() - startedAt)}ms` : error instanceof Error ? error.message : JSON.stringify(error);
|
|
5828
|
+
if (submitted || localFailure) message.retryable = false;
|
|
5829
|
+
phase = "cleanup";
|
|
5830
|
+
lifecycle({
|
|
5831
|
+
timeoutKind: internalTimeout,
|
|
5832
|
+
retryAllowed: !submitted && !userAborted && !localFailure,
|
|
5833
|
+
retryDecision: submitted ? "not_retryable_after_submission" : localFailure ? "local_configuration_error" : "not_submitted",
|
|
5834
|
+
cancelSource: userAborted ? "user" : internalTimeout ? "internal_timeout" : "none",
|
|
5835
|
+
cleanupOutcome: "request_abandoned"
|
|
5836
|
+
});
|
|
5662
5837
|
stream.push({ type: "error", reason: message.stopReason, error: message });
|
|
5663
5838
|
stream.end();
|
|
5839
|
+
} finally {
|
|
5840
|
+
clearTotalTimer();
|
|
5841
|
+
options?.signal?.removeEventListener("abort", onUserAbort);
|
|
5664
5842
|
}
|
|
5665
5843
|
})();
|
|
5666
5844
|
return stream;
|
|
@@ -5669,26 +5847,77 @@ function streamResponsesSSE(model, context, options) {
|
|
|
5669
5847
|
// ../../packages/ai/src/providers/responses/responses-ws.ts
|
|
5670
5848
|
var owners = /* @__PURE__ */ new WeakMap();
|
|
5671
5849
|
var MAX_ANCHOR_BYTES = 1024 * 1024;
|
|
5850
|
+
var DEFAULT_CONNECT_TIMEOUT_MS = 3e4;
|
|
5851
|
+
var DEFAULT_FIRST_RESPONSE_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
5852
|
+
var DEFAULT_STREAM_IDLE_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
5853
|
+
var DEFAULT_CONNECTION_IDLE_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
5854
|
+
var ITERATOR_CLEANUP_TIMEOUT_MS = 1e3;
|
|
5855
|
+
function explicitBudget2(value, field) {
|
|
5856
|
+
if (value === void 0) return void 0;
|
|
5857
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
5858
|
+
throw new ResponsesProtocolError(`Invalid ${field}: expected a finite positive number of milliseconds.`);
|
|
5859
|
+
}
|
|
5860
|
+
return value;
|
|
5861
|
+
}
|
|
5862
|
+
function resolveBudgets(options) {
|
|
5863
|
+
const firstResponse = explicitBudget2(options?.firstResponseTimeoutMs, "firstResponseTimeoutMs");
|
|
5864
|
+
const streamIdle = explicitBudget2(options?.streamIdleTimeoutMs, "streamIdleTimeoutMs");
|
|
5865
|
+
const requestTimeout = explicitBudget2(options?.requestTimeoutMs, "requestTimeoutMs");
|
|
5866
|
+
const connectionIdle = explicitBudget2(options?.connectionIdleTimeoutMs, "connectionIdleTimeoutMs");
|
|
5867
|
+
const legacy = options?.timeoutMs !== void 0 && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? options.timeoutMs : void 0;
|
|
5868
|
+
return {
|
|
5869
|
+
connectTimeoutMs: legacy ?? DEFAULT_CONNECT_TIMEOUT_MS,
|
|
5870
|
+
firstResponseTimeoutMs: firstResponse ?? legacy ?? DEFAULT_FIRST_RESPONSE_TIMEOUT_MS,
|
|
5871
|
+
streamIdleTimeoutMs: streamIdle ?? legacy ?? DEFAULT_STREAM_IDLE_TIMEOUT_MS,
|
|
5872
|
+
requestTimeoutMs: requestTimeout,
|
|
5873
|
+
connectionIdleTimeoutMs: connectionIdle ?? DEFAULT_CONNECTION_IDLE_TIMEOUT_MS
|
|
5874
|
+
};
|
|
5875
|
+
}
|
|
5672
5876
|
function disposeModelTransports(owner) {
|
|
5673
5877
|
const state = owners.get(owner);
|
|
5674
5878
|
if (!state) return;
|
|
5675
5879
|
disposeState(owner, state);
|
|
5676
5880
|
}
|
|
5881
|
+
function clearConnectionIdleTimer(state) {
|
|
5882
|
+
if (state.idleTimer !== void 0) {
|
|
5883
|
+
clearTimeout(state.idleTimer);
|
|
5884
|
+
state.idleTimer = void 0;
|
|
5885
|
+
}
|
|
5886
|
+
}
|
|
5677
5887
|
function disposeState(owner, state) {
|
|
5678
5888
|
if (owners.get(owner) === state) owners.delete(owner);
|
|
5889
|
+
clearConnectionIdleTimer(state);
|
|
5679
5890
|
state.anchor = void 0;
|
|
5680
5891
|
state.abort.abort();
|
|
5681
|
-
|
|
5892
|
+
const connection = state.connection;
|
|
5893
|
+
state.connection = void 0;
|
|
5894
|
+
void connection?.close().catch(() => {
|
|
5682
5895
|
});
|
|
5683
5896
|
}
|
|
5684
5897
|
function stateFor(owner) {
|
|
5685
5898
|
let state = owners.get(owner);
|
|
5686
5899
|
if (!state) {
|
|
5687
|
-
state = { busy: false, abort: new AbortController() };
|
|
5900
|
+
state = { busy: false, abort: new AbortController(), requestGeneration: 0, connectionGeneration: 0 };
|
|
5688
5901
|
owners.set(owner, state);
|
|
5689
5902
|
}
|
|
5690
5903
|
return state;
|
|
5691
5904
|
}
|
|
5905
|
+
function armConnectionIdleRecycle(state, connection, generation, timeoutMs) {
|
|
5906
|
+
clearConnectionIdleTimer(state);
|
|
5907
|
+
if (!(timeoutMs > 0)) return;
|
|
5908
|
+
const timer = setTimeout(() => {
|
|
5909
|
+
if (state.idleTimer !== timer) return;
|
|
5910
|
+
state.idleTimer = void 0;
|
|
5911
|
+
if (state.busy || state.connectionGeneration !== generation || state.connection !== connection) return;
|
|
5912
|
+
state.connection = void 0;
|
|
5913
|
+
state.anchor = void 0;
|
|
5914
|
+
state.identity = void 0;
|
|
5915
|
+
void connection.close().catch(() => {
|
|
5916
|
+
});
|
|
5917
|
+
}, timeoutMs);
|
|
5918
|
+
timer.unref?.();
|
|
5919
|
+
state.idleTimer = timer;
|
|
5920
|
+
}
|
|
5692
5921
|
function websocketUrl(base) {
|
|
5693
5922
|
const url = new URL(`${base.replace(/\/$/, "")}/responses`);
|
|
5694
5923
|
if (url.protocol !== "https:" && url.protocol !== "http:") throw new ResponsesProtocolError("Responses requires an HTTP(S) API root.");
|
|
@@ -5706,19 +5935,190 @@ function streamResponsesWS(model, context, options) {
|
|
|
5706
5935
|
stream.end();
|
|
5707
5936
|
return stream;
|
|
5708
5937
|
}
|
|
5938
|
+
let budgets;
|
|
5939
|
+
try {
|
|
5940
|
+
budgets = resolveBudgets(options);
|
|
5941
|
+
} catch (error) {
|
|
5942
|
+
reducer.message.stopReason = "error";
|
|
5943
|
+
reducer.message.errorMessage = error instanceof Error ? error.message : String(error);
|
|
5944
|
+
reducer.message.retryable = false;
|
|
5945
|
+
stream.push({ type: "error", reason: "error", error: reducer.message });
|
|
5946
|
+
stream.end();
|
|
5947
|
+
return stream;
|
|
5948
|
+
}
|
|
5709
5949
|
state.busy = true;
|
|
5950
|
+
const localRequestId = nextLocalRequestId("ws");
|
|
5951
|
+
const requestGeneration = ++state.requestGeneration;
|
|
5952
|
+
const startedAt = monotonicNow();
|
|
5953
|
+
let lastReceiveAt = startedAt;
|
|
5954
|
+
let receivedMessageCount = 0;
|
|
5955
|
+
let sawResponseCreated = false;
|
|
5956
|
+
let sawModelContent = false;
|
|
5957
|
+
let reusedConnection = false;
|
|
5958
|
+
let connectionGeneration = state.connectionGeneration;
|
|
5710
5959
|
void (async () => {
|
|
5711
5960
|
let sent = false;
|
|
5712
5961
|
let finished = false;
|
|
5713
5962
|
let terminalEvents = [];
|
|
5714
5963
|
let fallback = false;
|
|
5964
|
+
let timedOut;
|
|
5965
|
+
let retryDecision = "none";
|
|
5966
|
+
let requestTimer;
|
|
5967
|
+
let totalTimer;
|
|
5968
|
+
let receiveCleanupFailed = false;
|
|
5969
|
+
let cleanupCancelSource;
|
|
5970
|
+
const requestAbort = new AbortController();
|
|
5715
5971
|
const publish = (event) => {
|
|
5716
5972
|
if (event.type === "done" || event.type === "error") terminalEvents.push(event);
|
|
5717
5973
|
else stream.push(event);
|
|
5718
5974
|
};
|
|
5975
|
+
const ageMs = () => Math.round(monotonicNow() - startedAt);
|
|
5976
|
+
const lastReceiveAgeMs = () => Math.round(monotonicNow() - lastReceiveAt);
|
|
5977
|
+
const lifecycle = (phase, extra = {}) => reportLifecycle(options, {
|
|
5978
|
+
...extra,
|
|
5979
|
+
transport: "websocket",
|
|
5980
|
+
localRequestId,
|
|
5981
|
+
requestGeneration,
|
|
5982
|
+
connectionGeneration,
|
|
5983
|
+
reusedConnection,
|
|
5984
|
+
phase,
|
|
5985
|
+
requestAgeMs: ageMs(),
|
|
5986
|
+
lastReceiveAgeMs: lastReceiveAgeMs(),
|
|
5987
|
+
budgets,
|
|
5988
|
+
receivedMessageCount,
|
|
5989
|
+
sawResponseCreated,
|
|
5990
|
+
sawModelContent,
|
|
5991
|
+
// The WS transport never re-sends a submitted frame (no hidden retry).
|
|
5992
|
+
singleAttempt: "verified"
|
|
5993
|
+
});
|
|
5994
|
+
const clearRequestTimers = () => {
|
|
5995
|
+
if (requestTimer !== void 0) {
|
|
5996
|
+
clearTimeout(requestTimer);
|
|
5997
|
+
requestTimer = void 0;
|
|
5998
|
+
}
|
|
5999
|
+
if (totalTimer !== void 0) {
|
|
6000
|
+
clearTimeout(totalTimer);
|
|
6001
|
+
totalTimer = void 0;
|
|
6002
|
+
}
|
|
6003
|
+
};
|
|
6004
|
+
const armTotalTimer = () => {
|
|
6005
|
+
if (budgets.requestTimeoutMs === void 0 || totalTimer !== void 0) return;
|
|
6006
|
+
const generation = requestGeneration;
|
|
6007
|
+
totalTimer = setTimeout(() => {
|
|
6008
|
+
totalTimer = void 0;
|
|
6009
|
+
if (generation !== requestGeneration || finished || requestAbort.signal.aborted) return;
|
|
6010
|
+
timedOut = "request_total";
|
|
6011
|
+
retryDecision = sent ? "not_retryable_after_submission" : "not_submitted";
|
|
6012
|
+
lifecycle("timeout", {
|
|
6013
|
+
timeoutKind: "request_total",
|
|
6014
|
+
terminalAccepted: false,
|
|
6015
|
+
cancelSource: "internal_timeout",
|
|
6016
|
+
retryAllowed: !sent,
|
|
6017
|
+
retryDecision,
|
|
6018
|
+
submissionAttempted: sent
|
|
6019
|
+
});
|
|
6020
|
+
requestAbort.abort();
|
|
6021
|
+
}, budgets.requestTimeoutMs);
|
|
6022
|
+
totalTimer.unref?.();
|
|
6023
|
+
};
|
|
6024
|
+
const remainingRequestBudget = () => {
|
|
6025
|
+
if (budgets.requestTimeoutMs === void 0) return void 0;
|
|
6026
|
+
return Math.max(0, Math.floor(budgets.requestTimeoutMs - ageMs()));
|
|
6027
|
+
};
|
|
6028
|
+
let fallbackAbort;
|
|
6029
|
+
let detachFallbackAbort;
|
|
6030
|
+
const fallbackSignal = () => {
|
|
6031
|
+
if (!fallbackAbort) {
|
|
6032
|
+
const controller = new AbortController();
|
|
6033
|
+
const onAbort = () => controller.abort();
|
|
6034
|
+
options?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
6035
|
+
state.abort.signal.addEventListener("abort", onAbort, { once: true });
|
|
6036
|
+
if (options?.signal?.aborted || state.abort.signal.aborted) controller.abort();
|
|
6037
|
+
fallbackAbort = controller;
|
|
6038
|
+
detachFallbackAbort = () => {
|
|
6039
|
+
options?.signal?.removeEventListener("abort", onAbort);
|
|
6040
|
+
state.abort.signal.removeEventListener("abort", onAbort);
|
|
6041
|
+
};
|
|
6042
|
+
}
|
|
6043
|
+
return fallbackAbort.signal;
|
|
6044
|
+
};
|
|
6045
|
+
const closeReceiveIterator = async (iterator) => {
|
|
6046
|
+
const returnFn = iterator.return;
|
|
6047
|
+
if (typeof returnFn !== "function") return;
|
|
6048
|
+
let timer;
|
|
6049
|
+
try {
|
|
6050
|
+
await Promise.race([
|
|
6051
|
+
Promise.resolve().then(() => returnFn.call(iterator)),
|
|
6052
|
+
new Promise((_, reject) => {
|
|
6053
|
+
timer = setTimeout(() => reject(new Error("bounded iterator cleanup")), ITERATOR_CLEANUP_TIMEOUT_MS);
|
|
6054
|
+
timer.unref?.();
|
|
6055
|
+
})
|
|
6056
|
+
]);
|
|
6057
|
+
} catch {
|
|
6058
|
+
receiveCleanupFailed = true;
|
|
6059
|
+
state.connection = void 0;
|
|
6060
|
+
state.anchor = void 0;
|
|
6061
|
+
requestAbort.abort();
|
|
6062
|
+
} finally {
|
|
6063
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
6064
|
+
}
|
|
6065
|
+
};
|
|
6066
|
+
const armPhaseTimer = (kind, timeoutMs) => {
|
|
6067
|
+
if (requestTimer !== void 0) {
|
|
6068
|
+
clearTimeout(requestTimer);
|
|
6069
|
+
requestTimer = void 0;
|
|
6070
|
+
}
|
|
6071
|
+
if (timeoutMs === void 0 || timeoutMs <= 0 || finished) return;
|
|
6072
|
+
const generation = requestGeneration;
|
|
6073
|
+
requestTimer = setTimeout(() => {
|
|
6074
|
+
requestTimer = void 0;
|
|
6075
|
+
if (generation !== requestGeneration || finished || requestAbort.signal.aborted) return;
|
|
6076
|
+
timedOut = kind;
|
|
6077
|
+
retryDecision = sent ? "not_retryable_after_submission" : "not_submitted";
|
|
6078
|
+
lifecycle("timeout", {
|
|
6079
|
+
timeoutKind: kind,
|
|
6080
|
+
terminalAccepted: false,
|
|
6081
|
+
cancelSource: "internal_timeout",
|
|
6082
|
+
retryAllowed: !sent,
|
|
6083
|
+
retryDecision,
|
|
6084
|
+
submissionAttempted: sent
|
|
6085
|
+
});
|
|
6086
|
+
requestAbort.abort();
|
|
6087
|
+
}, timeoutMs);
|
|
6088
|
+
requestTimer.unref?.();
|
|
6089
|
+
};
|
|
6090
|
+
const onOwnerAbort = () => requestAbort.abort();
|
|
6091
|
+
if (state.abort.signal.aborted) requestAbort.abort();
|
|
6092
|
+
else state.abort.signal.addEventListener("abort", onOwnerAbort, { once: true });
|
|
5719
6093
|
const abort = () => disposeState(owner, state);
|
|
5720
6094
|
options?.signal?.addEventListener("abort", abort, { once: true });
|
|
6095
|
+
const runSSEFallback = async () => {
|
|
6096
|
+
const remaining = remainingRequestBudget();
|
|
6097
|
+
if (remaining !== void 0 && remaining <= 0) {
|
|
6098
|
+
timedOut = "request_total";
|
|
6099
|
+
retryDecision = sent ? "not_retryable_after_submission" : "not_submitted";
|
|
6100
|
+
lifecycle("timeout", {
|
|
6101
|
+
timeoutKind: "request_total",
|
|
6102
|
+
terminalAccepted: false,
|
|
6103
|
+
cancelSource: "internal_timeout",
|
|
6104
|
+
retryAllowed: !sent,
|
|
6105
|
+
retryDecision,
|
|
6106
|
+
submissionAttempted: sent
|
|
6107
|
+
});
|
|
6108
|
+
throw new Error("Responses request timeout (request_total)");
|
|
6109
|
+
}
|
|
6110
|
+
clearRequestTimers();
|
|
6111
|
+
fallback = true;
|
|
6112
|
+
const fallbackOptions = {
|
|
6113
|
+
...options,
|
|
6114
|
+
signal: fallbackSignal(),
|
|
6115
|
+
...remaining !== void 0 ? { requestTimeoutMs: remaining } : {}
|
|
6116
|
+
};
|
|
6117
|
+
for await (const event of streamResponsesSSE(model, context, fallbackOptions)) publish(event);
|
|
6118
|
+
finished = true;
|
|
6119
|
+
};
|
|
5721
6120
|
try {
|
|
6121
|
+
armTotalTimer();
|
|
5722
6122
|
if (options?.signal?.aborted) {
|
|
5723
6123
|
abort();
|
|
5724
6124
|
throw new Error("Request aborted");
|
|
@@ -5729,42 +6129,52 @@ function streamResponsesWS(model, context, options) {
|
|
|
5729
6129
|
const headers = { Authorization: `Bearer ${key}`, ...model.headers, ...options?.headers };
|
|
5730
6130
|
const { input, ...settings } = payload;
|
|
5731
6131
|
const identity = JSON.stringify([model.provider, model.api, model.id, model.baseUrl, headers, settings]);
|
|
6132
|
+
clearConnectionIdleTimer(state);
|
|
5732
6133
|
if (state.identity !== identity || state.connection?.state() !== "open") {
|
|
5733
|
-
|
|
6134
|
+
const stale = state.connection;
|
|
5734
6135
|
state.connection = void 0;
|
|
5735
6136
|
state.anchor = void 0;
|
|
5736
6137
|
state.identity = identity;
|
|
6138
|
+
await stale?.close().catch(() => {
|
|
6139
|
+
});
|
|
5737
6140
|
}
|
|
6141
|
+
reusedConnection = !!state.connection;
|
|
5738
6142
|
if (state.abort.signal.aborted) throw new Error("Request aborted");
|
|
6143
|
+
lifecycle("request_start");
|
|
5739
6144
|
const client = getWebSocketClient();
|
|
5740
6145
|
if (!client) {
|
|
5741
6146
|
if (options?.transport === "auto") {
|
|
5742
|
-
|
|
5743
|
-
for await (const event of streamResponsesSSE(model, context, { ...options, signal: state.abort.signal })) publish(event);
|
|
5744
|
-
finished = true;
|
|
6147
|
+
await runSSEFallback();
|
|
5745
6148
|
return;
|
|
5746
6149
|
}
|
|
5747
6150
|
throw new Error("WebSocket transport is not available on this host.");
|
|
5748
6151
|
}
|
|
5749
|
-
|
|
5750
|
-
if (!
|
|
6152
|
+
let connection = state.connection;
|
|
6153
|
+
if (!connection) {
|
|
5751
6154
|
try {
|
|
5752
|
-
|
|
6155
|
+
connection = await client.connect(websocketUrl(model.baseUrl), {
|
|
5753
6156
|
headers,
|
|
5754
|
-
signal:
|
|
5755
|
-
connectTimeoutMs:
|
|
5756
|
-
idleTimeoutMs: options?.timeoutMs ?? 12e4
|
|
6157
|
+
signal: requestAbort.signal,
|
|
6158
|
+
connectTimeoutMs: budgets.connectTimeoutMs
|
|
5757
6159
|
});
|
|
5758
6160
|
} catch (error) {
|
|
6161
|
+
if (error instanceof WebSocketTransportError && error.kind === "timeout") timedOut = "connect";
|
|
5759
6162
|
if (options?.transport === "auto" && error instanceof WebSocketTransportError && error.kind === "handshake" && [404, 405, 426, 501].includes(error.status ?? 0) && !options.signal?.aborted && !state.abort.signal.aborted) {
|
|
5760
|
-
|
|
5761
|
-
for await (const event of streamResponsesSSE(model, context, { ...options, signal: state.abort.signal })) publish(event);
|
|
5762
|
-
finished = true;
|
|
6163
|
+
await runSSEFallback();
|
|
5763
6164
|
return;
|
|
5764
6165
|
}
|
|
5765
6166
|
throw error;
|
|
5766
6167
|
}
|
|
6168
|
+
state.connection = connection;
|
|
6169
|
+
connectionGeneration = ++state.connectionGeneration;
|
|
6170
|
+
if (state.abort.signal.aborted || requestAbort.signal.aborted) {
|
|
6171
|
+
state.connection = void 0;
|
|
6172
|
+
void connection.close().catch(() => {
|
|
6173
|
+
});
|
|
6174
|
+
throw new Error("Request aborted");
|
|
6175
|
+
}
|
|
5767
6176
|
}
|
|
6177
|
+
const activeConnection = connection;
|
|
5768
6178
|
const fullInput = input;
|
|
5769
6179
|
const anchor = state.anchor;
|
|
5770
6180
|
if (!options?.onPayload && model.compat?.responsesContinuation && anchor && fullInput.length > anchor.count && JSON.stringify(fullInput.slice(0, anchor.count)) === anchor.prefix) {
|
|
@@ -5775,65 +6185,123 @@ function streamResponsesWS(model, context, options) {
|
|
|
5775
6185
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
5776
6186
|
let rebuild = false;
|
|
5777
6187
|
options?.onPayload?.(payload);
|
|
5778
|
-
if (state.abort.signal.aborted) throw new Error("Request aborted");
|
|
6188
|
+
if (state.abort.signal.aborted || requestAbort.signal.aborted) throw new Error("Request aborted");
|
|
5779
6189
|
sent = true;
|
|
5780
6190
|
const body = JSON.stringify(payload);
|
|
5781
6191
|
reportSubmission(options, body, { transport: "websocket", reusedConnection: reusedConnection || attempt > 0, continuation: !!payload.previous_response_id, attempt: attempt + 1 });
|
|
5782
|
-
|
|
6192
|
+
lifecycle("submission_attempted", { submissionAttempted: true });
|
|
6193
|
+
armPhaseTimer("first_response", budgets.firstResponseTimeoutMs);
|
|
6194
|
+
await activeConnection.send(body, { signal: requestAbort.signal });
|
|
5783
6195
|
let started = false;
|
|
5784
6196
|
let responseId;
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
rebuild = true;
|
|
5794
|
-
break;
|
|
5795
|
-
}
|
|
5796
|
-
if (!started && typeof event.type === "string" && event.type !== "error" && !event.type.startsWith("response.")) continue;
|
|
5797
|
-
if (!started && event.type !== "error") {
|
|
5798
|
-
if (event.type !== "response.created" || !isRecord2(event.response) || typeof event.response.id !== "string" || event.response.id === state.lastResponseId) {
|
|
5799
|
-
throw new ResponsesProtocolError("Expected a new response.created before output.");
|
|
6197
|
+
const receiveIterator = activeConnection.receive(requestAbort.signal)[Symbol.asyncIterator]();
|
|
6198
|
+
let receiveDone = false;
|
|
6199
|
+
try {
|
|
6200
|
+
while (true) {
|
|
6201
|
+
const step = await receiveIterator.next();
|
|
6202
|
+
if (step.done) {
|
|
6203
|
+
receiveDone = true;
|
|
6204
|
+
break;
|
|
5800
6205
|
}
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
if (
|
|
6206
|
+
const message = step.value;
|
|
6207
|
+
receivedMessageCount += 1;
|
|
6208
|
+
lastReceiveAt = monotonicNow();
|
|
6209
|
+
armPhaseTimer("stream_idle", budgets.streamIdleTimeoutMs);
|
|
6210
|
+
if (receivedMessageCount === 1) lifecycle("first_application_message");
|
|
6211
|
+
if (state.abort.signal.aborted || requestAbort.signal.aborted) {
|
|
6212
|
+
throw new Error(timedOut ? `Responses request timeout (${timedOut})` : "Request aborted");
|
|
6213
|
+
}
|
|
6214
|
+
if (message.isBinary) throw new ResponsesProtocolError("Responses requires JSON text messages.");
|
|
6215
|
+
const event = JSON.parse(message.data);
|
|
6216
|
+
if (!isRecord2(event)) throw new ResponsesProtocolError("Invalid Responses WebSocket event.");
|
|
6217
|
+
if (typeof event.type === "string") {
|
|
6218
|
+
if (event.type === "response.created") sawResponseCreated = true;
|
|
6219
|
+
else if (event.type.startsWith("response.output") || event.type.startsWith("response.content") || event.type.startsWith("response.reasoning")) sawModelContent = true;
|
|
6220
|
+
}
|
|
6221
|
+
if (!started && attempt === 0 && payload.previous_response_id && event.type === "error" && event.status === 400 && isRecord2(event.error) && event.error.code === "previous_response_not_found" && event.error.param === "previous_response_id") {
|
|
6222
|
+
delete payload.previous_response_id;
|
|
6223
|
+
payload.input = fullInput;
|
|
6224
|
+
rebuild = true;
|
|
6225
|
+
break;
|
|
6226
|
+
}
|
|
6227
|
+
if (!started && typeof event.type === "string" && event.type !== "error" && !event.type.startsWith("response.")) continue;
|
|
6228
|
+
if (!started && event.type !== "error") {
|
|
6229
|
+
if (event.type !== "response.created" || !isRecord2(event.response) || typeof event.response.id !== "string" || event.response.id === state.lastResponseId) {
|
|
6230
|
+
throw new ResponsesProtocolError("Expected a new response.created before output.");
|
|
6231
|
+
}
|
|
6232
|
+
started = true;
|
|
6233
|
+
responseId = event.response.id;
|
|
6234
|
+
}
|
|
6235
|
+
if (responseId && isRecord2(event.response) && event.response.id !== responseId) throw new ResponsesProtocolError("Response identity changed within a request.");
|
|
6236
|
+
const events = reducer.feed(event);
|
|
6237
|
+
if (reducer.isTerminal) {
|
|
6238
|
+
if (reducer.message.stopReason === "error") reducer.message.retryable = false;
|
|
6239
|
+
const response = reducer.terminalResponse;
|
|
6240
|
+
state.lastResponseId = response?.id;
|
|
6241
|
+
if (!options?.onPayload && response?.status === "completed" && reducer.message.stopReason !== "error") {
|
|
6242
|
+
const prefixItems = [...fullInput, ...buildResponsesOutput(reducer.message)];
|
|
6243
|
+
const prefix = JSON.stringify(prefixItems);
|
|
6244
|
+
if (prefix.length * 2 <= MAX_ANCHOR_BYTES) state.anchor = { id: response.id, prefix, count: prefixItems.length };
|
|
6245
|
+
}
|
|
6246
|
+
finished = true;
|
|
6247
|
+
clearRequestTimers();
|
|
6248
|
+
lifecycle("terminal_accepted", {
|
|
6249
|
+
terminalAccepted: true,
|
|
6250
|
+
retryAllowed: reducer.message.retryable !== false,
|
|
6251
|
+
retryDecision: "terminal"
|
|
6252
|
+
});
|
|
6253
|
+
for (const e of events) publish(e);
|
|
6254
|
+
break;
|
|
5814
6255
|
}
|
|
5815
|
-
finished = true;
|
|
5816
6256
|
for (const e of events) publish(e);
|
|
5817
|
-
break;
|
|
5818
6257
|
}
|
|
5819
|
-
|
|
6258
|
+
} finally {
|
|
6259
|
+
if (!receiveDone) await closeReceiveIterator(receiveIterator);
|
|
6260
|
+
}
|
|
6261
|
+
if (rebuild) {
|
|
6262
|
+
retryDecision = "rebuild_previous_response";
|
|
6263
|
+
continue;
|
|
5820
6264
|
}
|
|
5821
|
-
if (rebuild) continue;
|
|
5822
6265
|
if (!reducer.isTerminal) throw reducer.finishWithoutTerminal();
|
|
5823
6266
|
break;
|
|
5824
6267
|
}
|
|
5825
6268
|
} catch (error) {
|
|
5826
6269
|
if (!finished) {
|
|
5827
6270
|
const message = reducer.message;
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
6271
|
+
const userAborted = options?.signal?.aborted === true;
|
|
6272
|
+
const ownerDisposed = !userAborted && !timedOut && state.abort.signal.aborted;
|
|
6273
|
+
message.stopReason = userAborted || ownerDisposed ? "aborted" : "error";
|
|
6274
|
+
const cause = timedOut ? `Responses request timeout (${timedOut}) after ${ageMs()}ms` : error instanceof Error ? error.message : String(error);
|
|
6275
|
+
message.errorMessage = cause;
|
|
6276
|
+
if (sent) {
|
|
6277
|
+
message.retryable = false;
|
|
6278
|
+
retryDecision = "not_retryable_after_submission";
|
|
6279
|
+
}
|
|
6280
|
+
cleanupCancelSource = userAborted ? "user" : timedOut ? "internal_timeout" : ownerDisposed ? "owner_disposal" : "none";
|
|
5831
6281
|
stream.push({ type: "error", reason: message.stopReason, error: message });
|
|
5832
6282
|
}
|
|
5833
6283
|
} finally {
|
|
6284
|
+
clearRequestTimers();
|
|
6285
|
+
detachFallbackAbort?.();
|
|
6286
|
+
state.abort.signal.removeEventListener("abort", onOwnerAbort);
|
|
5834
6287
|
options?.signal?.removeEventListener("abort", abort);
|
|
5835
6288
|
state.busy = false;
|
|
5836
|
-
|
|
6289
|
+
const cancelSource = cleanupCancelSource ?? (options?.signal?.aborted ? "user" : timedOut ? "internal_timeout" : state.abort.signal.aborted ? "owner_disposal" : "none");
|
|
6290
|
+
const retain = finished && !fallback && !receiveCleanupFailed && !!options?.transportOwner && reducer.message.stopReason !== "error" && !timedOut && state.connection !== void 0 && !state.abort.signal.aborted;
|
|
6291
|
+
if (!retain) disposeState(owner, state);
|
|
6292
|
+
else if (state.connection) {
|
|
6293
|
+
armConnectionIdleRecycle(state, state.connection, connectionGeneration, budgets.connectionIdleTimeoutMs);
|
|
6294
|
+
}
|
|
6295
|
+
const cleanupOutcome = receiveCleanupFailed ? "cleanup_failed" : retain ? "connection_reused" : finished ? "connection_closed" : "request_abandoned";
|
|
6296
|
+
lifecycle("cleanup", {
|
|
6297
|
+
terminalAccepted: finished,
|
|
6298
|
+
timeoutKind: timedOut,
|
|
6299
|
+
submissionAttempted: sent,
|
|
6300
|
+
retryAllowed: !finished && !sent && reducer.message.stopReason !== "aborted" && reducer.message.retryable !== false,
|
|
6301
|
+
cancelSource,
|
|
6302
|
+
retryDecision,
|
|
6303
|
+
cleanupOutcome
|
|
6304
|
+
});
|
|
5837
6305
|
for (const event of terminalEvents) stream.push(event);
|
|
5838
6306
|
stream.end();
|
|
5839
6307
|
}
|