@effect-agent/platform-cloudflare 0.1.0-beta.37 → 0.1.0-beta.39
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/bindings-C00ZfjsX.d.mts +100 -0
- package/dist/index.d.mts +191 -206
- package/dist/index.mjs +157 -163
- package/dist/index.mjs.map +1 -1
- package/dist/interactive-browser.d.mts +23 -3
- package/dist/interactive-browser.mjs +110 -35
- package/dist/interactive-browser.mjs.map +1 -1
- package/dist/{scheduling-B-OFqoS9.mjs → prepared-admission-D5hQ-ofV.mjs} +121 -432
- package/dist/prepared-admission-D5hQ-ofV.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/scheduling.d.mts +47 -2
- package/dist/scheduling.mjs +339 -1
- package/dist/scheduling.mjs.map +1 -0
- package/dist/subscriptions.d.mts +52 -0
- package/dist/subscriptions.mjs +346 -0
- package/dist/subscriptions.mjs.map +1 -0
- package/package.json +18 -11
- package/src/alarm.ts +264 -265
- package/src/bindings.ts +30 -30
- package/src/browser-session-lifecycle.ts +142 -0
- package/src/client.ts +90 -98
- package/src/config.ts +7 -7
- package/src/index.ts +9 -11
- package/src/interactive-browser.ts +81 -70
- package/src/layers.ts +178 -257
- package/src/prepared-admission.ts +90 -0
- package/src/scheduling.ts +19 -55
- package/src/subscriptions.ts +661 -0
- package/src/{conversation-object.ts → thread-object.ts} +167 -136
- package/src/transport.ts +15 -15
- package/src/wake-scheduler.ts +17 -22
- package/dist/scheduling-B-OFqoS9.mjs.map +0 -1
- package/dist/scheduling-BJs_kHTx.d.mts +0 -142
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
} from "@effect-agent/sandbox";
|
|
33
33
|
import {
|
|
34
34
|
Context,
|
|
35
|
+
Clock,
|
|
35
36
|
Duration,
|
|
36
37
|
Effect,
|
|
37
38
|
Layer,
|
|
@@ -43,6 +44,9 @@ import {
|
|
|
43
44
|
type Scope,
|
|
44
45
|
} from "effect";
|
|
45
46
|
|
|
47
|
+
import { BrowserRunSessionLifecycle } from "./browser-session-lifecycle.ts";
|
|
48
|
+
export { BrowserRunCleanupError, BrowserRunSessionLifecycle } from "./browser-session-lifecycle.ts";
|
|
49
|
+
|
|
46
50
|
export const browserRunInteractiveImplementation = SandboxImplementation.make({
|
|
47
51
|
isolation: "isolated",
|
|
48
52
|
identity: "cloudflare-browser-run-interactive",
|
|
@@ -57,7 +61,6 @@ const MAX_LIVE_VIEW_EXPIRY_MILLIS = 60 * 60_000;
|
|
|
57
61
|
const MAX_HANDOFF_TIMEOUT_MILLIS = 30 * 60_000;
|
|
58
62
|
const MAX_HOST_TEXT_LENGTH = 8 * 1024;
|
|
59
63
|
const CLEANUP_STEP_TIMEOUT_MILLIS = 10_000;
|
|
60
|
-
const CLOSE_SESSION_TIMEOUT_MILLIS = 10_000;
|
|
61
64
|
const ACTION_NETWORK_QUIET_MILLIS = 200;
|
|
62
65
|
const ACTION_NETWORK_SETTLE_MILLIS = 2_000;
|
|
63
66
|
const ACTION_POST_STATE_MILLIS = 250;
|
|
@@ -330,15 +333,23 @@ export class BrowserRunInteractiveBinding extends Context.Service<
|
|
|
330
333
|
BrowserRunInteractiveBinding,
|
|
331
334
|
{
|
|
332
335
|
readonly launch: (keepAliveMillis: number) => Promise<BrowserRunInteractiveBrowser>;
|
|
333
|
-
|
|
336
|
+
/** Success proves whole-browser termination or exact-session absence. */
|
|
337
|
+
readonly closeSession: (
|
|
338
|
+
sessionId: Redacted.Redacted<string>,
|
|
339
|
+
) => Effect.Effect<void, InteractiveBrowserError>;
|
|
334
340
|
}
|
|
335
341
|
>()("@effect-agent/platform-cloudflare/BrowserRunInteractiveBinding") {
|
|
336
342
|
static layer(options: {
|
|
337
343
|
readonly browser: BrowserRun;
|
|
338
344
|
readonly viewport?: BrowserRunViewport;
|
|
339
|
-
}): Layer.Layer<
|
|
345
|
+
}): Layer.Layer<
|
|
346
|
+
BrowserRunInteractiveBinding,
|
|
347
|
+
InteractiveBrowserPolicyDeniedError,
|
|
348
|
+
BrowserRunSessionLifecycle
|
|
349
|
+
> {
|
|
340
350
|
return Layer.effect(BrowserRunInteractiveBinding)(
|
|
341
351
|
Effect.gen(function* () {
|
|
352
|
+
const lifecycle = yield* BrowserRunSessionLifecycle;
|
|
342
353
|
const viewport =
|
|
343
354
|
options.viewport === undefined ? undefined : yield* decodeViewport(options.viewport);
|
|
344
355
|
return {
|
|
@@ -349,8 +360,10 @@ export class BrowserRunInteractiveBinding extends Context.Service<
|
|
|
349
360
|
...(viewport === undefined ? {} : { defaultViewport: { ...viewport } }),
|
|
350
361
|
}),
|
|
351
362
|
),
|
|
352
|
-
|
|
353
|
-
|
|
363
|
+
closeSession: (sessionId: Redacted.Redacted<string>) =>
|
|
364
|
+
lifecycle
|
|
365
|
+
.close(sessionId)
|
|
366
|
+
.pipe(Effect.mapError((cause) => actionError("close", cause))),
|
|
354
367
|
};
|
|
355
368
|
}),
|
|
356
369
|
);
|
|
@@ -383,6 +396,7 @@ export interface BrowserRunInteractiveSession {
|
|
|
383
396
|
export class BrowserRunInteractiveHost extends Context.Service<
|
|
384
397
|
BrowserRunInteractiveHost,
|
|
385
398
|
{
|
|
399
|
+
readonly cleanupSemantics?: "confirmed-terminal";
|
|
386
400
|
readonly open: (
|
|
387
401
|
policy: InteractiveBrowserPolicy,
|
|
388
402
|
) => Effect.Effect<BrowserRunInteractiveSession, InteractiveBrowserError, Scope.Scope>;
|
|
@@ -1719,6 +1733,55 @@ const cdpCommand = <A>(
|
|
|
1719
1733
|
const makeHostService = (
|
|
1720
1734
|
binding: BrowserRunInteractiveBinding["Service"],
|
|
1721
1735
|
): BrowserRunInteractiveHost["Service"] => {
|
|
1736
|
+
const closeSession = binding.closeSession;
|
|
1737
|
+
const terminate = Effect.fn("BrowserRunInteractiveHost.terminate")(function* (
|
|
1738
|
+
sessionId: Redacted.Redacted<string>,
|
|
1739
|
+
entries: ReadonlyArray<CloseEntry>,
|
|
1740
|
+
) {
|
|
1741
|
+
const deadline = (yield* Clock.currentTimeMillis) + 10_000;
|
|
1742
|
+
yield* closeSession(sessionId).pipe(
|
|
1743
|
+
Effect.interruptible,
|
|
1744
|
+
Effect.timeoutOrElse({
|
|
1745
|
+
duration: "10 seconds",
|
|
1746
|
+
orElse: () => Effect.fail(actionError("close")),
|
|
1747
|
+
}),
|
|
1748
|
+
);
|
|
1749
|
+
const remaining = deadline - (yield* Clock.currentTimeMillis);
|
|
1750
|
+
if (remaining <= 0)
|
|
1751
|
+
return yield* Effect.logWarning(
|
|
1752
|
+
"Local browser teardown skipped after confirmed termination deadline",
|
|
1753
|
+
);
|
|
1754
|
+
// Remote termination is authoritative. Local cleanup must not veto it or extend the deadline.
|
|
1755
|
+
yield* runTeardown(entries).pipe(
|
|
1756
|
+
Effect.flatMap((failures) =>
|
|
1757
|
+
Effect.forEach(failures, (failure) => Effect.logWarning(failure.warning), {
|
|
1758
|
+
discard: true,
|
|
1759
|
+
}),
|
|
1760
|
+
),
|
|
1761
|
+
Effect.interruptible,
|
|
1762
|
+
Effect.timeout(`${remaining} millis`),
|
|
1763
|
+
Effect.catchCause(() =>
|
|
1764
|
+
Effect.logWarning("Local browser teardown incomplete after confirmed termination"),
|
|
1765
|
+
),
|
|
1766
|
+
);
|
|
1767
|
+
});
|
|
1768
|
+
const closeAcquired = Effect.fn("BrowserRunInteractiveHost.closeAcquired")(function* (
|
|
1769
|
+
browser: BrowserRunInteractiveBrowser,
|
|
1770
|
+
) {
|
|
1771
|
+
const sessionId = yield* Effect.try({
|
|
1772
|
+
try: browser.sessionId,
|
|
1773
|
+
catch: () => actionError("close"),
|
|
1774
|
+
}).pipe(
|
|
1775
|
+
Effect.flatMap(Schema.decodeUnknownEffect(BrowserRunSessionId)),
|
|
1776
|
+
Effect.mapError(() => actionError("close")),
|
|
1777
|
+
Effect.onError(() =>
|
|
1778
|
+
closeWithWarning(browser.close, "Closing an unidentified browser failed"),
|
|
1779
|
+
),
|
|
1780
|
+
);
|
|
1781
|
+
yield* terminate(Redacted.make(sessionId), [
|
|
1782
|
+
closeEntry(browser.close, "Closing the local browser connection failed"),
|
|
1783
|
+
]);
|
|
1784
|
+
});
|
|
1722
1785
|
const open = Effect.fn("BrowserRunInteractiveHost.open")(function* (
|
|
1723
1786
|
policy: InteractiveBrowserPolicy,
|
|
1724
1787
|
): Effect.fn.Return<BrowserRunInteractiveSession, InteractiveBrowserError, Scope.Scope> {
|
|
@@ -1733,7 +1796,6 @@ const makeHostService = (
|
|
|
1733
1796
|
};
|
|
1734
1797
|
const lifecycle = {
|
|
1735
1798
|
managedTeardownInstalled: false,
|
|
1736
|
-
explicitCloseInvoked: false,
|
|
1737
1799
|
};
|
|
1738
1800
|
const closers: Array<CloseEntry> = [];
|
|
1739
1801
|
const releaseBeforeManaged = (entry: CloseEntry): Effect.Effect<void> =>
|
|
@@ -1750,7 +1812,7 @@ const makeHostService = (
|
|
|
1750
1812
|
closeLateAcquisition(
|
|
1751
1813
|
signal,
|
|
1752
1814
|
() => binding.launch(keepAliveMillis(fixedPolicy)),
|
|
1753
|
-
(acquired) =>
|
|
1815
|
+
(acquired) => Effect.runPromise(closeAcquired(acquired)),
|
|
1754
1816
|
),
|
|
1755
1817
|
catch: (cause) =>
|
|
1756
1818
|
isCapacityRefusal(cause)
|
|
@@ -1765,9 +1827,11 @@ const makeHostService = (
|
|
|
1765
1827
|
),
|
|
1766
1828
|
(acquired) => {
|
|
1767
1829
|
state.disconnected.value = true;
|
|
1768
|
-
return
|
|
1769
|
-
|
|
1770
|
-
|
|
1830
|
+
return lifecycle.managedTeardownInstalled
|
|
1831
|
+
? Effect.void
|
|
1832
|
+
: closeAcquired(acquired).pipe(
|
|
1833
|
+
Effect.catch(() => Effect.logWarning("Whole-browser cleanup remains unconfirmed")),
|
|
1834
|
+
);
|
|
1771
1835
|
},
|
|
1772
1836
|
{ interruptible: true },
|
|
1773
1837
|
);
|
|
@@ -1901,7 +1965,7 @@ const makeHostService = (
|
|
|
1901
1965
|
|
|
1902
1966
|
const teardown = yield* Effect.uninterruptible(
|
|
1903
1967
|
Effect.gen(function* () {
|
|
1904
|
-
const cached = yield* Effect.cached(
|
|
1968
|
+
const cached = yield* Effect.cached(terminate(Redacted.make(sessionIdValue), closers));
|
|
1905
1969
|
lifecycle.managedTeardownInstalled = true;
|
|
1906
1970
|
yield* Effect.addFinalizer(() =>
|
|
1907
1971
|
Effect.uninterruptible(
|
|
@@ -1910,13 +1974,7 @@ const makeHostService = (
|
|
|
1910
1974
|
state.disconnected.value = true;
|
|
1911
1975
|
}).pipe(
|
|
1912
1976
|
Effect.andThen(cached),
|
|
1913
|
-
Effect.
|
|
1914
|
-
lifecycle.explicitCloseInvoked
|
|
1915
|
-
? Effect.void
|
|
1916
|
-
: Effect.forEach(failures, (failure) => Effect.logWarning(failure.warning)).pipe(
|
|
1917
|
-
Effect.asVoid,
|
|
1918
|
-
),
|
|
1919
|
-
),
|
|
1977
|
+
Effect.catch(() => Effect.logWarning("Whole-browser cleanup remains unconfirmed")),
|
|
1920
1978
|
),
|
|
1921
1979
|
),
|
|
1922
1980
|
);
|
|
@@ -1926,15 +1984,9 @@ const makeHostService = (
|
|
|
1926
1984
|
|
|
1927
1985
|
const close: Effect.Effect<void, InteractiveBrowserError> = Effect.uninterruptible(
|
|
1928
1986
|
Effect.sync(() => {
|
|
1929
|
-
lifecycle.explicitCloseInvoked = true;
|
|
1930
1987
|
state.closed.value = true;
|
|
1931
1988
|
state.disconnected.value = true;
|
|
1932
|
-
}).pipe(
|
|
1933
|
-
Effect.andThen(teardown),
|
|
1934
|
-
Effect.flatMap((failures) =>
|
|
1935
|
-
failures[0] === undefined ? Effect.void : Effect.fail(failures[0].error),
|
|
1936
|
-
),
|
|
1937
|
-
),
|
|
1989
|
+
}).pipe(Effect.andThen(teardown)),
|
|
1938
1990
|
);
|
|
1939
1991
|
|
|
1940
1992
|
const runtime = yield* makeHandle(page, fixedPolicy, startedAt, state, close);
|
|
@@ -2057,52 +2109,11 @@ const makeHostService = (
|
|
|
2057
2109
|
};
|
|
2058
2110
|
});
|
|
2059
2111
|
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
sessionId,
|
|
2065
|
-
).pipe(
|
|
2066
|
-
Effect.mapError(() => policyError("The Browser Run cleanup session identity is malformed")),
|
|
2067
|
-
);
|
|
2068
|
-
return yield* Effect.scoped(
|
|
2069
|
-
Effect.gen(function* () {
|
|
2070
|
-
const closeAttempted = { value: false };
|
|
2071
|
-
const browser = yield* Effect.acquireRelease(
|
|
2072
|
-
Effect.tryPromise({
|
|
2073
|
-
try: (signal) =>
|
|
2074
|
-
closeLateAcquisition(
|
|
2075
|
-
signal,
|
|
2076
|
-
() => binding.connect(Redacted.value(decoded)),
|
|
2077
|
-
(acquired) => acquired.close(),
|
|
2078
|
-
),
|
|
2079
|
-
catch: (cause) => actionError("close", cause),
|
|
2080
|
-
}),
|
|
2081
|
-
(acquired) =>
|
|
2082
|
-
closeAttempted.value
|
|
2083
|
-
? Effect.void
|
|
2084
|
-
: closeWithWarning(acquired.close, "Closing the leaked Browser Run session failed"),
|
|
2085
|
-
{ interruptible: true },
|
|
2086
|
-
);
|
|
2087
|
-
return yield* Effect.tryPromise({
|
|
2088
|
-
try: () => {
|
|
2089
|
-
// Mark and start are synchronous so interruption cannot suppress the
|
|
2090
|
-
// Scope fallback before the one remote close attempt begins.
|
|
2091
|
-
closeAttempted.value = true;
|
|
2092
|
-
return browser.close();
|
|
2093
|
-
},
|
|
2094
|
-
catch: (cause) => actionError("close", cause),
|
|
2095
|
-
});
|
|
2096
|
-
}),
|
|
2097
|
-
).pipe(
|
|
2098
|
-
Effect.timeoutOrElse({
|
|
2099
|
-
duration: Duration.millis(CLOSE_SESSION_TIMEOUT_MILLIS),
|
|
2100
|
-
orElse: () => Effect.fail(actionError("close")),
|
|
2101
|
-
}),
|
|
2102
|
-
);
|
|
2112
|
+
return BrowserRunInteractiveHost.of({
|
|
2113
|
+
open,
|
|
2114
|
+
closeSession,
|
|
2115
|
+
cleanupSemantics: "confirmed-terminal",
|
|
2103
2116
|
});
|
|
2104
|
-
|
|
2105
|
-
return BrowserRunInteractiveHost.of({ open, closeSession });
|
|
2106
2117
|
};
|
|
2107
2118
|
|
|
2108
2119
|
/** Cloudflare host controls and private session identity for one scoped Browser Run pass. */
|