deepline 0.3.231 → 0.3.232
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/bundling-sources/sdk/src/client.ts +16 -523
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/tool-calls.ts +530 -0
- package/dist/bundling-sources/shared_libs/observability/scheduled-work.ts +11 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +33 -415
- package/dist/bundling-sources/shared_libs/play-runtime/play-http-request.ts +419 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-reliability-policy.ts +10 -0
- package/dist/cli/index.js +831 -800
- package/dist/cli/index.mjs +831 -795
- package/dist/index.d.mts +191 -190
- package/dist/index.d.ts +191 -190
- package/dist/index.js +489 -472
- package/dist/index.mjs +489 -472
- package/dist/install-integrity.json +2 -0
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
|
@@ -92,7 +92,9 @@ import {
|
|
|
92
92
|
} from './run-execution-scope';
|
|
93
93
|
import { DEFAULT_RUNTIME_EXECUTION_CAPABILITIES } from './execution-capabilities';
|
|
94
94
|
import { vercelProtectionBypassHeader } from './vercel-protection';
|
|
95
|
-
import {
|
|
95
|
+
import { positiveTimeoutMs } from './runtime-reliability-policy';
|
|
96
|
+
import { sendPlayHttpRequest } from './play-http-request';
|
|
97
|
+
export { CtxFetchTimeoutError } from './play-http-request';
|
|
96
98
|
export {
|
|
97
99
|
RuntimeSheetRowsBlockedError,
|
|
98
100
|
type RuntimeSheetBlockedRowDetail,
|
|
@@ -205,10 +207,6 @@ import {
|
|
|
205
207
|
TOOL_EXECUTE_PROVIDER_THROTTLE_HEADER,
|
|
206
208
|
classifyToolExecuteHttpFailure,
|
|
207
209
|
createToolExecuteHttpFailureAttemptTracker,
|
|
208
|
-
TOOL_EXECUTE_BARE_RATE_LIMIT_MAX_ATTEMPTS,
|
|
209
|
-
TOOL_EXECUTE_RATE_LIMIT_MAX_ATTEMPTS,
|
|
210
|
-
TOOL_EXECUTE_RETRY_DELAY_FALLBACK_MS,
|
|
211
|
-
TOOL_EXECUTE_RATE_LIMIT_RETRY_DELAY_MAX_MS,
|
|
212
210
|
parseToolExecuteRetryAfterMs,
|
|
213
211
|
parseToolExecuteRetryAtMs,
|
|
214
212
|
parseToolExecuteAuthScopeChangedError,
|
|
@@ -590,19 +588,10 @@ const DEEPLINEAGENT_TOOL_RUNTIME_TIMEOUT_MS = 15 * 60 * 1000;
|
|
|
590
588
|
// a bounded, repairable transport failure that row-isolation settles per row
|
|
591
589
|
// instead of hanging the whole run.
|
|
592
590
|
const DEFAULT_TOOL_RUNTIME_TIMEOUT_MS = 15 * 60 * 1000 + 30_000;
|
|
593
|
-
const FETCH_TRANSPORT_MAX_ATTEMPTS =
|
|
594
|
-
RUNTIME_RELIABILITY_POLICY.egress.fetchMaxAttempts;
|
|
595
|
-
const FETCH_TRANSPORT_RETRY_DELAY_MS = 100;
|
|
596
591
|
// Diagnostic logs are retained in the context until terminalization. Keep a
|
|
597
592
|
// representative, deduplicated set so row-heavy continued failures cannot turn
|
|
598
593
|
// customer-safe observability into unbounded runner memory or log traffic.
|
|
599
594
|
const MAX_CTX_FETCH_HTTP_FAILURE_DIAGNOSTICS = 16;
|
|
600
|
-
const CTX_FETCH_HEADERS_TIMEOUT_MS =
|
|
601
|
-
RUNTIME_RELIABILITY_POLICY.egress.fetchHeadersTimeoutMs;
|
|
602
|
-
const CTX_FETCH_BODY_TIMEOUT_MS =
|
|
603
|
-
RUNTIME_RELIABILITY_POLICY.egress.fetchBodyTimeoutMs;
|
|
604
|
-
const CTX_FETCH_TOTAL_TIMEOUT_MS =
|
|
605
|
-
RUNTIME_RELIABILITY_POLICY.egress.fetchTotalTimeoutMs;
|
|
606
595
|
// cloudflared returns this branded HTML when its connection to the local app
|
|
607
596
|
// resets. It is a transport failure before the app can reach a provider, not a
|
|
608
597
|
// provider 502; require both the branded page and our development tunnel host.
|
|
@@ -647,185 +636,6 @@ class ToolExecuteResponseBodyTimeoutError extends Error {
|
|
|
647
636
|
}
|
|
648
637
|
}
|
|
649
638
|
|
|
650
|
-
export class CtxFetchTimeoutError extends Error {
|
|
651
|
-
readonly code = 'CTX_FETCH_TIMEOUT';
|
|
652
|
-
|
|
653
|
-
constructor(
|
|
654
|
-
readonly phase: 'headers' | 'body' | 'total',
|
|
655
|
-
readonly timeoutMs: number,
|
|
656
|
-
) {
|
|
657
|
-
super(
|
|
658
|
-
`[CTX_FETCH_TIMEOUT phase=${phase} timeout_ms=${timeoutMs}] ` +
|
|
659
|
-
`ctx.fetch exceeded its ${phase} deadline after ${timeoutMs}ms.`,
|
|
660
|
-
);
|
|
661
|
-
this.name = 'CtxFetchTimeoutError';
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
type CtxFetchDeadline = {
|
|
666
|
-
bodyMs: number;
|
|
667
|
-
headersMs: number;
|
|
668
|
-
startedAt: number;
|
|
669
|
-
totalMs: number;
|
|
670
|
-
};
|
|
671
|
-
|
|
672
|
-
function positiveTimeoutMs(
|
|
673
|
-
value: number | undefined,
|
|
674
|
-
fallback: number,
|
|
675
|
-
): number {
|
|
676
|
-
return value === undefined || !Number.isFinite(value) || value <= 0
|
|
677
|
-
? fallback
|
|
678
|
-
: Math.max(1, Math.floor(value));
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
function remainingCtxFetchMs(deadline: CtxFetchDeadline): number {
|
|
682
|
-
return Math.max(0, deadline.totalMs - (Date.now() - deadline.startedAt));
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
async function runCtxFetchPhase<T>(input: {
|
|
686
|
-
callerSignal?: AbortSignal | null;
|
|
687
|
-
controller: AbortController;
|
|
688
|
-
deadline: CtxFetchDeadline;
|
|
689
|
-
phase: 'headers' | 'body' | 'total';
|
|
690
|
-
run: () => Promise<T>;
|
|
691
|
-
}): Promise<T> {
|
|
692
|
-
const remainingMs = remainingCtxFetchMs(input.deadline);
|
|
693
|
-
if (remainingMs <= 0) {
|
|
694
|
-
throw new CtxFetchTimeoutError('total', input.deadline.totalMs);
|
|
695
|
-
}
|
|
696
|
-
if (input.callerSignal?.aborted) {
|
|
697
|
-
throw (
|
|
698
|
-
input.callerSignal.reason ?? new DOMException('Aborted', 'AbortError')
|
|
699
|
-
);
|
|
700
|
-
}
|
|
701
|
-
const phaseLimitMs =
|
|
702
|
-
input.phase === 'headers'
|
|
703
|
-
? input.deadline.headersMs
|
|
704
|
-
: input.phase === 'body'
|
|
705
|
-
? input.deadline.bodyMs
|
|
706
|
-
: remainingMs;
|
|
707
|
-
const timeoutMs = Math.min(phaseLimitMs, remainingMs);
|
|
708
|
-
const timeoutPhase = timeoutMs >= remainingMs ? 'total' : input.phase;
|
|
709
|
-
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
710
|
-
let onCallerAbort: (() => void) | null = null;
|
|
711
|
-
const phaseTimeout = new Promise<never>((_resolve, reject) => {
|
|
712
|
-
timeout = setTimeout(() => {
|
|
713
|
-
const error = new CtxFetchTimeoutError(
|
|
714
|
-
timeoutPhase,
|
|
715
|
-
timeoutPhase === 'total' ? input.deadline.totalMs : phaseLimitMs,
|
|
716
|
-
);
|
|
717
|
-
reject(error);
|
|
718
|
-
input.controller.abort(error);
|
|
719
|
-
}, timeoutMs);
|
|
720
|
-
});
|
|
721
|
-
const callerAbort = input.callerSignal
|
|
722
|
-
? new Promise<never>((_resolve, reject) => {
|
|
723
|
-
onCallerAbort = () => {
|
|
724
|
-
const reason =
|
|
725
|
-
input.callerSignal?.reason ??
|
|
726
|
-
new DOMException('Aborted', 'AbortError');
|
|
727
|
-
reject(reason);
|
|
728
|
-
input.controller.abort(reason);
|
|
729
|
-
};
|
|
730
|
-
input.callerSignal!.addEventListener('abort', onCallerAbort, {
|
|
731
|
-
once: true,
|
|
732
|
-
});
|
|
733
|
-
})
|
|
734
|
-
: null;
|
|
735
|
-
try {
|
|
736
|
-
return await Promise.race([
|
|
737
|
-
input.run(),
|
|
738
|
-
phaseTimeout,
|
|
739
|
-
...(callerAbort ? [callerAbort] : []),
|
|
740
|
-
]);
|
|
741
|
-
} finally {
|
|
742
|
-
if (timeout) clearTimeout(timeout);
|
|
743
|
-
if (onCallerAbort) {
|
|
744
|
-
input.callerSignal?.removeEventListener('abort', onCallerAbort);
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
async function sleepWithinCtxFetchDeadline(input: {
|
|
750
|
-
callerSignal?: AbortSignal | null;
|
|
751
|
-
deadline: CtxFetchDeadline;
|
|
752
|
-
delayMs: number;
|
|
753
|
-
}): Promise<void> {
|
|
754
|
-
const controller = new AbortController();
|
|
755
|
-
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
756
|
-
try {
|
|
757
|
-
await runCtxFetchPhase({
|
|
758
|
-
callerSignal: input.callerSignal,
|
|
759
|
-
controller,
|
|
760
|
-
deadline: input.deadline,
|
|
761
|
-
phase: 'total',
|
|
762
|
-
run: () =>
|
|
763
|
-
new Promise<void>((resolve) => {
|
|
764
|
-
timer = setTimeout(resolve, input.delayMs);
|
|
765
|
-
}),
|
|
766
|
-
});
|
|
767
|
-
} finally {
|
|
768
|
-
if (timer !== undefined) clearTimeout(timer);
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
function isCtxFetchBackpressure(response: Response, bodyText: string): boolean {
|
|
773
|
-
if (response.status === 429) return true;
|
|
774
|
-
if (response.status !== 503) return false;
|
|
775
|
-
// This Salesforce endpoint reports a rejected burst as 503. Match the final
|
|
776
|
-
// destination and exact response contract; arbitrary 503s remain failures.
|
|
777
|
-
try {
|
|
778
|
-
const destination = new URL(response.url);
|
|
779
|
-
const body: unknown = JSON.parse(bodyText);
|
|
780
|
-
return (
|
|
781
|
-
destination.protocol === 'https:' &&
|
|
782
|
-
destination.hostname.endsWith('.salesforce-sites.com') &&
|
|
783
|
-
body !== null &&
|
|
784
|
-
typeof body === 'object' &&
|
|
785
|
-
!Array.isArray(body) &&
|
|
786
|
-
(body as Record<string, unknown>).status === 'burst_circuit_open'
|
|
787
|
-
);
|
|
788
|
-
} catch {
|
|
789
|
-
return false;
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
async function readCtxFetchBody(input: {
|
|
794
|
-
callerSignal?: AbortSignal | null;
|
|
795
|
-
controller: AbortController;
|
|
796
|
-
deadline: CtxFetchDeadline;
|
|
797
|
-
response: Response;
|
|
798
|
-
}): Promise<string> {
|
|
799
|
-
if (!input.response.body) return '';
|
|
800
|
-
const reader = input.response.body.getReader();
|
|
801
|
-
const decoder = new TextDecoder();
|
|
802
|
-
try {
|
|
803
|
-
return await runCtxFetchPhase({
|
|
804
|
-
callerSignal: input.callerSignal,
|
|
805
|
-
controller: input.controller,
|
|
806
|
-
deadline: input.deadline,
|
|
807
|
-
phase: 'body',
|
|
808
|
-
run: async () => {
|
|
809
|
-
let body = '';
|
|
810
|
-
for (;;) {
|
|
811
|
-
const chunk = await reader.read();
|
|
812
|
-
if (chunk.done) return body + decoder.decode();
|
|
813
|
-
body += decoder.decode(chunk.value, { stream: true });
|
|
814
|
-
}
|
|
815
|
-
},
|
|
816
|
-
});
|
|
817
|
-
} catch (error) {
|
|
818
|
-
// Cancellation is socket hygiene, not part of the timeout contract. A
|
|
819
|
-
// hostile/custom stream may never resolve its cancel hook; awaiting it
|
|
820
|
-
// here would recreate the exact forever-pending ctx.fetch this deadline
|
|
821
|
-
// is meant to eliminate.
|
|
822
|
-
void reader.cancel(error).catch(() => {});
|
|
823
|
-
throw error;
|
|
824
|
-
} finally {
|
|
825
|
-
reader.releaseLock();
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
|
|
829
639
|
/** What a map is parked on when nothing in it is settling. */
|
|
830
640
|
export type RuntimeMapReceiptWaitParking = {
|
|
831
641
|
/** Receipt-wait groups parked right now. */
|
|
@@ -1336,9 +1146,6 @@ export function chunkArrayForReceiptRequest<T>(items: readonly T[]): T[][] {
|
|
|
1336
1146
|
return chunks;
|
|
1337
1147
|
}
|
|
1338
1148
|
|
|
1339
|
-
type SafeFetchModule = typeof import('../security/safe-fetch');
|
|
1340
|
-
let safeFetchModule: Promise<SafeFetchModule> | null = null;
|
|
1341
|
-
|
|
1342
1149
|
export async function waitForNodeRuntimeMapRowsVisible(input: {
|
|
1343
1150
|
mapName: string;
|
|
1344
1151
|
tableNamespace: string;
|
|
@@ -1546,11 +1353,6 @@ function isUnmarkedExecutionGateway502(input: {
|
|
|
1546
1353
|
}
|
|
1547
1354
|
}
|
|
1548
1355
|
|
|
1549
|
-
function loadSafeFetch(): Promise<SafeFetchModule> {
|
|
1550
|
-
safeFetchModule ??= import('../security/safe-fetch');
|
|
1551
|
-
return safeFetchModule;
|
|
1552
|
-
}
|
|
1553
|
-
|
|
1554
1356
|
function waitForSecretResolutionRetry(ms: number): Promise<void> {
|
|
1555
1357
|
if (ms <= 0) return Promise.resolve();
|
|
1556
1358
|
return new Promise<void>((resolve) => setTimeout(resolve, ms));
|
|
@@ -1578,10 +1380,6 @@ const NO_SECRET_RESOLUTION_RETRY: SecretResolutionRetryDecision = {
|
|
|
1578
1380
|
retryAfterExceedsCap: false,
|
|
1579
1381
|
};
|
|
1580
1382
|
|
|
1581
|
-
function isUnsafeOutboundUrlError(error: unknown): boolean {
|
|
1582
|
-
return error instanceof Error && error.name === 'UnsafeOutboundUrlError';
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
1383
|
function publicToolResponseEnvelope(value: unknown): {
|
|
1586
1384
|
status: string;
|
|
1587
1385
|
raw: unknown;
|
|
@@ -2635,7 +2433,9 @@ const WAITING_ROW = Symbol('deepline.waiting_row');
|
|
|
2635
2433
|
const FAILED_ROW = Symbol('deepline.failed_row');
|
|
2636
2434
|
const COMPLETED_ROW = Symbol('deepline.completed_row');
|
|
2637
2435
|
type MapRowExecutionResult =
|
|
2638
|
-
|
|
2436
|
+
| typeof COMPLETED_ROW
|
|
2437
|
+
| typeof WAITING_ROW
|
|
2438
|
+
| typeof FAILED_ROW;
|
|
2639
2439
|
|
|
2640
2440
|
function normalizeFetchHeaders(
|
|
2641
2441
|
headers: RequestInit['headers'],
|
|
@@ -4433,7 +4233,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
4433
4233
|
artifactTableNamespace: string;
|
|
4434
4234
|
mapNodeId?: string | null;
|
|
4435
4235
|
explicitKey?:
|
|
4436
|
-
((row: Record<string, unknown>, index: number) => string)
|
|
4236
|
+
| ((row: Record<string, unknown>, index: number) => string)
|
|
4237
|
+
| null;
|
|
4437
4238
|
}): MapExecutionScope {
|
|
4438
4239
|
const mapInvocationId = `${input.logicalNamespace}:${this.mapInvocationIndex}`;
|
|
4439
4240
|
this.mapInvocationIndex += 1;
|
|
@@ -4683,10 +4484,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
4683
4484
|
// run actually owns. The map/cohort publisher keeps the claim-free
|
|
4684
4485
|
// batched write, where the pointer genuinely shares the canonical
|
|
4685
4486
|
// result's single bounded request.
|
|
4686
|
-
if (
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4487
|
+
if (
|
|
4488
|
+
!(
|
|
4489
|
+
this.#options.claimRuntimeStepReceipt ||
|
|
4490
|
+
this.#options.claimRuntimeStepReceipts
|
|
4491
|
+
)
|
|
4492
|
+
) {
|
|
4690
4493
|
return;
|
|
4691
4494
|
}
|
|
4692
4495
|
const existing = await this.getRuntimeStepReceipts(
|
|
@@ -8470,7 +8273,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8470
8273
|
_key: string,
|
|
8471
8274
|
_items: PlayDatasetInput<T>,
|
|
8472
8275
|
_input?:
|
|
8473
|
-
MapFieldDefinition<T, Record<string, unknown>>
|
|
8276
|
+
| MapFieldDefinition<T, Record<string, unknown>>
|
|
8277
|
+
| RuntimeStepProgram,
|
|
8474
8278
|
_options?: RuntimeDatasetOptions<T>,
|
|
8475
8279
|
): never {
|
|
8476
8280
|
void _key;
|
|
@@ -8594,7 +8398,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8594
8398
|
|
|
8595
8399
|
const userKeyOption = options?.key;
|
|
8596
8400
|
let explicitKeyResolver:
|
|
8597
|
-
((row: Record<string, unknown>, index: number) => string)
|
|
8401
|
+
| ((row: Record<string, unknown>, index: number) => string)
|
|
8402
|
+
| null = null;
|
|
8598
8403
|
if (userKeyOption !== undefined) {
|
|
8599
8404
|
explicitKeyResolver = (row, index) => {
|
|
8600
8405
|
const inputRow = stripFieldOutputs(row);
|
|
@@ -15080,206 +14885,17 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15080
14885
|
rowStore?.mapStallObserver?.fetchStarted(
|
|
15081
14886
|
rowStore.mapStallResolverToken,
|
|
15082
14887
|
);
|
|
15083
|
-
const fetchDeadline: CtxFetchDeadline = {
|
|
15084
|
-
startedAt: Date.now(),
|
|
15085
|
-
headersMs: positiveTimeoutMs(
|
|
15086
|
-
this.#options.ctxFetchTimeouts?.headersMs,
|
|
15087
|
-
CTX_FETCH_HEADERS_TIMEOUT_MS,
|
|
15088
|
-
),
|
|
15089
|
-
bodyMs: positiveTimeoutMs(
|
|
15090
|
-
this.#options.ctxFetchTimeouts?.bodyMs,
|
|
15091
|
-
CTX_FETCH_BODY_TIMEOUT_MS,
|
|
15092
|
-
),
|
|
15093
|
-
totalMs: positiveTimeoutMs(
|
|
15094
|
-
this.#options.ctxFetchTimeouts?.totalMs,
|
|
15095
|
-
CTX_FETCH_TOTAL_TIMEOUT_MS,
|
|
15096
|
-
),
|
|
15097
|
-
};
|
|
15098
|
-
let response: Response | null = null;
|
|
15099
|
-
let bodyText: string | null = null;
|
|
15100
14888
|
try {
|
|
15101
|
-
|
|
15102
|
-
|
|
15103
|
-
|
|
15104
|
-
init
|
|
15105
|
-
|
|
15106
|
-
|
|
15107
|
-
|
|
15108
|
-
|
|
15109
|
-
|
|
15110
|
-
|
|
15111
|
-
const attemptController = new AbortController();
|
|
15112
|
-
let permit: Awaited<
|
|
15113
|
-
ReturnType<RuntimeResourceGovernor['acquireProviderPermit']>
|
|
15114
|
-
> | null = null;
|
|
15115
|
-
let dispatched = false;
|
|
15116
|
-
const releasePhysicalPermit = async () => {
|
|
15117
|
-
const current = permit;
|
|
15118
|
-
permit = null;
|
|
15119
|
-
if (!current) return;
|
|
15120
|
-
if (dispatched) current.release();
|
|
15121
|
-
else await current.cancel();
|
|
15122
|
-
dispatched = false;
|
|
15123
|
-
};
|
|
15124
|
-
let transportRetry = false;
|
|
15125
|
-
try {
|
|
15126
|
-
const attemptInit = {
|
|
15127
|
-
...fetchInit,
|
|
15128
|
-
signal: init.signal
|
|
15129
|
-
? AbortSignal.any([init.signal, attemptController.signal])
|
|
15130
|
-
: attemptController.signal,
|
|
15131
|
-
};
|
|
15132
|
-
response = await runCtxFetchPhase({
|
|
15133
|
-
callerSignal: init.signal,
|
|
15134
|
-
controller: attemptController,
|
|
15135
|
-
deadline: fetchDeadline,
|
|
15136
|
-
phase: 'total',
|
|
15137
|
-
run: () =>
|
|
15138
|
-
safePublicFetch(url, attemptInit, {
|
|
15139
|
-
// safePublicFetch validates each URL before invoking this
|
|
15140
|
-
// wrapper. Redirect hops are physical dispatches too.
|
|
15141
|
-
fetchImpl: async (physicalUrl, physicalInit) => {
|
|
15142
|
-
await releasePhysicalPermit();
|
|
15143
|
-
const signal = attemptInit.signal;
|
|
15144
|
-
permit = await runCtxFetchPhase({
|
|
15145
|
-
callerSignal: init.signal,
|
|
15146
|
-
controller: attemptController,
|
|
15147
|
-
deadline: fetchDeadline,
|
|
15148
|
-
phase: 'total',
|
|
15149
|
-
run: async () => {
|
|
15150
|
-
for (;;) {
|
|
15151
|
-
const acquired =
|
|
15152
|
-
await this.resourceGovernor.acquireProviderPermit(
|
|
15153
|
-
{ toolId: CTX_FETCH_EGRESS_TOOL_ID, signal },
|
|
15154
|
-
);
|
|
15155
|
-
try {
|
|
15156
|
-
signal.throwIfAborted();
|
|
15157
|
-
const valid =
|
|
15158
|
-
!acquired.validate ||
|
|
15159
|
-
(await acquired.validate());
|
|
15160
|
-
signal.throwIfAborted();
|
|
15161
|
-
if (valid) return acquired;
|
|
15162
|
-
} catch (error) {
|
|
15163
|
-
await acquired.cancel();
|
|
15164
|
-
throw error;
|
|
15165
|
-
}
|
|
15166
|
-
await acquired.cancel();
|
|
15167
|
-
}
|
|
15168
|
-
},
|
|
15169
|
-
});
|
|
15170
|
-
return await runCtxFetchPhase({
|
|
15171
|
-
callerSignal: init.signal,
|
|
15172
|
-
controller: attemptController,
|
|
15173
|
-
deadline: fetchDeadline,
|
|
15174
|
-
phase: 'headers',
|
|
15175
|
-
run: () => {
|
|
15176
|
-
dispatched = true;
|
|
15177
|
-
return (
|
|
15178
|
-
this.#options.fetchImpl ?? globalThis.fetch
|
|
15179
|
-
)(physicalUrl, physicalInit);
|
|
15180
|
-
},
|
|
15181
|
-
});
|
|
15182
|
-
},
|
|
15183
|
-
sensitiveHeaders: Object.keys(secretHeaderMarkers),
|
|
15184
|
-
stripHeadersOnCrossOriginRedirect: true,
|
|
15185
|
-
headersOnCrossOriginRedirect: {
|
|
15186
|
-
'user-agent': 'Deepline Play Runtime',
|
|
15187
|
-
},
|
|
15188
|
-
}),
|
|
15189
|
-
});
|
|
15190
|
-
bodyText = await readCtxFetchBody({
|
|
15191
|
-
callerSignal: init.signal,
|
|
15192
|
-
controller: attemptController,
|
|
15193
|
-
deadline: fetchDeadline,
|
|
15194
|
-
response,
|
|
15195
|
-
});
|
|
15196
|
-
} catch (error) {
|
|
15197
|
-
void response?.body?.cancel(error).catch(() => undefined);
|
|
15198
|
-
response = null;
|
|
15199
|
-
bodyText = null;
|
|
15200
|
-
if (error instanceof CtxFetchTimeoutError) throw error;
|
|
15201
|
-
if (init.signal?.aborted) {
|
|
15202
|
-
throw init.signal.reason ?? error;
|
|
15203
|
-
}
|
|
15204
|
-
if (isUnsafeOutboundUrlError(error)) {
|
|
15205
|
-
throw error;
|
|
15206
|
-
}
|
|
15207
|
-
const message =
|
|
15208
|
-
error instanceof Error ? error.message : String(error);
|
|
15209
|
-
if (
|
|
15210
|
-
canRetryTransport &&
|
|
15211
|
-
transportAttempt < FETCH_TRANSPORT_MAX_ATTEMPTS
|
|
15212
|
-
) {
|
|
15213
|
-
this.runtimeLog(
|
|
15214
|
-
`ctx.fetch(${method} ${url}) transport failed on attempt ${transportAttempt}/${FETCH_TRANSPORT_MAX_ATTEMPTS}; retrying: ${message}`,
|
|
15215
|
-
);
|
|
15216
|
-
transportRetry = true;
|
|
15217
|
-
} else {
|
|
15218
|
-
throw new Error(
|
|
15219
|
-
`ctx.fetch(${method} ${url}) failed on attempt ${transportAttempt}/${FETCH_TRANSPORT_MAX_ATTEMPTS}: ${message}`,
|
|
15220
|
-
);
|
|
15221
|
-
}
|
|
15222
|
-
} finally {
|
|
15223
|
-
await releasePhysicalPermit();
|
|
15224
|
-
}
|
|
15225
|
-
if (transportRetry) {
|
|
15226
|
-
await sleepWithinCtxFetchDeadline({
|
|
15227
|
-
callerSignal: init.signal,
|
|
15228
|
-
deadline: fetchDeadline,
|
|
15229
|
-
delayMs: FETCH_TRANSPORT_RETRY_DELAY_MS * transportAttempt,
|
|
15230
|
-
});
|
|
15231
|
-
transportAttempt += 1;
|
|
15232
|
-
continue;
|
|
15233
|
-
}
|
|
15234
|
-
if (
|
|
15235
|
-
!response ||
|
|
15236
|
-
bodyText === null ||
|
|
15237
|
-
!replayableBody ||
|
|
15238
|
-
!isCtxFetchBackpressure(response, bodyText)
|
|
15239
|
-
)
|
|
15240
|
-
break;
|
|
15241
|
-
rateLimitAttempt += 1;
|
|
15242
|
-
const retryAfter = response.headers.get('retry-after');
|
|
15243
|
-
const attemptCap = retryAfter?.trim()
|
|
15244
|
-
? TOOL_EXECUTE_RATE_LIMIT_MAX_ATTEMPTS
|
|
15245
|
-
: TOOL_EXECUTE_BARE_RATE_LIMIT_MAX_ATTEMPTS;
|
|
15246
|
-
if (rateLimitAttempt >= attemptCap) break;
|
|
15247
|
-
const delayMs = Math.max(
|
|
15248
|
-
parseToolExecuteRetryAfterMs(retryAfter),
|
|
15249
|
-
Math.min(
|
|
15250
|
-
TOOL_EXECUTE_RATE_LIMIT_RETRY_DELAY_MAX_MS,
|
|
15251
|
-
TOOL_EXECUTE_RETRY_DELAY_FALLBACK_MS * rateLimitAttempt,
|
|
15252
|
-
),
|
|
15253
|
-
);
|
|
15254
|
-
this.runtimeLog(
|
|
15255
|
-
`[runtime.ctx_fetch_backpressure] ${JSON.stringify({
|
|
15256
|
-
key: normalizedKey,
|
|
15257
|
-
method,
|
|
15258
|
-
origin: new URL(response.url || url).origin,
|
|
15259
|
-
http_status: response.status,
|
|
15260
|
-
attempt: rateLimitAttempt,
|
|
15261
|
-
max_attempts: attemptCap,
|
|
15262
|
-
retry_delay_ms: delayMs,
|
|
15263
|
-
})}`,
|
|
15264
|
-
);
|
|
15265
|
-
// Destination feedback must not cool the shared generic_http
|
|
15266
|
-
// bucket used by unrelated websites. Pace this logical request.
|
|
15267
|
-
await sleepWithinCtxFetchDeadline({
|
|
15268
|
-
callerSignal: init.signal,
|
|
15269
|
-
deadline: fetchDeadline,
|
|
15270
|
-
delayMs,
|
|
15271
|
-
});
|
|
15272
|
-
}
|
|
15273
|
-
if (!response) {
|
|
15274
|
-
throw new Error(
|
|
15275
|
-
`ctx.fetch(${method} ${url}) failed before receiving a response.`,
|
|
15276
|
-
);
|
|
15277
|
-
}
|
|
15278
|
-
if (bodyText === null) {
|
|
15279
|
-
throw new Error(
|
|
15280
|
-
`ctx.fetch(${method} ${url}) failed while reading the response body.`,
|
|
15281
|
-
);
|
|
15282
|
-
}
|
|
14889
|
+
const { response, bodyText } = await sendPlayHttpRequest({
|
|
14890
|
+
key: normalizedKey,
|
|
14891
|
+
url,
|
|
14892
|
+
init: fetchInit,
|
|
14893
|
+
governor: this.resourceGovernor,
|
|
14894
|
+
sensitiveHeaders: Object.keys(secretHeaderMarkers),
|
|
14895
|
+
timeouts: this.#options.ctxFetchTimeouts,
|
|
14896
|
+
fetchImpl: this.#options.fetchImpl,
|
|
14897
|
+
log: (message) => this.runtimeLog(message),
|
|
14898
|
+
});
|
|
15283
14899
|
const rawJson = parseJsonOrNull(bodyText);
|
|
15284
14900
|
const redactedBodyText = this.secretRedactor.redactString(bodyText);
|
|
15285
14901
|
const output: PlayFetchResponse = {
|
|
@@ -16685,8 +16301,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
16685
16301
|
},
|
|
16686
16302
|
})),
|
|
16687
16303
|
buildPhysicalCall: async ({ members }) => {
|
|
16688
|
-
const owned = members.map(
|
|
16689
|
-
batchRequestByCallId.get(member.memberId)!,
|
|
16304
|
+
const owned = members.map(
|
|
16305
|
+
(member) => batchRequestByCallId.get(member.memberId)!,
|
|
16690
16306
|
);
|
|
16691
16307
|
const compiled = compileOwnedPhysicalBatch(owned);
|
|
16692
16308
|
const providerActionVersion =
|
|
@@ -19012,7 +18628,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
19012
18628
|
// running for.
|
|
19013
18629
|
const activeRowScope = rowContext.getStore();
|
|
19014
18630
|
const memoryLedgerRowScopes: readonly (
|
|
19015
|
-
|
|
18631
|
+
| MemoryLedgerRowScope
|
|
18632
|
+
| null
|
|
18633
|
+
| undefined
|
|
19016
18634
|
)[] = options?.memoryLedgerRowScopes ?? [
|
|
19017
18635
|
{ rowKey: activeRowScope?.rowKey, fieldName: activeRowScope?.fieldName },
|
|
19018
18636
|
];
|