deepline 0.3.408 → 0.3.410
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/agent-runtime.ts +18 -1
- package/dist/bundling-sources/sdk/src/client.ts +212 -334
- package/dist/bundling-sources/sdk/src/compat.ts +45 -2
- package/dist/bundling-sources/sdk/src/db/query.ts +9 -10
- package/dist/bundling-sources/sdk/src/http.ts +38 -20
- package/dist/bundling-sources/sdk/src/index.ts +4 -5
- package/dist/bundling-sources/sdk/src/play.ts +10 -10
- package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +6 -4
- package/dist/bundling-sources/sdk/src/plays/check-local-play-graph.ts +114 -0
- package/dist/bundling-sources/sdk/src/plays/client.ts +431 -0
- package/dist/bundling-sources/sdk/src/plays/local-play-execution.ts +2 -2
- package/dist/bundling-sources/sdk/src/plays/run-request.ts +106 -16
- package/dist/bundling-sources/sdk/src/release.ts +13 -7
- package/dist/bundling-sources/sdk/src/runs/read-errors.ts +8 -0
- package/dist/bundling-sources/sdk/src/runs/watch-polling.ts +124 -13
- package/dist/bundling-sources/sdk/src/tool-calls.ts +108 -19
- package/dist/bundling-sources/sdk/src/types.ts +18 -0
- package/dist/bundling-sources/shared_libs/integration-contracts/execute-retry-contract.ts +37 -0
- package/dist/bundling-sources/shared_libs/integration-contracts/package.json +3 -0
- package/dist/bundling-sources/shared_libs/play-data-plane/r2.ts +3 -1
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +277 -76
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/dataset-row-input.ts +124 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +6 -62
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-tool-identity.ts +67 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execute-server-timing.ts +64 -6
- package/dist/bundling-sources/shared_libs/play-runtime/freshness-receipt-execution.ts +1 -5
- package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-artifact-runtime-contract.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-http-request.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/query-result-dataset.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/egress-probe.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +26 -16
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-heartbeat.ts +36 -11
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +35 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-launch-transport.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +21 -6
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +11 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +85 -20
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +71 -13
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-execution-queries.ts +100 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-pool.ts +9 -5
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +337 -146
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +26 -12
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +3 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +425 -102
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-dispatch-authority.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-capacity.ts +181 -124
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-worker-wake.ts +152 -0
- package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +5 -3
- package/dist/bundling-sources/shared_libs/play-runtime/tool-cache-policy.ts +31 -0
- package/dist/bundling-sources/shared_libs/play-runtime/transient-service-error.ts +10 -0
- package/dist/bundling-sources/shared_libs/plays/ambiguous-write-error.ts +25 -0
- package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +4 -0
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +63 -68
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +129 -63
- package/dist/bundling-sources/shared_libs/plays/contracts.ts +14 -0
- package/dist/bundling-sources/shared_libs/plays/enrich-play-compiler.ts +58 -16
- package/dist/bundling-sources/shared_libs/plays/fetch-http-error.ts +50 -0
- package/dist/bundling-sources/shared_libs/plays/source-authoring-edition.ts +351 -0
- package/dist/bundling-sources/shared_libs/plays/tool-list-getter-mode.ts +9 -0
- package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +1 -1
- package/dist/cli/{auth-UVD3CUBB.mjs → auth-LLXLIILR.mjs} +6 -6
- package/dist/cli/auth-UG4WOYGL.js +40 -0
- package/dist/cli/{billing-BZVTXXLM.mjs → billing-5FVXZYAC.mjs} +8 -8
- package/dist/cli/{billing-BZVYAF6T.js → billing-NFPK7GYP.js} +84 -84
- package/dist/cli/{chunk-ZAAKSJAS.js → chunk-27GJKC5Q.js} +17 -10
- package/dist/cli/{chunk-UHNDVD3P.js → chunk-2H4EY7KH.js} +100 -41
- package/dist/cli/{chunk-WTT6CLED.mjs → chunk-2YR7BIIC.mjs} +5 -2
- package/dist/cli/{chunk-3ZB6IIA7.js → chunk-36AHOWVS.js} +26 -26
- package/dist/cli/{chunk-OSDXE27V.mjs → chunk-4PG4ARL2.mjs} +17 -10
- package/dist/cli/{chunk-LOYISF7S.mjs → chunk-4XQLVUC7.mjs} +738 -512
- package/dist/cli/{chunk-J5PTX66Y.js → chunk-5MLCBXXU.js} +8 -1
- package/dist/cli/chunk-5TUGWDEB.js +313 -0
- package/dist/cli/{chunk-D6FFBSM5.js → chunk-5UUQAGJS.js} +5 -2
- package/dist/cli/{chunk-4CFULMRB.mjs → chunk-5V5Y37BC.mjs} +2 -2
- package/dist/cli/{chunk-PMVFAYL7.mjs → chunk-63ILLN5H.mjs} +2 -2
- package/dist/cli/{chunk-AUMD2VXP.js → chunk-63S7X326.js} +1032 -806
- package/dist/cli/{chunk-4QFT5LGV.mjs → chunk-6PGATQ6L.mjs} +6 -3
- package/dist/cli/{chunk-6BJK4EJ3.mjs → chunk-AARWVK4C.mjs} +2132 -65
- package/dist/cli/{chunk-FERRDNMO.js → chunk-AD47BXN2.js} +5 -5
- package/dist/cli/{chunk-4RNDKFNQ.js → chunk-D37FEBES.js} +2 -2
- package/dist/cli/{chunk-S6R3OX7B.js → chunk-E4CABWXL.js} +25 -2
- package/dist/cli/{chunk-NXGCO4JB.mjs → chunk-E4IXDAZI.mjs} +5 -0
- package/dist/cli/chunk-EQ4Z7RJW.mjs +35 -0
- package/dist/cli/chunk-FR2ILQ4G.mjs +8 -0
- package/dist/cli/{chunk-QUAQX3FQ.js → chunk-FVLNYDI4.js} +128 -376
- package/dist/cli/{chunk-BRJUESQV.js → chunk-FZJAM576.js} +50 -50
- package/dist/cli/{chunk-BZAMOCPA.mjs → chunk-IMZ56EOH.mjs} +3 -3
- package/dist/cli/{chunk-IOHRWLQE.mjs → chunk-IPG42SD2.mjs} +452 -138
- package/dist/cli/chunk-J33TSWO5.js +8 -0
- package/dist/cli/{chunk-XXLXOUUU.mjs → chunk-JNXJZ577.mjs} +1 -1
- package/dist/cli/{chunk-JI2B4RZP.mjs → chunk-KCC3FKIW.mjs} +71 -12
- package/dist/cli/{chunk-6IKARAYD.mjs → chunk-L2VKAASU.mjs} +23 -0
- package/dist/cli/{chunk-PYZZMAJQ.mjs → chunk-LFSHGNT2.mjs} +8 -1
- package/dist/cli/{chunk-2XXXFPTD.js → chunk-LTFL4JRN.js} +426 -721
- package/dist/cli/{chunk-YHPYUZZX.mjs → chunk-MAMVGOSC.mjs} +1 -1
- package/dist/cli/{chunk-URACNNLO.mjs → chunk-MN2OWLFA.mjs} +2 -2
- package/dist/cli/{chunk-Q3PUHXOY.js → chunk-NF4V5DTY.js} +12 -12
- package/dist/cli/{chunk-Q2E4E4FJ.js → chunk-NPBJI7RE.js} +8 -8
- package/dist/cli/chunk-NTVLGHFM.js +501 -0
- package/dist/cli/{chunk-HQXL5526.mjs → chunk-NUX2QHNC.mjs} +1 -1
- package/dist/cli/{chunk-E3PJF6YP.js → chunk-OBB4QXHA.js} +19 -4
- package/dist/cli/{chunk-SEFMGO3Z.js → chunk-OQQOXSYA.js} +2184 -117
- package/dist/cli/chunk-OZ6E3IRZ.js +6084 -0
- package/dist/cli/{chunk-7P5AA3SN.js → chunk-PPDBRJRW.js} +6 -1
- package/dist/cli/{chunk-IWBXJDZA.mjs → chunk-PZSLPBDA.mjs} +2 -2
- package/dist/cli/{chunk-ILHRA7AD.js → chunk-QADCFSGH.js} +7 -7
- package/dist/cli/{chunk-ECVLPS67.js → chunk-QJI5FIOW.js} +170 -49
- package/dist/cli/{chunk-I3DHIXVJ.mjs → chunk-QL22Y56M.mjs} +149 -28
- package/dist/cli/{chunk-SD7MXAJA.mjs → chunk-RFB25DSQ.mjs} +36 -284
- package/dist/cli/chunk-T44IIOT6.mjs +501 -0
- package/dist/cli/chunk-VK2LZ5IP.js +35 -0
- package/dist/cli/chunk-VKKVSFRD.js +62 -0
- package/dist/cli/{chunk-RWCY6YI2.js → chunk-VYKV5XHL.js} +644 -330
- package/dist/cli/{chunk-GSK6NHVM.js → chunk-WU4U6VSB.js} +5 -5
- package/dist/cli/chunk-XHLKJOTN.mjs +313 -0
- package/dist/cli/{chunk-MJELBQBU.mjs → chunk-XIHWSXSV.mjs} +330 -625
- package/dist/cli/chunk-XNZJKRJA.mjs +6084 -0
- package/dist/cli/chunk-ZCALLYF6.mjs +62 -0
- package/dist/cli/{chunk-ZBABPK6Y.js → chunk-ZFZ427HN.js} +11 -8
- package/dist/cli/{chunk-SJVVBWRL.mjs → chunk-ZKYEW55U.mjs} +16 -1
- package/dist/cli/command-docs.js +2 -2
- package/dist/cli/command-docs.mjs +1 -1
- package/dist/cli/{csv-XLGLHJ6P.js → csv-7U3FJYKZ.js} +14 -14
- package/dist/cli/{csv-WPSXG6ZZ.mjs → csv-ZWLDMM4A.mjs} +5 -5
- package/dist/cli/{db-5PTNJG4X.js → db-DNCGV5PC.js} +42 -47
- package/dist/cli/{db-ABS57ABJ.mjs → db-IMYXVMGI.mjs} +15 -20
- package/dist/cli/{describe-PVWQJ2PB.mjs → describe-Q23DWJUK.mjs} +20 -15
- package/dist/cli/{describe-EZS6PQXA.js → describe-S34JUX5W.js} +27 -22
- package/dist/cli/describe-core.d.mts +2 -2
- package/dist/cli/describe-core.d.ts +2 -2
- package/dist/cli/describe-core.js +6 -6
- package/dist/cli/describe-core.mjs +5 -5
- package/dist/cli/{executions-OKJGATQZ.mjs → executions-A73EUGSI.mjs} +8 -8
- package/dist/cli/{executions-FNTAFQ7Y.js → executions-XOMPX2YJ.js} +10 -10
- package/dist/cli/{feedback-W27HDMRR.mjs → feedback-N4GJ2POI.mjs} +11 -7
- package/dist/cli/{feedback-6YBOMSUA.js → feedback-QTCHNJJH.js} +13 -9
- package/dist/cli/{git-YOOUEL6M.mjs → git-4Q3JIQYZ.mjs} +6 -6
- package/dist/cli/git-I6CPBEVG.js +24 -0
- package/dist/cli/index.js +240 -352
- package/dist/cli/index.mjs +163 -275
- package/dist/cli/{legacy-noops-V6OKW7VR.mjs → legacy-noops-3SOVGFF7.mjs} +6 -6
- package/dist/cli/{legacy-noops-2VZ7XT6C.js → legacy-noops-4OSHZM34.js} +9 -9
- package/dist/cli/{monitors-FBWGVIUS.js → monitors-LVE2KZNG.js} +94 -94
- package/dist/cli/{monitors-P2YYWTWT.mjs → monitors-QP2XID2B.mjs} +8 -8
- package/dist/cli/{org-DVMG6H3C.mjs → org-IVRETVQA.mjs} +8 -8
- package/dist/cli/{org-3XUSVX5T.js → org-XFLK5IAP.js} +68 -68
- package/dist/cli/{play-GNC5CQAS.mjs → play-DE7VH6NU.mjs} +533 -98
- package/dist/cli/{play-YG5L4BWQ.js → play-XXTPPP23.js} +1067 -632
- package/dist/cli/{providers-7PSWG2OV.js → providers-DLD3I4JF.js} +11 -11
- package/dist/cli/{providers-JR33SLWL.mjs → providers-GX2NIZQQ.mjs} +8 -8
- package/dist/cli/{quickstart-VUOBQYIQ.mjs → quickstart-72B4L4PK.mjs} +5 -5
- package/dist/cli/{quickstart-FDVZ5BL3.js → quickstart-XNS3HSWC.js} +7 -7
- package/dist/cli/search-6NCH4HB5.js +30 -0
- package/dist/cli/{search-JZYLO33K.mjs → search-SUTRANJI.mjs} +9 -9
- package/dist/cli/{secrets-VS266FVA.js → secrets-6OBLOVEV.js} +17 -17
- package/dist/cli/{secrets-3MCYBT4I.mjs → secrets-FPFXXNUH.mjs} +8 -8
- package/dist/cli/sessions-235532DR.js +16 -0
- package/dist/cli/{sessions-ELLLT23E.mjs → sessions-R25I4QKN.mjs} +6 -6
- package/dist/cli/{settings-PZCKAFII.mjs → settings-AOP6F5PZ.mjs} +12 -12
- package/dist/cli/{settings-7364NMMA.js → settings-VL5FOFPN.js} +79 -79
- package/dist/cli/{setup-PMWYLCT6.js → setup-BSC67YT3.js} +45 -44
- package/dist/cli/{setup-A7GQ4KIA.mjs → setup-ZMPHBHVJ.mjs} +11 -10
- package/dist/cli/skills-3IWLYYRL.js +29 -0
- package/dist/cli/{skills-WS6BIFPQ.mjs → skills-KF65TVFO.mjs} +7 -7
- package/dist/cli/tools-LR66SPYU.js +44 -0
- package/dist/cli/{tools-BR4DOZA2.mjs → tools-SX4XIPM7.mjs} +19 -14
- package/dist/cli/{update-D2B7UNFT.mjs → update-QRMFRAQQ.mjs} +11 -9
- package/dist/cli/update-SP7UEO53.js +31 -0
- package/dist/cli/{workflow-PQXC2R47.mjs → workflow-FOEEEB7G.mjs} +44 -20
- package/dist/cli/{workflow-6ODTCYXU.js → workflow-KOCK2FLW.js} +54 -30
- package/dist/{client-Z9Pye1Wm.d.mts → client-74k4IPh6.d.mts} +137 -54
- package/dist/{client-BcvhDfJJ.d.ts → client-C1IK1f8c.d.ts} +137 -54
- package/dist/{compiler-manifest-Ycx-hoAr.d.mts → compiler-manifest-lzWpbV3w.d.mts} +83 -66
- package/dist/{compiler-manifest-Ycx-hoAr.d.ts → compiler-manifest-lzWpbV3w.d.ts} +83 -66
- package/dist/{index-CZXgiN3E.d.mts → index-C8Cy-YTm.d.ts} +5 -1
- package/dist/{index-NPG5pqxQ.d.ts → index-CtNK-0nI.d.mts} +5 -1
- package/dist/index.d.mts +47 -9
- package/dist/index.d.ts +47 -9
- package/dist/index.js +17 -14
- package/dist/index.mjs +13 -10
- package/dist/install-integrity.json +120 -104
- package/dist/plays/bundle-play-file.d.mts +3 -3
- package/dist/plays/bundle-play-file.d.ts +3 -3
- package/dist/plays/bundle-play-file.mjs +5 -4
- package/dist/plays/csv-data-input.js +3 -3
- package/dist/plays/csv-data-input.mjs +2 -2
- package/dist/plays/play-description.d.mts +2 -2
- package/dist/plays/play-description.d.ts +2 -2
- package/dist/plays/run-request.d.mts +2 -2
- package/dist/plays/run-request.d.ts +2 -2
- package/dist/plays/run-request.js +11 -3
- package/dist/plays/run-request.mjs +10 -2
- package/dist/release.d.mts +15 -6
- package/dist/release.d.ts +15 -6
- package/dist/release.js +2 -2
- package/dist/release.mjs +1 -1
- package/package.json +2 -2
- package/dist/cli/auth-3GB2K636.js +0 -40
- package/dist/cli/chunk-67RWR34T.mjs +0 -155
- package/dist/cli/chunk-CLJGSOVM.mjs +0 -1716
- package/dist/cli/chunk-HQY2SG3K.js +0 -83
- package/dist/cli/chunk-OSFWFV72.js +0 -155
- package/dist/cli/chunk-Q3LFI3VH.js +0 -392
- package/dist/cli/chunk-WPAB4DCU.mjs +0 -83
- package/dist/cli/chunk-YMOFWHGO.mjs +0 -392
- package/dist/cli/chunk-Z4VNJOF7.js +0 -1716
- package/dist/cli/git-YKUXXJOR.js +0 -24
- package/dist/cli/search-PPPYC5OZ.js +0 -30
- package/dist/cli/sessions-TEHEWW7O.js +0 -16
- package/dist/cli/skills-SYS7VRRK.js +0 -29
- package/dist/cli/tools-KK4BKVQR.js +0 -39
- package/dist/cli/update-2IV5JTX2.js +0 -29
|
@@ -18,6 +18,8 @@ export type RunLedgerDispatchLaunch = Pick<
|
|
|
18
18
|
| 'runtimeTokenOrigin'
|
|
19
19
|
| 'orgId'
|
|
20
20
|
| 'runtimeAuthority'
|
|
21
|
+
| 'absurdReleaseId'
|
|
22
|
+
| 'requiresRunCreatedProjection'
|
|
21
23
|
| 'vercelProtectionBypassToken'
|
|
22
24
|
>;
|
|
23
25
|
|
|
@@ -34,6 +36,8 @@ export function runLedgerDispatchLaunchSql(alias: string): string {
|
|
|
34
36
|
'runtimeTokenOrigin',
|
|
35
37
|
'orgId',
|
|
36
38
|
'runtimeAuthority',
|
|
39
|
+
'absurdReleaseId',
|
|
40
|
+
'requiresRunCreatedProjection',
|
|
37
41
|
'vercelProtectionBypassToken',
|
|
38
42
|
];
|
|
39
43
|
return `jsonb_build_object(${fields
|
|
@@ -886,6 +886,7 @@ export async function deliverRunCreatedOutboxEventInline(input: {
|
|
|
886
886
|
withClient: RunLedgerOutboxWithClient;
|
|
887
887
|
schedulerOptions?: Pick<PostgresSchedulerOptions, 'schema'>;
|
|
888
888
|
tokenAuthority: RunLedgerOutboxTokenAuthority;
|
|
889
|
+
appRuntimeFetch?: typeof fetch;
|
|
889
890
|
deadlineMs?: number;
|
|
890
891
|
/** Test seam; clamped to `RUN_CREATED_INLINE_RELEASE_GRACE_MS`. */
|
|
891
892
|
releaseGraceMs?: number;
|
|
@@ -926,6 +927,9 @@ export async function deliverRunCreatedOutboxEventInline(input: {
|
|
|
926
927
|
limit: 1,
|
|
927
928
|
leaseSeconds: RUN_CREATED_INLINE_DELIVERY_LEASE_SECONDS,
|
|
928
929
|
tokenAuthority: input.tokenAuthority,
|
|
930
|
+
...(input.appRuntimeFetch
|
|
931
|
+
? { appRuntimeFetch: input.appRuntimeFetch }
|
|
932
|
+
: {}),
|
|
929
933
|
signal: controller.signal,
|
|
930
934
|
onAbortedLeaseRelease: (released) => {
|
|
931
935
|
abandoned.releaseFailed = !released;
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts
CHANGED
|
@@ -1223,6 +1223,15 @@ export async function getRuntimeRunLogPage(
|
|
|
1223
1223
|
};
|
|
1224
1224
|
}
|
|
1225
1225
|
|
|
1226
|
+
/** Acquire inside the writer's transaction; all namespaces share these objects. */
|
|
1227
|
+
export async function lockRuntimeEventSearchDdl(
|
|
1228
|
+
client: PostgresSchedulerQueryClient,
|
|
1229
|
+
): Promise<void> {
|
|
1230
|
+
await client.query(
|
|
1231
|
+
`SELECT pg_advisory_xact_lock(hashtext('deepline:runtime-observability'), hashtext('schema-ddl'))`,
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1226
1235
|
async function ensureRuntimeEventFetchReceiptFunction(
|
|
1227
1236
|
client: PostgresSchedulerQueryClient,
|
|
1228
1237
|
): Promise<void> {
|
|
@@ -242,6 +242,19 @@ export async function readRuntimeOrgSandboxLimitCacheViaRuntimeScheduler(input:
|
|
|
242
242
|
);
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
type RuntimeSandboxReservationProof = {
|
|
246
|
+
org_id: string;
|
|
247
|
+
run_id: string;
|
|
248
|
+
provider: RuntimeSandboxProviderId;
|
|
249
|
+
operation_attempt: number | null;
|
|
250
|
+
operation_environment: 'preview' | 'production' | null;
|
|
251
|
+
operation_provider: RuntimeSandboxProviderId | null;
|
|
252
|
+
provider_operation_token: string | null;
|
|
253
|
+
operation_state: RuntimeSandboxOperationState | null;
|
|
254
|
+
released_at: Date | null;
|
|
255
|
+
sandbox_id: string | null;
|
|
256
|
+
};
|
|
257
|
+
|
|
245
258
|
export async function reserveRuntimeSandboxCapacityLease(
|
|
246
259
|
client: PostgresSchedulerQueryClient,
|
|
247
260
|
input: {
|
|
@@ -272,11 +285,47 @@ export async function reserveRuntimeSandboxCapacityLease(
|
|
|
272
285
|
const orgId = assertId(input.orgId, 'orgId');
|
|
273
286
|
const runId = assertId(input.runId, 'runId');
|
|
274
287
|
const { cache, leases, operations, runs } = schemaTables(options);
|
|
288
|
+
const leaseId = input.leaseId?.trim() || randomUUID();
|
|
289
|
+
const unsentProviderOperationToken = unsentReservationInputs.get(input);
|
|
290
|
+
const providerOperationToken =
|
|
291
|
+
input.attempt === undefined
|
|
292
|
+
? null
|
|
293
|
+
: (unsentProviderOperationToken ??
|
|
294
|
+
runtimeSandboxProviderOperationToken(leaseId));
|
|
295
|
+
const resumePendingDaytonaOperation =
|
|
296
|
+
input.resumePendingDaytonaOperation === true &&
|
|
297
|
+
input.provider === RuntimeSandboxProvider.Daytona &&
|
|
298
|
+
input.attempt !== undefined;
|
|
275
299
|
// This is deliberately a soft admission check: concurrent workers may both
|
|
276
300
|
// observe space and insert. Avoid locks and long transactions; the provider
|
|
277
301
|
// has its own global safety cap and the 50-minute lease bounds crashes.
|
|
278
|
-
|
|
302
|
+
// Read the capacity and existing intent together. Neither observation proves
|
|
303
|
+
// an INSERT winner; the fenced write must return proof or read it back.
|
|
304
|
+
const observed = await client.query<{
|
|
305
|
+
limit: number;
|
|
306
|
+
active_leases: number;
|
|
307
|
+
pending_operations: Array<{
|
|
308
|
+
lease_id: string;
|
|
309
|
+
provider_operation_token: string | null;
|
|
310
|
+
attempt: number;
|
|
311
|
+
environment: 'preview' | 'production';
|
|
312
|
+
}>;
|
|
313
|
+
existing_operation: {
|
|
314
|
+
org_id: string;
|
|
315
|
+
run_id: string;
|
|
316
|
+
provider: RuntimeSandboxProviderId;
|
|
317
|
+
operation_attempt: number | null;
|
|
318
|
+
operation_environment: 'preview' | 'production' | null;
|
|
319
|
+
operation_provider: RuntimeSandboxProviderId | null;
|
|
320
|
+
provider_operation_token: string | null;
|
|
321
|
+
operation_state: RuntimeSandboxOperationState | null;
|
|
322
|
+
released_at: string | null;
|
|
323
|
+
sandbox_id: string | null;
|
|
324
|
+
} | null;
|
|
325
|
+
inserted_proof: RuntimeSandboxReservationProof | null;
|
|
326
|
+
}>(
|
|
279
327
|
`
|
|
328
|
+
WITH observed AS MATERIALIZED (
|
|
280
329
|
SELECT
|
|
281
330
|
coalesce(
|
|
282
331
|
(
|
|
@@ -300,53 +349,119 @@ export async function reserveRuntimeSandboxCapacityLease(
|
|
|
300
349
|
-- is only the crash backstop for a create that never became
|
|
301
350
|
-- durable enough to bind to a sandbox id.
|
|
302
351
|
AND (sandbox_id IS NOT NULL OR expires_at > clock_timestamp())
|
|
303
|
-
) AS active_leases
|
|
352
|
+
) AS active_leases,
|
|
353
|
+
coalesce((
|
|
354
|
+
SELECT jsonb_agg(pending)
|
|
355
|
+
FROM (
|
|
356
|
+
SELECT lease.lease_id, operation.provider_operation_token,
|
|
357
|
+
operation.attempt, operation.environment
|
|
358
|
+
FROM ${operations} AS operation
|
|
359
|
+
JOIN ${leases} AS lease ON lease.lease_id = operation.operation_key
|
|
360
|
+
WHERE $6::boolean
|
|
361
|
+
AND operation.org_id = $1
|
|
362
|
+
AND operation.run_id = $3
|
|
363
|
+
AND operation.provider = $4
|
|
364
|
+
AND operation.state = $7
|
|
365
|
+
AND lease.released_at IS NULL
|
|
366
|
+
AND lease.sandbox_id IS NULL
|
|
367
|
+
ORDER BY operation.created_at ASC
|
|
368
|
+
LIMIT 2
|
|
369
|
+
) AS pending
|
|
370
|
+
), '[]'::jsonb) AS pending_operations,
|
|
371
|
+
(
|
|
372
|
+
SELECT to_jsonb(existing)
|
|
373
|
+
FROM (
|
|
374
|
+
SELECT lease.org_id, lease.run_id, lease.provider,
|
|
375
|
+
operation.attempt AS operation_attempt,
|
|
376
|
+
operation.environment AS operation_environment,
|
|
377
|
+
operation.provider AS operation_provider,
|
|
378
|
+
operation.provider_operation_token,
|
|
379
|
+
operation.state AS operation_state,
|
|
380
|
+
lease.released_at, lease.sandbox_id
|
|
381
|
+
FROM ${leases} AS lease
|
|
382
|
+
LEFT JOIN ${operations} AS operation
|
|
383
|
+
ON operation.operation_key = lease.lease_id
|
|
384
|
+
WHERE lease.lease_id = $5
|
|
385
|
+
) AS existing
|
|
386
|
+
) AS existing_operation
|
|
387
|
+
), active_run AS MATERIALIZED (
|
|
388
|
+
SELECT run.run_id
|
|
389
|
+
FROM ${runs} AS run, observed
|
|
390
|
+
WHERE $8::integer IS NOT NULL
|
|
391
|
+
AND observed.existing_operation IS NULL
|
|
392
|
+
AND jsonb_array_length(observed.pending_operations) = 0
|
|
393
|
+
AND (observed.active_leases < observed."limit" OR $10::text <> 'enforce')
|
|
394
|
+
AND run.run_id = $3
|
|
395
|
+
AND run.status IN ('queued', 'running', 'waiting')
|
|
396
|
+
AND NOT EXISTS (
|
|
397
|
+
SELECT 1 FROM ${quoteIdent(options?.schema ?? 'runtime_scheduler')}."control"
|
|
398
|
+
WHERE key = 'runtime_cancel:' || $3::text
|
|
399
|
+
)
|
|
400
|
+
FOR UPDATE OF run
|
|
401
|
+
), reserved_lease AS (
|
|
402
|
+
INSERT INTO ${leases} (
|
|
403
|
+
lease_id, org_id, run_id, provider, expires_at
|
|
404
|
+
)
|
|
405
|
+
SELECT $5, $1, $3, $4,
|
|
406
|
+
clock_timestamp() + ($11::integer * interval '1 millisecond')
|
|
407
|
+
WHERE EXISTS (SELECT 1 FROM active_run)
|
|
408
|
+
ON CONFLICT (lease_id) DO NOTHING
|
|
409
|
+
RETURNING lease_id, org_id, run_id, provider, released_at, sandbox_id
|
|
410
|
+
), reserved_operation AS (
|
|
411
|
+
INSERT INTO ${operations} (
|
|
412
|
+
operation_key, provider_operation_token, provider, run_id, attempt, org_id, environment, state
|
|
413
|
+
)
|
|
414
|
+
SELECT lease_id, $12, $4, $3, $8, $1, $9, $7
|
|
415
|
+
FROM reserved_lease
|
|
416
|
+
ON CONFLICT (operation_key) DO NOTHING
|
|
417
|
+
RETURNING operation_key, provider_operation_token, provider,
|
|
418
|
+
attempt, environment, state
|
|
419
|
+
)
|
|
420
|
+
SELECT observed.*, (
|
|
421
|
+
SELECT to_jsonb(proof)
|
|
422
|
+
FROM (
|
|
423
|
+
SELECT lease.org_id, lease.run_id, lease.provider,
|
|
424
|
+
operation.attempt AS operation_attempt,
|
|
425
|
+
operation.environment AS operation_environment,
|
|
426
|
+
operation.provider AS operation_provider,
|
|
427
|
+
operation.provider_operation_token,
|
|
428
|
+
operation.state AS operation_state,
|
|
429
|
+
lease.released_at, lease.sandbox_id
|
|
430
|
+
FROM reserved_lease AS lease
|
|
431
|
+
JOIN reserved_operation AS operation
|
|
432
|
+
ON operation.operation_key = lease.lease_id
|
|
433
|
+
) AS proof
|
|
434
|
+
) AS inserted_proof
|
|
435
|
+
FROM observed
|
|
304
436
|
`,
|
|
305
|
-
[
|
|
437
|
+
[
|
|
438
|
+
orgId,
|
|
439
|
+
DEFAULT_PLAY_SANDBOX_CONCURRENCY,
|
|
440
|
+
runId,
|
|
441
|
+
input.provider,
|
|
442
|
+
leaseId,
|
|
443
|
+
resumePendingDaytonaOperation,
|
|
444
|
+
RuntimeSandboxOperationState.Reserved,
|
|
445
|
+
input.attempt ?? null,
|
|
446
|
+
input.environment ?? 'production',
|
|
447
|
+
input.mode ?? 'enforce',
|
|
448
|
+
RUNTIME_SANDBOX_CAPACITY_LEASE_MS,
|
|
449
|
+
providerOperationToken,
|
|
450
|
+
],
|
|
306
451
|
);
|
|
307
452
|
const limit = Number(
|
|
308
453
|
observed.rows[0]?.limit ?? DEFAULT_PLAY_SANDBOX_CONCURRENCY,
|
|
309
454
|
);
|
|
310
455
|
const activeLeases = Number(observed.rows[0]?.active_leases ?? 0);
|
|
311
456
|
const wouldDefer = activeLeases >= limit;
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
input.attempt === undefined
|
|
316
|
-
? null
|
|
317
|
-
: (unsentProviderOperationToken ??
|
|
318
|
-
runtimeSandboxProviderOperationToken(leaseId));
|
|
319
|
-
if (
|
|
320
|
-
input.resumePendingDaytonaOperation === true &&
|
|
321
|
-
input.provider === RuntimeSandboxProvider.Daytona &&
|
|
322
|
-
input.attempt !== undefined
|
|
323
|
-
) {
|
|
324
|
-
const pending = await client.query<{
|
|
325
|
-
lease_id: string;
|
|
326
|
-
provider_operation_token: string | null;
|
|
327
|
-
attempt: number;
|
|
328
|
-
environment: 'preview' | 'production';
|
|
329
|
-
}>(
|
|
330
|
-
`SELECT lease.lease_id, operation.provider_operation_token,
|
|
331
|
-
operation.attempt, operation.environment
|
|
332
|
-
FROM ${operations} AS operation
|
|
333
|
-
JOIN ${leases} AS lease ON lease.lease_id = operation.operation_key
|
|
334
|
-
WHERE operation.org_id = $1
|
|
335
|
-
AND operation.run_id = $2
|
|
336
|
-
AND operation.provider = $3
|
|
337
|
-
AND operation.state = $4
|
|
338
|
-
AND lease.released_at IS NULL
|
|
339
|
-
AND lease.sandbox_id IS NULL
|
|
340
|
-
ORDER BY operation.created_at ASC
|
|
341
|
-
LIMIT 2`,
|
|
342
|
-
[orgId, runId, input.provider, RuntimeSandboxOperationState.Reserved],
|
|
343
|
-
);
|
|
344
|
-
if (pending.rows.length > 1) {
|
|
457
|
+
if (resumePendingDaytonaOperation) {
|
|
458
|
+
const pending = observed.rows[0]?.pending_operations ?? [];
|
|
459
|
+
if (pending.length > 1) {
|
|
345
460
|
throw new Error(
|
|
346
461
|
'More than one Daytona sandbox operation is pending for this run.',
|
|
347
462
|
);
|
|
348
463
|
}
|
|
349
|
-
const pendingOperation = pending
|
|
464
|
+
const pendingOperation = pending[0];
|
|
350
465
|
if (
|
|
351
466
|
pendingOperation &&
|
|
352
467
|
!(
|
|
@@ -384,33 +499,9 @@ export async function reserveRuntimeSandboxCapacityLease(
|
|
|
384
499
|
};
|
|
385
500
|
}
|
|
386
501
|
}
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
provider: RuntimeSandboxProviderId;
|
|
391
|
-
operation_attempt: number | null;
|
|
392
|
-
operation_environment: 'preview' | 'production' | null;
|
|
393
|
-
operation_provider: RuntimeSandboxProviderId | null;
|
|
394
|
-
provider_operation_token: string | null;
|
|
395
|
-
operation_state: RuntimeSandboxOperationState | null;
|
|
396
|
-
released_at: Date | null;
|
|
397
|
-
sandbox_id: string | null;
|
|
398
|
-
}>(
|
|
399
|
-
`SELECT lease.org_id, lease.run_id, lease.provider,
|
|
400
|
-
operation.attempt AS operation_attempt,
|
|
401
|
-
operation.environment AS operation_environment,
|
|
402
|
-
operation.provider AS operation_provider,
|
|
403
|
-
operation.provider_operation_token,
|
|
404
|
-
operation.state AS operation_state,
|
|
405
|
-
lease.released_at, lease.sandbox_id
|
|
406
|
-
FROM ${leases} AS lease
|
|
407
|
-
LEFT JOIN ${operations} AS operation
|
|
408
|
-
ON operation.operation_key = lease.lease_id
|
|
409
|
-
WHERE lease.lease_id = $1`,
|
|
410
|
-
[leaseId],
|
|
411
|
-
);
|
|
412
|
-
if (existing.rows[0]) {
|
|
413
|
-
const row = existing.rows[0];
|
|
502
|
+
const existingOperation = observed.rows[0]?.existing_operation;
|
|
503
|
+
if (existingOperation) {
|
|
504
|
+
const row = existingOperation;
|
|
414
505
|
if (
|
|
415
506
|
row.org_id !== orgId ||
|
|
416
507
|
row.run_id !== runId ||
|
|
@@ -487,64 +578,30 @@ export async function reserveRuntimeSandboxCapacityLease(
|
|
|
487
578
|
if (wouldDefer && (input.mode ?? 'enforce') === 'enforce') {
|
|
488
579
|
return { granted: false, limit, activeLeases };
|
|
489
580
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
reserved_lease.lease_id, $9, $4, $3, $6, $2, $7, $8
|
|
515
|
-
FROM reserved_lease
|
|
516
|
-
WHERE $6::integer IS NOT NULL
|
|
517
|
-
ON CONFLICT (operation_key) DO NOTHING
|
|
518
|
-
`,
|
|
519
|
-
[
|
|
520
|
-
leaseId,
|
|
521
|
-
orgId,
|
|
522
|
-
runId,
|
|
523
|
-
input.provider,
|
|
524
|
-
RUNTIME_SANDBOX_CAPACITY_LEASE_MS,
|
|
525
|
-
input.attempt ?? null,
|
|
526
|
-
input.environment ?? 'production',
|
|
527
|
-
RuntimeSandboxOperationState.Reserved,
|
|
528
|
-
providerOperationToken,
|
|
529
|
-
],
|
|
530
|
-
);
|
|
531
|
-
// The caller may have lost the INSERT response after PostgreSQL committed,
|
|
532
|
-
// or raced another identical retry. A second read is mandatory: conflict
|
|
533
|
-
// swallowing alone would manufacture a lease without proving whose intent
|
|
534
|
-
// it resumes.
|
|
535
|
-
const persisted = await client.query<{
|
|
536
|
-
org_id: string;
|
|
537
|
-
run_id: string;
|
|
538
|
-
provider: RuntimeSandboxProviderId;
|
|
539
|
-
operation_attempt: number | null;
|
|
540
|
-
operation_environment: 'preview' | 'production' | null;
|
|
541
|
-
operation_provider: RuntimeSandboxProviderId | null;
|
|
542
|
-
provider_operation_token: string | null;
|
|
543
|
-
operation_state: RuntimeSandboxOperationState | null;
|
|
544
|
-
released_at: Date | null;
|
|
545
|
-
sandbox_id: string | null;
|
|
546
|
-
}>(
|
|
547
|
-
`SELECT lease.org_id, lease.run_id, lease.provider,
|
|
581
|
+
if (input.attempt === undefined) {
|
|
582
|
+
// Historical lease-only callers retain their read/write/readback contract.
|
|
583
|
+
await client.query(
|
|
584
|
+
`INSERT INTO ${leases} (lease_id, org_id, run_id, provider, expires_at)
|
|
585
|
+
VALUES ($1, $2, $3, $4,
|
|
586
|
+
clock_timestamp() + ($5::integer * interval '1 millisecond'))
|
|
587
|
+
ON CONFLICT (lease_id) DO NOTHING`,
|
|
588
|
+
[
|
|
589
|
+
leaseId,
|
|
590
|
+
orgId,
|
|
591
|
+
runId,
|
|
592
|
+
input.provider,
|
|
593
|
+
RUNTIME_SANDBOX_CAPACITY_LEASE_MS,
|
|
594
|
+
],
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
// RETURNING proves only this statement's newly inserted lease AND intent.
|
|
598
|
+
// A conflict, legacy lease-only write or ambiguous result still needs the
|
|
599
|
+
// authoritative readback; swallowing a conflict never grants create rights.
|
|
600
|
+
const insertedProof = observed.rows[0]?.inserted_proof;
|
|
601
|
+
const persisted = insertedProof
|
|
602
|
+
? { rows: [insertedProof] }
|
|
603
|
+
: await client.query<RuntimeSandboxReservationProof>(
|
|
604
|
+
`SELECT lease.org_id, lease.run_id, lease.provider,
|
|
548
605
|
operation.attempt AS operation_attempt,
|
|
549
606
|
operation.environment AS operation_environment,
|
|
550
607
|
operation.provider AS operation_provider,
|
|
@@ -555,8 +612,8 @@ export async function reserveRuntimeSandboxCapacityLease(
|
|
|
555
612
|
LEFT JOIN ${operations} AS operation
|
|
556
613
|
ON operation.operation_key = lease.lease_id
|
|
557
614
|
WHERE lease.lease_id = $1`,
|
|
558
|
-
|
|
559
|
-
|
|
615
|
+
[leaseId],
|
|
616
|
+
);
|
|
560
617
|
const row = persisted.rows[0];
|
|
561
618
|
if (
|
|
562
619
|
!row ||
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-worker-wake.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import dns from 'node:dns';
|
|
2
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
3
|
+
|
|
4
|
+
export const RUNTIME_WORKER_WAKE_PATH = '/internal/runtime-worker/wake';
|
|
5
|
+
|
|
6
|
+
/** Wake the existing, fixed queue only. The hint cannot admit or name a task. */
|
|
7
|
+
export function handleRuntimeWorkerWake(
|
|
8
|
+
req: IncomingMessage,
|
|
9
|
+
res: ServerResponse,
|
|
10
|
+
input: { expectedToken: string | undefined; wake: (() => boolean) | null },
|
|
11
|
+
): boolean {
|
|
12
|
+
if (
|
|
13
|
+
new URL(req.url ?? '/', 'http://worker.local').pathname !==
|
|
14
|
+
RUNTIME_WORKER_WAKE_PATH
|
|
15
|
+
)
|
|
16
|
+
return false;
|
|
17
|
+
const respond = (status: number, body: string) => {
|
|
18
|
+
res.writeHead(status, {
|
|
19
|
+
'content-type': 'application/json',
|
|
20
|
+
'cache-control': 'no-store',
|
|
21
|
+
...(status === 405 ? { allow: 'POST' } : {}),
|
|
22
|
+
});
|
|
23
|
+
res.end(body);
|
|
24
|
+
};
|
|
25
|
+
if (req.method !== 'POST') {
|
|
26
|
+
respond(405, '{"error":"Use POST."}');
|
|
27
|
+
} else if (
|
|
28
|
+
!input.expectedToken?.trim() ||
|
|
29
|
+
req.headers.authorization?.trim() !== `Bearer ${input.expectedToken.trim()}`
|
|
30
|
+
) {
|
|
31
|
+
respond(401, '{"error":"Unauthorized."}');
|
|
32
|
+
} else if (!input.wake?.()) {
|
|
33
|
+
respond(503, '{"error":"Worker is not accepting wakes."}');
|
|
34
|
+
} else {
|
|
35
|
+
respond(202, '{"ok":true}');
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type RuntimeWorkerWakeTarget = { url: string; token: string };
|
|
41
|
+
|
|
42
|
+
/** Deployment-owned routing only. No request may supply a worker destination. */
|
|
43
|
+
export function runtimeWorkerWakeTargetFromEnv(
|
|
44
|
+
env: Record<string, string | undefined> = process.env,
|
|
45
|
+
): RuntimeWorkerWakeTarget | null {
|
|
46
|
+
const url = env.DEEPLINE_RUNTIME_WORKER_WAKE_URL?.trim();
|
|
47
|
+
const token = env.DEEPLINE_INTERNAL_TOKEN?.trim();
|
|
48
|
+
return url && token ? { url, token } : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** A bounded hint, never a retry, admission, or correctness dependency. */
|
|
52
|
+
export async function requestRuntimeWorkerWake(
|
|
53
|
+
target: RuntimeWorkerWakeTarget,
|
|
54
|
+
): Promise<boolean> {
|
|
55
|
+
const startedAt = performance.now();
|
|
56
|
+
let phase = 'resolve';
|
|
57
|
+
let status: number | undefined;
|
|
58
|
+
// Bun 1.3.14 can drop AbortSignal.timeout's timer when DNS removes its listener.
|
|
59
|
+
const controller = new AbortController();
|
|
60
|
+
const signal = controller.signal;
|
|
61
|
+
const timeout = setTimeout(
|
|
62
|
+
() =>
|
|
63
|
+
controller.abort(
|
|
64
|
+
new DOMException('Worker wake timed out.', 'TimeoutError'),
|
|
65
|
+
),
|
|
66
|
+
500,
|
|
67
|
+
);
|
|
68
|
+
try {
|
|
69
|
+
const url = new URL(target.url);
|
|
70
|
+
// Fly's private names use IPv6. Bun 1.3.14 can select an unusable IPv4
|
|
71
|
+
// address even with node:http's family: 6; dial the resolved address.
|
|
72
|
+
if (url.protocol === 'http:' && url.hostname.endsWith('.internal')) {
|
|
73
|
+
const address = await new Promise<string>((resolve, reject) => {
|
|
74
|
+
const abort = () => reject(signal.reason);
|
|
75
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
76
|
+
dns.lookup(url.hostname, { family: 6 }, (error, address) => {
|
|
77
|
+
signal.removeEventListener('abort', abort);
|
|
78
|
+
if (signal.aborted) reject(signal.reason);
|
|
79
|
+
else if (error) reject(error);
|
|
80
|
+
else resolve(address);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
url.hostname = `[${address}]`;
|
|
84
|
+
}
|
|
85
|
+
phase = 'request';
|
|
86
|
+
const response = await fetch(url, {
|
|
87
|
+
method: 'POST',
|
|
88
|
+
headers: { authorization: `Bearer ${target.token}` },
|
|
89
|
+
redirect: 'error',
|
|
90
|
+
signal,
|
|
91
|
+
});
|
|
92
|
+
status = response.status;
|
|
93
|
+
phase = 'cancel_response';
|
|
94
|
+
await response.body?.cancel();
|
|
95
|
+
if (status === 202) {
|
|
96
|
+
console.info('[runtime.worker_wake.accepted]', {
|
|
97
|
+
status,
|
|
98
|
+
ms: performance.now() - startedAt,
|
|
99
|
+
});
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
console.warn('[runtime.worker_wake.failed]', { status });
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.warn('[runtime.worker_wake.failed]', {
|
|
105
|
+
phase,
|
|
106
|
+
status,
|
|
107
|
+
error: error instanceof Error ? error.name : 'TransportError',
|
|
108
|
+
code:
|
|
109
|
+
error instanceof Error && 'code' in error
|
|
110
|
+
? String(error.code)
|
|
111
|
+
: undefined,
|
|
112
|
+
});
|
|
113
|
+
} finally {
|
|
114
|
+
clearTimeout(timeout);
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** The existing gateway relays to its fixed private worker; it cannot name work. */
|
|
120
|
+
export async function handleRuntimeGatewayWorkerWake(
|
|
121
|
+
req: Request,
|
|
122
|
+
input: {
|
|
123
|
+
expectedToken: string | undefined;
|
|
124
|
+
target: RuntimeWorkerWakeTarget | null;
|
|
125
|
+
},
|
|
126
|
+
): Promise<Response | null> {
|
|
127
|
+
if (new URL(req.url).pathname !== RUNTIME_WORKER_WAKE_PATH) return null;
|
|
128
|
+
const respond = (status: number, body: string) =>
|
|
129
|
+
new Response(body, {
|
|
130
|
+
status,
|
|
131
|
+
headers: {
|
|
132
|
+
'content-type': 'application/json',
|
|
133
|
+
'cache-control': 'no-store',
|
|
134
|
+
...(status === 405 ? { allow: 'POST' } : {}),
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
if (req.method !== 'POST') return respond(405, '{"error":"Use POST."}');
|
|
138
|
+
if (
|
|
139
|
+
!input.expectedToken?.trim() ||
|
|
140
|
+
req.headers.get('authorization')?.trim() !==
|
|
141
|
+
`Bearer ${input.expectedToken.trim()}`
|
|
142
|
+
) {
|
|
143
|
+
return respond(401, '{"error":"Unauthorized."}');
|
|
144
|
+
}
|
|
145
|
+
if (!input.target || !(await requestRuntimeWorkerWake(input.target))) {
|
|
146
|
+
return respond(
|
|
147
|
+
503,
|
|
148
|
+
'{"error":"Worker wake unavailable; durable polling remains active."}',
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return respond(202, '{"ok":true}');
|
|
152
|
+
}
|
|
@@ -59,9 +59,11 @@ type ValidatedRuntimeTestPolicyOverrides =
|
|
|
59
59
|
| { ok: true; overrides: RuntimeTestPolicyOverrides | null }
|
|
60
60
|
| { ok: false; status: 400 | 403; error: string };
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
// Keep local construction removable when a Play never uses the test seams.
|
|
63
|
+
const SUPPORTED_RUNTIME_TEST_FAULTS = /* @__PURE__ */ (() =>
|
|
64
|
+
new Set<RuntimeTestFaultName>(
|
|
65
|
+
runtimeTestFaultDrills().map(({ id }) => id),
|
|
66
|
+
))();
|
|
65
67
|
|
|
66
68
|
const RUNTIME_TEST_POLICY_MS_FIELDS = new Set([
|
|
67
69
|
'receiptLeaseTtlMs',
|
|
@@ -14,3 +14,34 @@ const ALWAYS_FRESH_INTEGRATION_TOOL_IDS = new Set([
|
|
|
14
14
|
export function isAlwaysFreshIntegrationTool(toolId: string): boolean {
|
|
15
15
|
return ALWAYS_FRESH_INTEGRATION_TOOL_IDS.has(toolId.trim());
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
/** Catalog capabilities are added without changing legacy artifact semantics. */
|
|
19
|
+
export function toolReusableResultSupport(input: {
|
|
20
|
+
toolId?: string;
|
|
21
|
+
resultReuse?: 'read' | 'operation';
|
|
22
|
+
retry?: { idempotencyScope?: 'invocation'; requiresExecutionFence?: boolean };
|
|
23
|
+
authoredTags?: string[];
|
|
24
|
+
tags?: string[];
|
|
25
|
+
}): { supported: boolean; unavailableReason: string | null } {
|
|
26
|
+
if (input.toolId && isAlwaysFreshIntegrationTool(input.toolId))
|
|
27
|
+
return {
|
|
28
|
+
supported: false,
|
|
29
|
+
unavailableReason:
|
|
30
|
+
'Workspace edits and conflict checks must be observed on every invocation.',
|
|
31
|
+
};
|
|
32
|
+
const write =
|
|
33
|
+
input.resultReuse === 'operation' ||
|
|
34
|
+
input.retry?.idempotencyScope === 'invocation' ||
|
|
35
|
+
input.retry?.requiresExecutionFence === true ||
|
|
36
|
+
[...(input.authoredTags ?? []), ...(input.tags ?? [])].some(
|
|
37
|
+
(tag) => tag === 'write' || tag.endsWith('_write') || tag === 'persist',
|
|
38
|
+
);
|
|
39
|
+
return {
|
|
40
|
+
supported: !write && input.resultReuse === 'read',
|
|
41
|
+
unavailableReason: write
|
|
42
|
+
? 'This action performs an intended operation; completed operations recover within their run, but separate calls must execute independently.'
|
|
43
|
+
: input.resultReuse === 'read'
|
|
44
|
+
? null
|
|
45
|
+
: 'This action has no declared read-result reuse capability. Use staleAfterSeconds: 0; completed operations still recover within their run.',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -72,6 +72,16 @@ export function transientServiceUnavailableDetail(
|
|
|
72
72
|
) {
|
|
73
73
|
return detail;
|
|
74
74
|
}
|
|
75
|
+
// Convex can fail to start a clean isolate before a function begins. Keep
|
|
76
|
+
// the exact structured internal error retryable without
|
|
77
|
+
// treating a matching message alone as a transient failure.
|
|
78
|
+
if (
|
|
79
|
+
/(?=[\s\S]*"code"\s*:\s*"IsolateNotClean")(?=[\s\S]*"message"\s*:\s*"Couldn't start running this function")/i.test(
|
|
80
|
+
detail,
|
|
81
|
+
)
|
|
82
|
+
) {
|
|
83
|
+
return detail;
|
|
84
|
+
}
|
|
75
85
|
// Convex uses explicit ServiceUnavailable, ExpiredInQueue and
|
|
76
86
|
// TooManyConcurrentRequests admission responses, as
|
|
77
87
|
// well as this generic InternalServerError envelope for short control-plane
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** A dispatched HTTP write whose outcome could not be confirmed. */
|
|
2
|
+
export class CtxFetchAmbiguousWriteError extends Error {
|
|
3
|
+
static [Symbol.hasInstance](value: unknown): boolean {
|
|
4
|
+
return (
|
|
5
|
+
value !== null &&
|
|
6
|
+
typeof value === 'object' &&
|
|
7
|
+
(value as { code?: unknown }).code === 'CTX_FETCH_WRITE_OUTCOME_UNKNOWN'
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
readonly code = 'CTX_FETCH_WRITE_OUTCOME_UNKNOWN' as const;
|
|
12
|
+
readonly outcome = 'unknown' as const;
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
readonly method: string,
|
|
16
|
+
readonly origin: string,
|
|
17
|
+
options?: ErrorOptions,
|
|
18
|
+
) {
|
|
19
|
+
super(
|
|
20
|
+
`ctx.fetch ${method} to ${origin} was dispatched, but its outcome is unknown. Reconcile with the provider or retry only under its supported idempotency contract using the same intended-operation key.`,
|
|
21
|
+
options,
|
|
22
|
+
);
|
|
23
|
+
this.name = 'CtxFetchAmbiguousWriteError';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -38,6 +38,8 @@ export type PlayArtifactCompatibility = {
|
|
|
38
38
|
toolResponseReceiptRevision?: string;
|
|
39
39
|
/** Missing preserves Slack HITL's historical parked timeout behavior. */
|
|
40
40
|
slackHitlTimeoutOutcome?: 'error';
|
|
41
|
+
/** Missing preserves historical stored-output hydration into dataset rows. */
|
|
42
|
+
datasetRowInput?: 'current_run';
|
|
41
43
|
};
|
|
42
44
|
|
|
43
45
|
/** The only executable Play artifact contract. */
|
|
@@ -53,6 +55,8 @@ export type PlayBundleArtifact = {
|
|
|
53
55
|
/** Defaults to cjs_node20 when not present on legacy Node artifacts. */
|
|
54
56
|
artifactKind?: PlayArtifactKind;
|
|
55
57
|
entryFile: string;
|
|
58
|
+
/** Source declaration selected by a named-export bundle. Absent preserves legacy admission. */
|
|
59
|
+
exportName?: string;
|
|
56
60
|
virtualFilename: string;
|
|
57
61
|
sourceHash: string;
|
|
58
62
|
graphHash: string;
|