@songsid/agend 2.1.12-beta.9 → 2.2.0-alpha.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 +2 -2
- package/README.zh-TW.md +2 -2
- package/dist/auth/one-time-code.d.ts +19 -0
- package/dist/auth/one-time-code.js +49 -0
- package/dist/auth/one-time-code.js.map +1 -0
- package/dist/auth-api.d.ts +52 -0
- package/dist/auth-api.js +346 -0
- package/dist/auth-api.js.map +1 -0
- package/dist/backend/claude-code.d.ts +4 -0
- package/dist/backend/claude-code.js +97 -0
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex.js +59 -30
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/kiro-agent.d.ts +66 -0
- package/dist/backend/kiro-agent.js +302 -0
- package/dist/backend/kiro-agent.js.map +1 -0
- package/dist/backend/kiro-identity.d.ts +80 -0
- package/dist/backend/kiro-identity.js +537 -0
- package/dist/backend/kiro-identity.js.map +1 -0
- package/dist/backend/kiro-v2-store-worker.d.ts +1 -0
- package/dist/backend/kiro-v2-store-worker.js +87 -0
- package/dist/backend/kiro-v2-store-worker.js.map +1 -0
- package/dist/backend/kiro-v2-store.d.ts +54 -0
- package/dist/backend/kiro-v2-store.js +131 -0
- package/dist/backend/kiro-v2-store.js.map +1 -0
- package/dist/backend/kiro-v3-identity.d.ts +5 -0
- package/dist/backend/kiro-v3-identity.js +22 -0
- package/dist/backend/kiro-v3-identity.js.map +1 -1
- package/dist/backend/kiro.d.ts +83 -7
- package/dist/backend/kiro.js +418 -91
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +45 -3
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +1 -0
- package/dist/channel/adapters/discord.js +29 -10
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +1 -0
- package/dist/channel/adapters/telegram.js +13 -1
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/types.d.ts +9 -1
- package/dist/classic-channel-manager.d.ts +18 -10
- package/dist/classic-channel-manager.js +45 -18
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +225 -127
- package/dist/cli.js.map +1 -1
- package/dist/command-table.d.ts +5 -3
- package/dist/command-table.js +29 -27
- package/dist/command-table.js.map +1 -1
- package/dist/config-validator.js +46 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +12 -0
- package/dist/config.js.map +1 -1
- package/dist/connection-secrets.d.ts +6 -0
- package/dist/connection-secrets.js +4 -0
- package/dist/connection-secrets.js.map +1 -1
- package/dist/daemon.d.ts +89 -4
- package/dist/daemon.js +661 -77
- package/dist/daemon.js.map +1 -1
- package/dist/darwin-memory.d.ts +6 -1
- package/dist/darwin-memory.js +17 -6
- package/dist/darwin-memory.js.map +1 -1
- package/dist/delivery-consumed-watch.d.ts +85 -0
- package/dist/delivery-consumed-watch.js +244 -0
- package/dist/delivery-consumed-watch.js.map +1 -0
- package/dist/delivery-outbox.d.ts +61 -0
- package/dist/delivery-outbox.js +120 -2
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-reconciliation.d.ts +14 -5
- package/dist/delivery-reconciliation.js +27 -4
- package/dist/delivery-reconciliation.js.map +1 -1
- package/dist/delivery-reconciliation.test.js +35 -1
- package/dist/delivery-reconciliation.test.js.map +1 -1
- package/dist/event-loop-watch.d.ts +28 -0
- package/dist/event-loop-watch.js +75 -3
- package/dist/event-loop-watch.js.map +1 -1
- package/dist/fleet-context.d.ts +22 -2
- package/dist/fleet-level-config.d.ts +2 -2
- package/dist/fleet-level-config.js +6 -1
- package/dist/fleet-level-config.js.map +1 -1
- package/dist/fleet-manager.d.ts +293 -12
- package/dist/fleet-manager.js +2364 -449
- package/dist/fleet-manager.js.map +1 -1
- package/dist/host-memory.d.ts +4 -0
- package/dist/host-memory.js.map +1 -1
- package/dist/instance-config-impact.js +5 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +10 -5
- package/dist/instance-lifecycle.js +162 -53
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/locale.js +150 -16
- package/dist/locale.js.map +1 -1
- package/dist/login-controller.d.ts +2 -0
- package/dist/login-controller.js +17 -5
- package/dist/login-controller.js.map +1 -1
- package/dist/memory-pressure.d.ts +7 -8
- package/dist/memory-pressure.js +76 -15
- package/dist/memory-pressure.js.map +1 -1
- package/dist/monotonic-budget.d.ts +2 -0
- package/dist/monotonic-budget.js +28 -0
- package/dist/monotonic-budget.js.map +1 -0
- package/dist/needs-you-hub.d.ts +114 -0
- package/dist/needs-you-hub.js +370 -0
- package/dist/needs-you-hub.js.map +1 -0
- package/dist/needs-you-live.d.ts +116 -0
- package/dist/needs-you-live.js +228 -0
- package/dist/needs-you-live.js.map +1 -0
- package/dist/needs-you.d.ts +132 -0
- package/dist/needs-you.js +164 -0
- package/dist/needs-you.js.map +1 -0
- package/dist/node-version-guard.d.ts +30 -0
- package/dist/node-version-guard.js +50 -0
- package/dist/node-version-guard.js.map +1 -0
- package/dist/operator-control-paths.d.ts +4 -0
- package/dist/operator-control-paths.js +24 -0
- package/dist/operator-control-paths.js.map +1 -0
- package/dist/outbound-handlers.d.ts +2 -0
- package/dist/outbound-handlers.js +9 -2
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +5 -5
- package/dist/profile-control.js +4 -20
- package/dist/profile-control.js.map +1 -1
- package/dist/public-web-gateway.d.ts +18 -0
- package/dist/public-web-gateway.js +141 -0
- package/dist/public-web-gateway.js.map +1 -0
- package/dist/public-web-link.d.ts +53 -0
- package/dist/public-web-link.js +175 -0
- package/dist/public-web-link.js.map +1 -0
- package/dist/quickstart-api.d.ts +8 -1
- package/dist/quickstart-api.js +86 -48
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.d.ts +1 -0
- package/dist/quickstart.js +6 -2
- package/dist/quickstart.js.map +1 -1
- package/dist/scheduler/db.d.ts +7 -1
- package/dist/scheduler/db.js +38 -0
- package/dist/scheduler/db.js.map +1 -1
- package/dist/scheduler/index.d.ts +1 -1
- package/dist/scheduler/retry-label.d.ts +11 -0
- package/dist/scheduler/retry-label.js +24 -0
- package/dist/scheduler/retry-label.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +55 -2
- package/dist/scheduler/scheduler.js +220 -8
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/scheduler/types.d.ts +22 -0
- package/dist/scheduler/types.js.map +1 -1
- package/dist/secret-store.d.ts +5 -0
- package/dist/secret-store.js +20 -0
- package/dist/secret-store.js.map +1 -1
- package/dist/service-activation.d.ts +110 -0
- package/dist/service-activation.js +302 -0
- package/dist/service-activation.js.map +1 -0
- package/dist/service-installer.d.ts +7 -0
- package/dist/service-installer.js +7 -4
- package/dist/service-installer.js.map +1 -1
- package/dist/service-restart-selection.d.ts +6 -5
- package/dist/service-restart-selection.js +6 -5
- package/dist/service-restart-selection.js.map +1 -1
- package/dist/settings-api.d.ts +10 -2
- package/dist/settings-api.js +175 -72
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-baseline.d.ts +20 -0
- package/dist/settings-baseline.js +104 -0
- package/dist/settings-baseline.js.map +1 -0
- package/dist/settings-change.d.ts +21 -0
- package/dist/settings-change.js +133 -0
- package/dist/settings-change.js.map +1 -0
- package/dist/settings-confirmation.d.ts +105 -0
- package/dist/settings-confirmation.js +232 -0
- package/dist/settings-confirmation.js.map +1 -0
- package/dist/settings-control.d.ts +41 -0
- package/dist/settings-control.js +294 -0
- package/dist/settings-control.js.map +1 -0
- package/dist/settings-effect.d.ts +24 -0
- package/dist/settings-effect.js +169 -0
- package/dist/settings-effect.js.map +1 -0
- package/dist/settings-http-confirmation.d.ts +29 -0
- package/dist/settings-http-confirmation.js +123 -0
- package/dist/settings-http-confirmation.js.map +1 -0
- package/dist/settings-instance-patch.d.ts +4 -0
- package/dist/settings-instance-patch.js +26 -0
- package/dist/settings-instance-patch.js.map +1 -0
- package/dist/settings-request-capability.d.ts +16 -0
- package/dist/settings-request-capability.js +32 -0
- package/dist/settings-request-capability.js.map +1 -0
- package/dist/settings-transaction.d.ts +65 -0
- package/dist/settings-transaction.js +223 -0
- package/dist/settings-transaction.js.map +1 -0
- package/dist/setup-auth.d.ts +10 -18
- package/dist/setup-auth.js +14 -38
- package/dist/setup-auth.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +75 -18
- package/dist/setup-form.js.map +1 -1
- package/dist/setup-guide.d.ts +13 -0
- package/dist/setup-guide.js +25 -0
- package/dist/setup-guide.js.map +1 -0
- package/dist/setup-host.d.ts +16 -0
- package/dist/setup-host.js +197 -32
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-wizard.js +3 -0
- package/dist/setup-wizard.js.map +1 -1
- package/dist/slash-authz.d.ts +17 -3
- package/dist/slash-authz.js +5 -0
- package/dist/slash-authz.js.map +1 -1
- package/dist/spawn-gate.js +3 -3
- package/dist/spawn-gate.js.map +1 -1
- package/dist/statusline-watcher.d.ts +5 -0
- package/dist/statusline-watcher.js +2 -0
- package/dist/statusline-watcher.js.map +1 -1
- package/dist/steer-capability.d.ts +6 -0
- package/dist/steer-capability.js +8 -0
- package/dist/steer-capability.js.map +1 -1
- package/dist/sync-work-attribution.d.ts +14 -0
- package/dist/sync-work-attribution.js +43 -8
- package/dist/sync-work-attribution.js.map +1 -1
- package/dist/tips.js +10 -5
- package/dist/tips.js.map +1 -1
- package/dist/tmux-control.d.ts +35 -5
- package/dist/tmux-control.js +291 -57
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-manager.d.ts +20 -6
- package/dist/tmux-manager.js +90 -47
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-read.d.ts +59 -0
- package/dist/tmux-read.js +213 -0
- package/dist/tmux-read.js.map +1 -0
- package/dist/topic-commands.d.ts +21 -10
- package/dist/topic-commands.js +191 -58
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-delta-reader.js +2 -1
- package/dist/transcript-delta-reader.js.map +1 -1
- package/dist/tunnel/cloudflared-install.d.ts +2 -0
- package/dist/tunnel/cloudflared-install.js +1 -1
- package/dist/tunnel/cloudflared-install.js.map +1 -1
- package/dist/tunnel/purpose-lane.d.ts +14 -0
- package/dist/tunnel/purpose-lane.js +43 -0
- package/dist/tunnel/purpose-lane.js.map +1 -0
- package/dist/turn-reply-guard.d.ts +2 -0
- package/dist/turn-reply-guard.js +10 -1
- package/dist/turn-reply-guard.js.map +1 -1
- package/dist/types.d.ts +33 -0
- package/dist/ui/app.html +18 -0
- package/dist/ui/chat-render.js +478 -0
- package/dist/ui/chat-store.js +289 -0
- package/dist/ui/chat-strings.js +69 -0
- package/dist/ui/chat-thread.js +209 -0
- package/dist/ui/chat-tour.js +116 -0
- package/dist/ui/fleet-org.js +142 -0
- package/dist/ui/panel-chat.js +418 -0
- package/dist/ui/panel-fleet.js +413 -0
- package/dist/ui/panel-needs.js +202 -0
- package/dist/ui/panel-settings.js +750 -0
- package/dist/ui/preview.js +266 -0
- package/dist/ui/settings-apply.js +239 -0
- package/dist/ui/settings-confirm.js +186 -0
- package/dist/ui/settings-dialogs.js +546 -0
- package/dist/ui/settings-model.js +277 -0
- package/dist/ui/settings-strings.js +652 -0
- package/dist/ui/settings-wizard.js +140 -0
- package/dist/ui/shared/agend-auth.js +93 -0
- package/dist/ui/shared/app-ctx.js +80 -0
- package/dist/ui/shared/app-html.js +9 -0
- package/dist/ui/shared/app-i18n.js +95 -0
- package/dist/ui/shared/app-nav.js +73 -0
- package/dist/ui/shared/app-needs.js +114 -0
- package/dist/ui/shared/app-route.js +73 -0
- package/dist/ui/shared/app-session.js +66 -0
- package/dist/ui/shared/app-shell.js +344 -0
- package/dist/ui/shared/app-store.js +51 -0
- package/dist/ui/shared/app-stream.js +201 -0
- package/dist/ui/shared/app.css +676 -0
- package/dist/ui/shared/app.js +88 -0
- package/dist/ui/shared/fonts/OFL.txt +92 -0
- package/dist/ui/shared/fonts/README.md +21 -0
- package/dist/ui/shared/fonts/inter.woff2 +0 -0
- package/dist/ui/shared/htm.module.js +1 -0
- package/dist/ui/shared/panel-view.js +655 -0
- package/dist/ui/shared/preact-hooks.module.js +2 -0
- package/dist/ui/shared/preact.module.js +2 -0
- package/dist/ui/shared/signin.js +130 -0
- package/dist/ui/shared/theme.js +29 -0
- package/dist/ui/shared/tokens.css +153 -0
- package/dist/ui/shared/ui-confirm.js +43 -0
- package/dist/ui/shared/ui-dialog.js +45 -0
- package/dist/ui/shared/ui-icons.js +61 -0
- package/dist/ui/shared/ui-menu.js +39 -0
- package/dist/ui/shared/ui-states.js +20 -0
- package/dist/ui/shared/ui-toast.js +18 -0
- package/dist/ui/shared/vendor/htm.LICENSE +202 -0
- package/dist/ui/shared/vendor/preact.LICENSE +21 -0
- package/dist/ui/shared/vendor/vendor.json +39 -0
- package/dist/ui/shared/view-strings.js +309 -0
- package/dist/ui/signin.html +51 -0
- package/dist/update-check.d.ts +6 -2
- package/dist/update-check.js +12 -3
- package/dist/update-check.js.map +1 -1
- package/dist/update-install.d.ts +71 -0
- package/dist/update-install.js +162 -0
- package/dist/update-install.js.map +1 -0
- package/dist/upgrade-notices.d.ts +17 -0
- package/dist/upgrade-notices.js +93 -0
- package/dist/upgrade-notices.js.map +1 -0
- package/dist/usage/statusline-usage.d.ts +2 -0
- package/dist/usage/statusline-usage.js +14 -10
- package/dist/usage/statusline-usage.js.map +1 -1
- package/dist/view-api.d.ts +6 -1
- package/dist/view-api.js +62 -51
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.d.ts +71 -14
- package/dist/web-api.js +492 -77
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.d.ts +71 -27
- package/dist/web-auth.js +169 -69
- package/dist/web-auth.js.map +1 -1
- package/dist/web-body.d.ts +3 -0
- package/dist/web-body.js +55 -0
- package/dist/web-body.js.map +1 -0
- package/dist/web-channel-echo.d.ts +9 -0
- package/dist/web-channel-echo.js +55 -0
- package/dist/web-channel-echo.js.map +1 -0
- package/dist/web-chat-history.d.ts +125 -0
- package/dist/web-chat-history.js +184 -0
- package/dist/web-chat-history.js.map +1 -0
- package/dist/web-continuation.d.ts +7 -0
- package/dist/web-continuation.js +22 -0
- package/dist/web-continuation.js.map +1 -0
- package/dist/web-host-guard.d.ts +34 -0
- package/dist/web-host-guard.js +57 -1
- package/dist/web-host-guard.js.map +1 -1
- package/dist/web-login.d.ts +72 -0
- package/dist/web-login.js +132 -0
- package/dist/web-login.js.map +1 -0
- package/dist/web-org.d.ts +55 -0
- package/dist/web-org.js +67 -0
- package/dist/web-org.js.map +1 -0
- package/dist/web-preview.d.ts +77 -0
- package/dist/web-preview.js +240 -0
- package/dist/web-preview.js.map +1 -0
- package/dist/web-request-context.d.ts +10 -0
- package/dist/web-request-context.js +13 -0
- package/dist/web-request-context.js.map +1 -0
- package/dist/web-session.d.ts +167 -0
- package/dist/web-session.js +389 -0
- package/dist/web-session.js.map +1 -0
- package/dist/web-shell-routes.d.ts +59 -0
- package/dist/web-shell-routes.js +96 -0
- package/dist/web-shell-routes.js.map +1 -0
- package/dist/web-upload.d.ts +183 -0
- package/dist/web-upload.js +468 -0
- package/dist/web-upload.js.map +1 -0
- package/dist/website-tips-renderer.d.ts +2 -2
- package/dist/website-tips-renderer.js +3 -3
- package/dist/website-tips-renderer.js.map +1 -1
- package/package.json +8 -5
- package/scripts/preinstall-guard.cjs +36 -0
- package/dist/ui/dashboard.html +0 -774
- package/dist/ui/settings.html +0 -2165
- package/dist/ui/view.html +0 -1139
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// #1408 §3/§5: the app's one source of live updates, for the whole page whatever panel is showing.
|
|
2
|
+
//
|
|
3
|
+
// The mode comes from the server (<body data-mode / data-web-transport>):
|
|
4
|
+
// - local, signed in: ONE EventSource on /ui/events; while it is silent or down, /ui/poll every 5 s until it speaks.
|
|
5
|
+
// - the public link (data-web-transport="poll"): NO EventSource (its manifest has no /ui/events); /ui/poll from the start.
|
|
6
|
+
// - View-only (step 2): neither.
|
|
7
|
+
// A proxy that buffers a stream looks exactly like a server that never sends, so a stream that says nothing is treated
|
|
8
|
+
// as no stream. Every read here is on the #1374 passive list: no timer here ever counts as the person's activity.
|
|
9
|
+
// The poll's cursor is the stream's cursor; ticks and prompts come with every poll, so nothing is re-read elsewhere.
|
|
10
|
+
//
|
|
11
|
+
// Catching up (#1425 review): a listener that attached after the stream opened (the chat loading only on Retry) missed
|
|
12
|
+
// the frames sent on connect, and a healthy stream sends only status after that. catchUp() reads /ui/poll once — and
|
|
13
|
+
// orders it against the live stream: while that read is in flight, live events are held, and once its snapshot is
|
|
14
|
+
// applied they are replayed in arrival order, so an answer or a new prompt that came meanwhile is never undone by the
|
|
15
|
+
// older snapshot. A failed read is retried a few times with a growing delay, and then waits for the person (Retry);
|
|
16
|
+
// the state is told as "hydration" events. Polls never overlap: a poll asked for while one runs follows it.
|
|
17
|
+
|
|
18
|
+
const EVENTS = ["status", "message", "delivery", "deliveries", "prompt", "prompts", "prompt_resolved", "activity", "needs"];
|
|
19
|
+
const MAX_HELD = 1000; // live events held during one catch-up read; more → read again afterwards
|
|
20
|
+
const RETRY_DELAYS = [1000, 2000, 4000]; // after the first failed catch-up read; then it waits for Retry
|
|
21
|
+
const POLL_LIMIT_MS = 10000; // a poll that has not answered by then counts as failed
|
|
22
|
+
const MAX_OVERFLOW_READS = 3; // reads again after dropped live events, within one catch-up
|
|
23
|
+
|
|
24
|
+
export function createStream(opts) {
|
|
25
|
+
const env = opts.env || globalThis;
|
|
26
|
+
const listeners = new Map(); // event name → Set of handlers
|
|
27
|
+
const state = { connection: "connecting", cursor: "", hydration: "none" };
|
|
28
|
+
let sse = null, pollTimer = null, silentTimer = null, errorTimer = null, closed = false, started = false;
|
|
29
|
+
let polling = null, pollAgain = false, pollSeq = 0; // the poll in flight, whether another was asked for meanwhile
|
|
30
|
+
let held = null; // live events held while a catch-up read is in flight
|
|
31
|
+
let catching = null, retryTimer = null;
|
|
32
|
+
|
|
33
|
+
function emit(name, data, extra) {
|
|
34
|
+
const set = listeners.get(name);
|
|
35
|
+
if (set) for (const fn of [...set]) { try { fn(data, extra); } catch (e) { if (env.console) env.console.error(e); } }
|
|
36
|
+
}
|
|
37
|
+
function setConnection(c) { if (state.connection !== c) { state.connection = c; emit("connection", c); } }
|
|
38
|
+
function setHydration(h) { if (state.hydration !== h) { state.hydration = h; emit("hydration", h); } }
|
|
39
|
+
|
|
40
|
+
/** A live event: delivered now, or held (in order) while a catch-up snapshot is on its way. */
|
|
41
|
+
function live(name, data, cursor) {
|
|
42
|
+
if (held) {
|
|
43
|
+
if (held.length < MAX_HELD) held.push([name, data, cursor]); else held.overflow = true;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (cursor) state.cursor = cursor;
|
|
47
|
+
emit(name, data);
|
|
48
|
+
// A full set of open prompts arrives on every (re)connect: whatever a failed catch-up missed is current again.
|
|
49
|
+
if (name === "prompts" && (state.hydration === "failed" || state.hydration === "retrying") && !catching) setHydration("ok");
|
|
50
|
+
}
|
|
51
|
+
function release() {
|
|
52
|
+
const list = held;
|
|
53
|
+
held = null;
|
|
54
|
+
if (!list) return false;
|
|
55
|
+
for (const [name, data, cursor] of list) { if (cursor) state.cursor = cursor; emit(name, data); }
|
|
56
|
+
return !!list.overflow;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* One /ui/poll; resolves { ok, overflow }: ok when it was read and applied, overflow when more live events arrived
|
|
61
|
+
* meanwhile than could be held (what was held is applied; the rest is recovered by reading again — see below).
|
|
62
|
+
* - Ordered against the live stream, whatever asked for it (the fallback loop, a catch-up, a poll queued behind
|
|
63
|
+
* another): live events that arrive while it is in flight are held and replayed after its snapshot, so an older
|
|
64
|
+
* snapshot never lands on top of newer events (#1425 review).
|
|
65
|
+
* - Never two at once: a poll asked for meanwhile runs after it — unless the caller only wants the current read
|
|
66
|
+
* (`join`, the catch-up): then it waits for that one and queues nothing.
|
|
67
|
+
* - One that has not answered within POLL_LIMIT_MS counts as failed and its late answer is ignored, so a stuck
|
|
68
|
+
* request never holds up the polls after it (or the live events it holds).
|
|
69
|
+
*/
|
|
70
|
+
function pollOnce(opts = {}) {
|
|
71
|
+
if (polling) { if (!opts.join) pollAgain = true; return polling; }
|
|
72
|
+
const mine = ++pollSeq;
|
|
73
|
+
if (sse && !held) held = [];
|
|
74
|
+
let limit = null;
|
|
75
|
+
const read = (async () => {
|
|
76
|
+
try {
|
|
77
|
+
const r = await env.fetch(`/ui/poll?after=${encodeURIComponent(state.cursor)}`, { cache: "no-store" });
|
|
78
|
+
if (closed || mine !== pollSeq || !r.ok) return false;
|
|
79
|
+
const d = await r.json();
|
|
80
|
+
if (closed || mine !== pollSeq) return false;
|
|
81
|
+
if (d.status) emit("status", d.status);
|
|
82
|
+
for (const m of d.messages || []) emit("message", m);
|
|
83
|
+
if (typeof d.cursor === "string") state.cursor = d.cursor;
|
|
84
|
+
if (Array.isArray(d.deliveries)) emit("deliveries", d.deliveries);
|
|
85
|
+
if (Array.isArray(d.prompts)) emit("prompts", d.prompts);
|
|
86
|
+
if (Array.isArray(d.needs)) emit("needs", { items: d.needs });
|
|
87
|
+
return true;
|
|
88
|
+
} catch { return false; /* the next tick, or the catch-up's retry, tries again */ }
|
|
89
|
+
})();
|
|
90
|
+
const timedOut = new Promise((resolve) => { limit = env.setTimeout(() => { if (mine === pollSeq) pollSeq++; resolve(false); }, POLL_LIMIT_MS); });
|
|
91
|
+
polling = Promise.race([read, timedOut]).then((ok) => {
|
|
92
|
+
env.clearTimeout(limit);
|
|
93
|
+
if (mine === pollSeq) pollSeq++; // whatever this read still does from here on is ignored
|
|
94
|
+
polling = null;
|
|
95
|
+
if (closed) { held = null; return { ok: false, overflow: false }; }
|
|
96
|
+
const overflow = release();
|
|
97
|
+
// Live events were dropped: what they changed is recovered by catching up — inside its lifecycle, so a failed
|
|
98
|
+
// recovery read is retried and shown, never taken for done (#1425 review). A catch-up awaiting this read loops.
|
|
99
|
+
if (overflow && !catching) catchUp();
|
|
100
|
+
if (pollAgain) { pollAgain = false; pollOnce(); }
|
|
101
|
+
return { ok, overflow };
|
|
102
|
+
});
|
|
103
|
+
return polling;
|
|
104
|
+
}
|
|
105
|
+
function startPolling() {
|
|
106
|
+
if (pollTimer || closed) return;
|
|
107
|
+
setConnection("polling");
|
|
108
|
+
pollOnce();
|
|
109
|
+
pollTimer = env.setInterval(pollOnce, 5000);
|
|
110
|
+
}
|
|
111
|
+
function stopPolling() { if (pollTimer) { env.clearInterval(pollTimer); pollTimer = null; } }
|
|
112
|
+
function alive() {
|
|
113
|
+
stopPolling();
|
|
114
|
+
env.clearTimeout(errorTimer); errorTimer = null;
|
|
115
|
+
env.clearTimeout(silentTimer); silentTimer = env.setTimeout(startPolling, 30000);
|
|
116
|
+
setConnection("live");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Read what is current now, once, ordered against the live stream (see the top). Resolves true once a read was
|
|
121
|
+
* applied. While retries are spent it resolves false and the state stays "failed" until catchUp() is called again.
|
|
122
|
+
* It opens no transport: still the one EventSource, and no polling loop.
|
|
123
|
+
*/
|
|
124
|
+
function catchUp() {
|
|
125
|
+
if (closed) return Promise.resolve(false);
|
|
126
|
+
if (catching) return catching;
|
|
127
|
+
env.clearTimeout(retryTimer); retryTimer = null;
|
|
128
|
+
setHydration("catching");
|
|
129
|
+
catching = (async () => {
|
|
130
|
+
let failures = 0, overflows = 0;
|
|
131
|
+
for (;;) {
|
|
132
|
+
// A poll already on its way will do (its answer reaches the listener that just attached); nothing is queued.
|
|
133
|
+
const { ok, overflow } = await pollOnce({ join: true });
|
|
134
|
+
if (closed) return false;
|
|
135
|
+
if (ok && !overflow) { catching = null; setHydration("ok"); return true; }
|
|
136
|
+
if (ok) {
|
|
137
|
+
// Read, but live events were dropped meanwhile: not done until a read without that succeeds. At once (nothing
|
|
138
|
+
// failed), and bounded: a stream that floods every read gives up as failed, with Retry.
|
|
139
|
+
if (++overflows > MAX_OVERFLOW_READS) { catching = null; setHydration("failed"); return false; }
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (failures >= RETRY_DELAYS.length) { catching = null; setHydration("failed"); return false; }
|
|
143
|
+
setHydration("retrying");
|
|
144
|
+
await new Promise((resolve) => { retryTimer = env.setTimeout(resolve, RETRY_DELAYS[failures++]); });
|
|
145
|
+
retryTimer = null;
|
|
146
|
+
if (closed) return false;
|
|
147
|
+
}
|
|
148
|
+
})();
|
|
149
|
+
return catching;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Open the transport. Called once every listener that must see the first frames is attached (the chat's store):
|
|
153
|
+
* a status, prompts or deliveries frame sent on connect is never missed. */
|
|
154
|
+
function start() {
|
|
155
|
+
if (started || closed) return;
|
|
156
|
+
started = true;
|
|
157
|
+
if (opts.mode === "full" && opts.transport === "poll") startPolling();
|
|
158
|
+
else if (opts.mode === "full") {
|
|
159
|
+
sse = new env.EventSource("/ui/events");
|
|
160
|
+
silentTimer = env.setTimeout(startPolling, 15000); // the server sends a status frame the moment a stream opens
|
|
161
|
+
for (const name of EVENTS) {
|
|
162
|
+
sse.addEventListener(name, (e) => {
|
|
163
|
+
alive();
|
|
164
|
+
let data;
|
|
165
|
+
try { data = JSON.parse(e.data); } catch { return; }
|
|
166
|
+
live(name, data, name === "message" ? e.lastEventId : null);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
sse.onerror = () => {
|
|
170
|
+
if (state.connection === "live") setConnection("down");
|
|
171
|
+
// Armed once, not re-armed: the browser retries every few seconds and each retry fires this again.
|
|
172
|
+
if (!errorTimer) errorTimer = env.setTimeout(startPolling, 5000);
|
|
173
|
+
};
|
|
174
|
+
} else setConnection("none");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
/** Listen to one event ("status", "message", …, "connection" or "hydration"); returns the unsubscribe. */
|
|
179
|
+
on(name, fn) {
|
|
180
|
+
if (!listeners.has(name)) listeners.set(name, new Set());
|
|
181
|
+
listeners.get(name).add(fn);
|
|
182
|
+
return () => listeners.get(name)?.delete(fn);
|
|
183
|
+
},
|
|
184
|
+
start,
|
|
185
|
+
started: () => started,
|
|
186
|
+
catchUp,
|
|
187
|
+
connection: () => state.connection,
|
|
188
|
+
hydration: () => state.hydration,
|
|
189
|
+
cursor: () => state.cursor,
|
|
190
|
+
usesEventSource: () => sse !== null,
|
|
191
|
+
/** For tests and page unload: nothing is emitted after this. */
|
|
192
|
+
close() {
|
|
193
|
+
closed = true;
|
|
194
|
+
held = null;
|
|
195
|
+
stopPolling();
|
|
196
|
+
env.clearTimeout(silentTimer); env.clearTimeout(errorTimer); env.clearTimeout(retryTimer);
|
|
197
|
+
if (sse) sse.close();
|
|
198
|
+
},
|
|
199
|
+
_pollOnce: pollOnce,
|
|
200
|
+
};
|
|
201
|
+
}
|