diff --git a/.changeset/chat-transport-session-renew-plus-preload.md b/.changeset/chat-transport-session-renew-plus-preload.md new file mode 100644 index 000000000..70ac843ce --- /dev/null +++ b/.changeset/chat-transport-session-renew-plus-preload.md @@ -0,0 +1,8 @@ +--- +"@trigger.dev/sdk": patch +--- + +`TriggerChatTransport` fixes for session-scoped auth and end-to-end UI smoke parity: + +- `RenewRunAccessTokenParams` now includes the durable `sessionId` alongside `chatId` + `runId`. Server-side renew handlers should mint the renewed PAT with `read:sessions:{sessionId}` + `write:sessions:{sessionId}` scopes (in addition to the existing run scopes) so it keeps authenticating against the session `.in` append + `.out` subscribe endpoints. Renewing without session scopes sends the transport into a 401 loop on the first append after expiry. +- `transport.preload(chatId)` on the `triggerTask` callback path no longer calls `apiClient.createSession` from the browser. The server action (e.g. `chat.createTriggerAction`) creates the session with its secret key and returns the `sessionId` in its result, matching how `sendMessages` already worked. Browser deployments that use the `triggerTask` callback path therefore no longer need `write:sessions` on any browser-side token. diff --git a/.claude/architecture/sessions-as-run-manager.md b/.claude/architecture/sessions-as-run-manager.md new file mode 100644 index 000000000..41f81720a --- /dev/null +++ b/.claude/architecture/sessions-as-run-manager.md @@ -0,0 +1,558 @@ +# Sessions as run manager + +Plan for the next chat.agent / Sessions branch. Builds on the row-agnostic +addressing branch (`chat-agent-sessions.md`). + +## Context + +The previous branch made `chatId` (the externalId) the universal addressing +string and made the `.in/.out/wait` routes row-agnostic. It works, but the +transport still owns run lifecycle: it triggers the first run, threads +`runId` through state, has to detect "run died" so the next user message +re-triggers, and re-triggers explicitly on `trigger:upgrade-required`. + +Two real gaps fall out of that: + +1. **Run-death blindness.** `.in/append` is run-independent — it appends to + S2 successfully whether or not the run is alive. The transport's + "non-auth error → re-trigger" fallback (`chat.ts:647-654`) is dead code + under row-agnostic addressing because the endpoint always 200s. If a run + is cancelled or crashes mid-turn before emitting `turn-complete`, the + user's next message sits in S2 with no listener and the transport has + no signal to recover. + +2. **Transport carries upgrade plumbing.** ~50 lines around + `subscribeToSessionStream`'s `upgradeRetry`, threaded `payload`+ + `messages` for re-trigger, and the `triggerNewRun` call on a + client-issued retry — all so the transport can react to a chunk the + agent emits. Server is in a better position to do this work. + +The fix: **make Session the run manager.** Sessions know their task, +their config, and their current run. Server triggers/re-triggers as +needed. Browser holds a session-scoped PAT and never sees runs. + +Nothing has shipped yet — no back-compat needed. We're free to break +public surface (`chat.createTriggerAction`, `onSessionChange` shape, +`ChatSession.runId`). + +## Design + +### Invariants + +- Session is the durable identity of a chat. One session, many runs over + its lifetime. +- Session always knows its task (`taskIdentifier`) and how to trigger it + (`triggerConfig`). Sessions without those fields don't exist anymore — + Sessions are task-bound by design. +- At most one live run per session at a time. Tracked as + `Session.currentRunId` (non-FK, can lag reality). +- `Session.currentRunVersion` (monotonic int) drives optimistic locking on + any state transition that swaps the run. +- Browser only ever holds session-scoped tokens. Run identifiers are a + server-side implementation detail. +- The append-time probe is the source of truth. Hooks from run-engine into + Session are optional eager-clears for dashboard freshness, never for + correctness. + +### State machine + +``` + ┌─────────────────┐ + │ Session created │ + │ first run fired │ + └────────┬────────┘ + ▼ + ┌──────────────────┐ user msg / .in append + │ currentRun alive │ ◀────────────────────────┐ + └────────┬─────────┘ │ + │ run terminates │ + │ (idle, cancel, crash, end-cont.) │ + ▼ │ + ┌──────────────────┐ .in/append probes │ + │ currentRun stale │ ─── ensureRunForSession ─┘ + └──────────────────┘ + │ session.close() + ▼ + ┌──────────────────┐ + │ closed (terminal)│ + └──────────────────┘ +``` + +### Three trigger paths + +1. **Session create.** `POST /api/v1/sessions` creates the row and triggers + the first run synchronously, returns `{ id, runId, publicAccessToken }`. +2. **`.in/append` probe.** Server checks `currentRunId`'s snapshot status; + if terminal, calls `ensureRunForSession` before processing the append. +3. **`end-and-continue`.** Agent calls `POST /api/v1/sessions/:id/end-and-continue` + to request a clean handoff to a fresh run on the latest version. Server + triggers v2, swaps `currentRunId`, returns the new runId. v1 emits its + final `.out` chunks (e.g. `trigger:upgrade-required` for transport + telemetry) and exits. + +## Schema + +### Prisma changes + +```prisma +model Session { + // existing fields stay... + + // Now required (today nullable). Sessions are task-bound. + taskIdentifier String + + // New: trigger payload + options for re-runs. + // { basePayload, machine, queue, tags, maxAttempts, idleTimeoutInSeconds } + triggerConfig Json + + // New: current run pointer. Non-FK so run deletion doesn't cascade. + currentRunId String? + + // New: monotonic counter for optimistic locking on currentRunId swaps. + currentRunVersion Int @default(0) + + @@index([currentRunId]) // only useful for "find session by run" reverse lookups +} +``` + +### Optional historical join (defer to v1.1) + +```prisma +model SessionRun { + sessionId String + runId String @unique + reason String // "initial" | "continuation" | "upgrade" | "manual" + triggeredAt DateTime @default(now()) + + @@index([sessionId]) +} +``` + +Not strictly needed for v1 — debugging/audit can use TaskRun's existing +metadata + `Session.currentRunId` history via `git`-style logs in +ClickHouse if desired. Add only if a concrete dashboard surface needs it. + +### Migration + +Two-step: + +1. Add the new columns + populate `taskIdentifier` from existing data + (chat.agent sessions all have it implicit via tags or metadata). +2. Set `triggerConfig = '{}'` for any existing sessions and either close + them or leave them as zombies. Since the old transport still works + pre-merge, this branch is the cutover. + +For the dev DB: I'll write a backfill that closes existing dev sessions +rather than try to compute valid triggerConfigs for them. They were all +test data anyway. + +## API surface + +### `POST /api/v1/sessions` — modified + +Two auth modes: + +| Mode | Caller | Required scope | Notes | +| ----------- | ----------------------- | ----------------------------- | -------------------------------------------------- | +| Secret key | Customer's server | env-wide | `chat.createStartSessionAction` server action | +| One-time JWT| Browser | `trigger:tasks:{taskId}` | Mints via `auth.createTriggerPublicToken(taskId)` | + +Body (Zod-validated): + +```ts +{ + type: string, // existing + externalId?: string, // chatId for chat.agent + taskIdentifier: string, // required; must match scope if JWT + triggerConfig: { + basePayload: Record, + machine?: MachinePresetName, + queue?: string, + tags?: string[], // ≤5 + maxAttempts?: number, + idleTimeoutInSeconds?: number, + }, + tags?: string[], // existing — session-level tags + metadata?: Record, // existing +} +``` + +Response: + +```ts +{ + id: string, // session_* + runId: string, // first run, freshly triggered + publicAccessToken: string, // session-scoped, long TTL + externalId: string | null, + type: string, + // ... rest of SessionItem fields +} +``` + +Behavior: + +- Idempotent on `(env, externalId)`. Repeat calls return the existing + session, ensure-running its run if terminal, return a fresh PAT. +- Token consumption: if JWT mode, the one-time token is consumed on first + successful call (existing replay-protection infra). +- PAT scopes returned: `read:sessions:{externalId} + write:sessions:{externalId}`. + No run-scoped permissions — the transport doesn't need them. + +### `POST /api/v1/sessions/:id/in/append` — modified + +Add the probe + ensure-run step before the existing S2 append. Pseudocode: + +```ts +const sess = await readSession(id); +if (sess.closedAt) return 400; +if (sess.expiresAt && sess.expiresAt < now) return 400; + +if (!sess.currentRunId || isTerminal(await getSnapshotStatus(sess.currentRunId))) { + await ensureRunForSession(sess); // see below +} + +return appendToS2(addressingKey, body); // unchanged +``` + +The probe is one Redis snapshot read (`getSnapshotStatus` is cheap, +already used by the run-engine). Net hot-path overhead: ~1ms. + +### `POST /api/v1/sessions/:id/end-and-continue` — new + +Called by the run itself (uses internal run auth, scoped to the +calling run's id + the session id). Triggers a fresh run for the same +session, atomically swaps `currentRunId`, returns the new runId. + +Body: + +```ts +{ + reason: "upgrade" | "explicit-handoff" | string, + // optional metadata for SessionRun.reason if/when we add the join table +} +``` + +Response: + +```ts +{ runId: string } +``` + +The calling run is expected to exit shortly after receiving the response — +it has done whatever wrap-up it wanted and is delegating the conversation +to the new run. The transport sees this as "more chunks arrive on `.out`, +some from v1 then some from v2" — it's the same S2 stream keyed on chatId. + +### Other routes — unchanged + +`GET /api/v1/sessions/:id`, `PATCH /api/v1/sessions/:id` (close, update), +`PUT /realtime/v1/sessions/:id/:io`, `GET /realtime/v1/sessions/:id/:io` +(SSE subscribe, including the row-agnostic addressing from the previous +branch) — all stay the same. + +## Server internals + +### `ensureRunForSession` — atomic re-run via optimistic locking + +Lives in a new service: `apps/webapp/app/services/realtime/sessionRunManager.server.ts`. + +```ts +async function ensureRunForSession( + sess: SessionRow, + reason: "initial" | "continuation" | "upgrade" | "manual" +): Promise<{ runId: string }> { + // 1. Trigger the run upfront. Cheap to cancel if we lose the race. + const newRun = await triggerTaskInternal(sess.taskIdentifier, sess.triggerConfig); + + // 2. Try to claim the slot. + const claimed = await prisma.session.updateMany({ + where: { + id: sess.id, + currentRunVersion: sess.currentRunVersion, + }, + data: { + currentRunId: newRun.id, + currentRunVersion: { increment: 1 }, + }, + }); + + if (claimed.count === 1) { + // Optionally record SessionRun history here. + return { runId: newRun.id }; + } + + // 3. Lost the race. Cancel ours, reuse whoever won. + cancelTaskRun(newRun.id).catch(() => {/* fire-and-forget */}); + const fresh = await readSession(sess.id); + if (fresh.currentRunId && !isTerminal(await getSnapshotStatus(fresh.currentRunId))) { + return { runId: fresh.currentRunId }; + } + + // 4. Pathological: winner's run died between win and our re-read. Recurse. + return ensureRunForSession(fresh, reason); +} +``` + +Key properties: +- No DB lock held across the trigger network call. +- Wasted-trigger window is small and bounded (multi-tab race on dead run, + ms apart). Cancel cost is negligible. +- Recursion only on pathological double-failure; bounded by run-engine's + own progress. + +### Run-engine eager-clear (optional, defer) + +A run-engine post-termination hook that nulls `Session.currentRunId` when +the terminal run matches. Purely a dashboard freshness concern. Skip in +v1 — append-time probe is the source of truth. + +## SDK changes + +### Transport (`packages/trigger-sdk/src/v3/chat.ts`) + +State collapses to: + +```ts +type ChatSessionState = { + publicAccessToken: string; // session-scoped, long TTL + lastEventId?: string; // for SSE resume + isStreaming?: boolean; // for reconnect-on-reload UX + skipToTurnComplete?: boolean; // for stop+resume UX +}; +``` + +Note: no `runId`, no `sessionId`. The chat is the chatId; the token is +session-scoped. + +Removed: +- `triggerTaskFn` callback option (constructor branch on it) +- `triggerNewRun()` method +- `renewRunPatForSession()` +- `renewRunAccessToken` callback option (token is session-scoped, doesn't + expire on run boundaries) +- `ensureSession()` (already removed in previous branch) +- The `trigger:upgrade-required` re-trigger handler in + `subscribeToSessionStream` (~50 lines) +- The `upgradeRetry: { payload, messages }` parameter threaded through + `sendMessages`, `preload`, `subscribeToSessionStream` +- The non-auth-error fallback in `sendMessages` (dead code, removed) + +Renamed/replaced: +- `chat.createTriggerAction` → `chat.createStartSessionAction` + - Calls `sessions.create({ taskIdentifier, externalId, triggerConfig })` + server-side with secret key + - Returns `{ publicAccessToken }` (no runId — invisible to browser) + +New methods: +- `transport.start(chatId, opts)` — for the browser-mediated path: + - Customer provides a `getStartToken(taskId)` callback that mints the + one-time JWT + - Transport calls `POST /sessions` with that token + - Receives session PAT, stores as state.publicAccessToken +- `transport.preload(chatId)` — same shape as `start` but with empty + basePayload override + +Method behavior changes: +- `sendMessages` — no trigger logic. Always `.in/append`. Server triggers + if needed. On 401/403, error out (token expired — customer's token + callback should provide fresh). +- `subscribeToSessionStream(chatId)` — pure passthrough on `.out`. Filters + `trigger:upgrade-required` for cleanliness (server handles the re-run + swap). Filters `trigger:turn-complete` as today. +- `stopGeneration` — `.in/append` with `{ kind: "stop" }`. Unchanged. +- `getSession(chatId)` — returns `{ publicAccessToken, lastEventId, isStreaming }`. + No id fields. + +### `chat-client.ts` (server-side AgentChat) + +Mirror the transport: state without `runId`/`sessionId`, no `triggerNewRun`, +constructor takes `{ chatId, publicAccessToken }` (or mints via secret +key). All `.in/append` and `.out` URLs use `chatId`. + +### `chat.agent` runtime (`packages/trigger-sdk/src/v3/ai.ts`) + +- Drop the fire-and-forget `sessions.create({ externalId: chatId })` at + bind. Session already exists by the time the agent boots — server + triggers via `ensureRunForSession` after creating the row. +- Keep `sessions.open(payload.chatId)` for helper resolution. No change. +- `chat.requestUpgrade()` plumbing: calls `POST /sessions/:id/end-and-continue` + with the run's internal auth. On success, emits `trigger:upgrade-required` + on `.out` for telemetry, exits cleanly. + +### Reference projects (`references/ai-chat`) + +- `actions.ts`: replace `chat.createTriggerAction` callsite with + `chat.createStartSessionAction` +- `chat-app.tsx`: pass the new `start` mode to `useTriggerChatTransport` +- `chat.tsx`: drop `runId` references +- `trigger/chat.ts`: no changes (chat.agent contract unchanged from + agent-author POV) + +## Auth model summary + +| Token | Scopes | Where minted | Lifetime | +| ----------------------------- | ------------------------------------------------------ | ----------------------------------------- | ----------- | +| Trigger-task one-shot | `trigger:tasks:{taskId}` | `auth.createTriggerPublicToken(taskId)` | One use | +| Session PAT | `read:sessions:{ext} + write:sessions:{ext}` | Issued by `POST /sessions` | 1h–24h | +| Run-internal PAT (chat.agent) | `read:runs:{run} + read:sessions:{ext} + …` | Server-side, never crosses to browser | Run-bounded | + +Browser holds at most a one-shot token (briefly) and a session PAT +(steady state). Never holds a run-scoped token. + +## Edge cases + +- **Concurrent multi-tab on dead run** — optimistic locking handles it, + loser cancels its triggered run. +- **Page refresh mid-stream** — `.out` SSE resumes via Last-Event-ID + (existing); session PAT survives because it's not run-scoped. +- **Run cancelled by user (dashboard)** — append-time probe sees terminal, + triggers new run on next message. +- **Idle exit** — same path; user comes back later, sends message, fresh + run boots. +- **Crash mid-turn (no `turn-complete` emitted)** — same path; persisted + store is pre-turn, fresh run reads `.in` from tail position, picks up + unanswered message. +- **Upgrade during user message** — optimistic locking in + `end-and-continue` ensures one wins. If user message wins, + `end-and-continue` returns conflict, agent v1 keeps running, processes + message, retries upgrade later. If upgrade wins, user message's append + probes fresh `currentRunId` (v2), uses it. +- **Session expiry mid-conversation** — `.in/append` and `end-and-continue` + reject after `expiresAt`. Existing run keeps running until idle, then + exits. Frontend sees a 400. +- **Concurrent `POST /sessions`** — unique constraint on + `(env, externalId)`, idempotent upsert returns existing row + ensure-runs. + +## Tests + +### Unit + +- `ensureRunForSession`: + - Happy path (no contention) + - Concurrent contention (two callers, one wins, loser reuses winner's + run) + - Pathological recursion (winner's run dies before loser re-reads) + - Trigger failure (caller's responsibility to surface) +- `POST /sessions` route: + - Idempotent upsert (same externalId → same row, fresh PAT) + - Auth: secret key path, JWT path with valid scope, JWT path with wrong + task scope (403), JWT replay (consumed token rejected) + - First run triggered, runId in response +- `POST /sessions/:id/in/append`: + - Probe path: alive run, terminal run, null currentRunId + - Probe + trigger: ensure new run before append + - Closed session 400 + - Expired session 400 +- `POST /sessions/:id/end-and-continue`: + - Auth: only callable from the current run + - Optimistic locking: stale currentRunId loses gracefully + +### Integration + +- chat.test.ts rewrite around the new transport surface (no `runId`, + no `triggerNewRun`) +- mock-chat-agent harness updates: install `__setSessionCreateImplForTests` + to also stub the first-run trigger (the create + trigger is now atomic + on the server, so the test harness needs to surface a fake runId) + +### Smoke (manual via Chrome DevTools) + +Same checklist as the previous branch's smoke test, plus: + +- Cancel run via dashboard → next user message triggers fresh run + automatically (no longer a gap) +- Deploy a new agent version mid-conversation → existing run requests + upgrade, exits, new run continues seamlessly (transport sees no + interruption beyond a possible extra TTFB) + +## Verification plan + +Per-package: + +``` +pnpm run typecheck --filter webapp # apps + internal pkgs +pnpm run typecheck --filter @internal/run-engine +pnpm run build --filter @trigger.dev/sdk # public package +pnpm run build --filter @trigger.dev/core # public package +pnpm run test --filter webapp -- sessionRunManager +pnpm run test --filter @trigger.dev/sdk -- chat +``` + +End-to-end via the playground: + +1. ai-chat (chat.agent) — basic send + reply +2. ai-chat-session (custom agent) — basic send + reply +3. ai-chat-raw — basic send + reply +4. ai-chat-hydrated — basic send + reply +5. Mid-stream reload — SSE reconnect +6. Stop + follow-up — same run handles next turn +7. Cancel run + send message → new run triggered automatically (the gap + from previous branch's S4 — must pass cleanly here) +8. Deploy new version + send message → in-flight conversation upgrades + transparently +9. Cross-form addressing curl matrix — unchanged from previous branch + +## Rollout + +- Single feature branch off `main` (or off the previous chat-agent-sessions + branch once that lands). +- No flag, no shim. Hard cutover. Pre-release SDK version. +- Reference projects updated in the same PR so the smoke test path works. + +## Open questions + +1. **Should `end-and-continue` accept a custom `triggerConfig` override?** + Use case: agent wants to swap to a different task identifier (rare). + Probably defer — keep it strictly "trigger another run with the same + config" for v1. +2. **Should `triggerConfig` pin the deploy version?** If a customer + redeploys with a chat.agent contract change, in-flight sessions might + have payloads incompatible with the new version. Probably defer — + chat.agent contract is stable; signature-breaking changes are rare and + warrant explicit handling. +3. **`SessionRun` join table**: yes/no/defer? Defer to v1.1 unless a + concrete dashboard surface needs it. +4. **`getSnapshotStatus` cost on hot path** — measure before optimizing. + Redis snapshot read should be sub-ms; if it isn't, cache for 1-2s + per session. + +## Out of scope + +- Session-level retry policies (separate feature) +- Multi-run-per-session (parallel agents on one chat) — explicit + non-goal; one currentRunId by design +- Cross-environment sessions (a session in dev, run in prod) — not + considered +- Public `Session.requestRun()` for callers other than the running + agent itself — defer until a use case appears +- Webhook notifications on run swap — defer + +## Effort estimate + +- Schema + migration: 0.5 day +- `ensureRunForSession` service + tests: 1.5 days +- `POST /sessions` auth modes + idempotent upsert + first-run trigger: 1 day +- `.in/append` probe: 0.5 day +- `end-and-continue` route + agent runtime wiring: 1 day +- Transport rewrite + tests: 2.5 days +- chat-client rewrite + tests: 1 day +- chat.agent runtime cleanup: 0.5 day +- `chat.createStartSessionAction` + browser path: 1 day +- Reference project migration: 0.5 day +- Smoke test + bug-fix buffer: 1.5 days + +**~11 days** focused work. Plus design doc review and any architectural +back-and-forth — call it 2 weeks calendar. + +## Implementation order + +1. Schema + migration (gives the new columns; everything else builds on this) +2. `ensureRunForSession` service + unit tests (the load-bearing primitive) +3. `POST /sessions` route changes (creates a session that actually has a run) +4. `.in/append` probe path (so the server can self-heal between runs) +5. `end-and-continue` route + chat.agent runtime call (upgrade flow) +6. Transport rewrite (depends on all the server pieces) +7. chat-client rewrite (mirrors transport; cheap once that's done) +8. `chat.createStartSessionAction` + reference project migration +9. Smoke test + final bug fixes diff --git a/.claude/docs-plans/sessions-as-run-manager-docs.md b/.claude/docs-plans/sessions-as-run-manager-docs.md new file mode 100644 index 000000000..4d8e858a8 --- /dev/null +++ b/.claude/docs-plans/sessions-as-run-manager-docs.md @@ -0,0 +1,366 @@ +# Docs update plan: Sessions-as-run-manager + +Companion to commits `7a48c1e6` (ai-chat) and `427541c2` (sessions server). Captures every doc page that needs to change, what's getting removed, and an upgrade guide for prerelease users. + +## Architectural summary (the diff readers should internalize) + +Pre-migration mental model: Sessions and chat.agent were two separate primitives. Sessions had its own create/list/close API; chat.agent rolled its own run-scoped streams. The two coexisted but didn't share machinery — chat.agent's wire path (run streams) was distinct from Sessions' wire path (`.in` / `.out` channels). + +Post-migration mental model: **Sessions is the run manager.** A Session row is task-bound (`taskIdentifier` + `triggerConfig` are required), it owns its current run via `currentRunId` (optimistic-claim), and it tracks every run it ever triggered in a `SessionRun` audit table. chat.agent is now just a particular kind of task you bind a Session to. The standalone "create a Session, then trigger something against it" path is gone — `sessions.start({...})` atomically creates the row and triggers the first run. + +Wire-level, the transport now talks to one set of routes (`/realtime/v1/sessions/:s/...` and `/api/v1/sessions/:s/...`); the per-run-stream code path is dead for chat. + +## Standalone Sessions docs: REMOVE + +`docs/sessions/` was written for the standalone-Session model. With sessions now task-bound, every page in that directory is incorrect: + +- `sessions/overview.mdx` — describes a generic session-as-bidirectional-channel primitive. Standalone create/list/close as the entry point. +- `sessions/quick-start.mdx` — `sessions.create({type, externalId})` then trigger something. Pattern no longer exists. +- `sessions/channels.mdx` — `.in` / `.out` documented from the standalone-session perspective. +- `sessions/reference.mdx` — API surface for the standalone primitive. + +**Action:** +1. Delete all four files: `docs/sessions/{overview,quick-start,channels,reference}.mdx`. +2. Remove the entire `Sessions` group from `docs/docs.json` under the `AI` group: + ```json + { + "group": "Sessions", + "pages": ["sessions/overview", "sessions/quick-start", "sessions/channels", "sessions/reference"] + } + ``` +3. Don't redirect — the URLs were never widely shared (this was alpha-tier surface). If we add Sessions docs back later, we can decide redirect-vs-fresh-slug then. + +We'll re-introduce Sessions docs once the primitive is stable and we have a non-chat.agent customer flow to document. + +## ai-chat docs: UPDATE + +Pages listed in the order they appear in `docs.json`. Each entry calls out the specific stale claims and what to replace. + +### `ai-chat/overview.mdx` +- Replace any line that says chat.agent runs on per-run streams or that the transport mints run-scoped tokens. +- Add one paragraph on the underlying primitive: chat.agent is bound to a Session that owns its runs. Customer-facing surface unchanged. +- If there's a "how it works" diagram, update arrows: browser → server action → `chat.createStartSessionAction` → Session row + first run + session PAT → browser → `.in/append` + `.out` SSE. + +### `ai-chat/changelog.mdx` +- Add an entry for the migration: "Sessions-as-run-manager — chat.agent now runs on top of a durable Session row that owns its runs. Public surface unchanged. See upgrade guide." + +### `ai-chat/quick-start.mdx` +- The transport snippet is the highest-value example in the docs. It must show the new shape: + ```ts + const transport = useTriggerChatTransport({ + task: "my-agent", + accessToken: ({ chatId }) => mintAccessToken(chatId), + startSession: ({ chatId, taskId, clientData }) => + startChatSession({ chatId, taskId, clientData }), + }); + ``` +- Server actions page should show `chat.createStartSessionAction("my-agent")` and `auth.createPublicToken({scopes: {sessions: chatId}})`. +- Drop any mention of `getStartToken` and `auth.createTriggerPublicToken` for the chat path. + +### `ai-chat/backend.mdx` +- The `chat.agent({...})` shape itself is unchanged — leave the `run`, `onPreload`, `onTurnStart`, `onTurnComplete` callbacks alone. +- Add a section on `chat.createStartSessionAction(taskId, options?)`. This is the canonical server-side entry point now. Show: + - Default `triggerConfig.basePayload`: `{messages: [], trigger: "preload"}` baked in. Customer overrides via `options.triggerConfig`. + - Idempotent on `(env, externalId)`. Concurrent calls for the same chatId converge. + - Returns `{sessionId, runId, publicAccessToken}`. +- Update `chat.requestUpgrade()` description: it now calls `endAndContinueSession` server-side, which atomically swaps `Session.currentRunId` to a new run. Browser keeps streaming across the swap. + +### `ai-chat/frontend.mdx` +- This is where most of the transport API lives. Rewrite around the two callbacks: + - `accessToken: ({chatId}) => string` — pure refresh, called on 401/403. + - `startSession?: ({chatId, taskId, clientData}) => {publicAccessToken}` — wraps the customer's server action, called on `transport.preload(chatId)` and lazy first `sendMessage`. +- Show the typed `clientData` flow: `useTriggerChatTransport` infers `clientData` from `withClientData`, threads it into `startSession`'s params, and merges into per-turn `metadata`. +- Drop `getStartToken` documentation entirely. +- `transport.preload(chatId)` no longer takes per-call options. If the customer needs dynamic per-call config they capture it in their server action via closure (typically over a ref for live values like the playground's `clientDataJsonRef`). +- Persistable `ChatSession`: `{publicAccessToken, lastEventId?}`. `runId` is gone. + +### `ai-chat/server-chat.mdx` +- `AgentChat` (server-side chat client) — same shape, but the `session` prop now takes `{lastEventId?}` only. +- `onTriggered({runId, chatId})` callback is still useful for telemetry / dashboard linking — the `runId` is the *current* run, not the only run. Note that across turns the runId may change (continuation runs after idle, upgrade runs, etc.). + +### `ai-chat/types.mdx` +- `ChatSession` — drop `runId`, drop `sessionId`. Just `{publicAccessToken, lastEventId?}`. +- `StartSessionParams`, `StartSessionResult` — new public types. +- `AccessTokenParams` — narrowed to `{chatId}` only (no metadata threading). +- Remove `GetStartTokenParams` from the type table. + +### `ai-chat/features.mdx` +- Audit for any mention of run-scoped streams, `CHAT_STREAM_KEY`, `CHAT_MESSAGES_STREAM_ID`, `CHAT_STOP_STREAM_ID`. All gone. +- Add: cross-form addressing on the wire (a session-scoped JWT minted for either `externalId` or `friendlyId` form authorizes either URL form). +- Add: SessionRun audit log — every run a chat session has triggered is recorded, queryable via the dashboard. + +### `ai-chat/compaction.mdx` +- Should be untouched (compaction lives inside `chat.agent`'s turn loop, doesn't depend on the wire model). + +### `ai-chat/pending-messages.mdx` +- Should be untouched (steering messages flow through `.in.append` regardless). + +### `ai-chat/background-injection.mdx` +- Same — injection happens inside the run, the run's wire path swap doesn't affect it. + +### `ai-chat/error-handling.mdx` +- Add: errors from `startSession` callback. The customer's server action can fail (auth check, DB write). Surface via `onSessionChange(chatId, null)` or via the customer's own try/catch in their callback. +- Replace any 401/403 retry logic that mentions `getStartToken` — it's `accessToken` now. + +### `ai-chat/mcp.mdx` +- Audit for `getStartToken` mentions in MCP tool examples. + +### `ai-chat/testing.mdx` +- The `mock-chat-agent` test harness moved to `setupSessionStartImplForTests` / similar — verify and update examples. +- Show how to mock `startSession` in unit tests (it's a fetch-mock or vi.fn returning `{publicAccessToken}`). + +### `ai-chat/client-protocol.mdx` +- The wire-level protocol page. Replace any `/realtime/v1/streams/{runId}/chat` URLs with `/realtime/v1/sessions/{chatId}/{io}`. +- Document the chunk shape on `.in`: tagged union — `{kind: "message", payload}` for user turns, `{kind: "stop"}` for stop signals, `{kind: "action", name, payload}` for typed actions. +- Document `.out` chunks: `UIMessageChunk`s interleaved with `trigger:turn-complete`, `trigger:upgrade-required` control markers. +- Cross-form addressing on session-scoped PATs. + +### `ai-chat/reference.mdx` +- Public API surface tables. `TriggerChatTransportOptions` — drop `getStartToken`, `triggerConfig`, `triggerOptions`; add `startSession`. +- `chat.createStartSessionAction(taskId, options?)` — full signature. +- `chat.requestUpgrade()` — keep, but note the new server-orchestrated swap behaviour. + +### `ai-chat/patterns/version-upgrades.mdx` +- This page is essentially about `chat.requestUpgrade()`. Update to explain the new mechanism: + - Old: agent emitted `trigger:upgrade-required` chunk, transport consumed it, transport triggered a new run from the browser side. + - New: agent calls `endAndContinueSession` (server-to-server), webapp atomically swaps `Session.currentRunId` to a freshly-triggered run, transport's existing SSE keeps streaming on the same session — no transport-side swap. +- Add: `SessionRun` audit row with `reason: "upgrade"`. + +### `ai-chat/patterns/sub-agents.mdx` +- Audit for any session.create / sub-agent-as-session-creator patterns. Sub-agents now get their session via the parent's task trigger (or by calling `sessions.start({ ... })` themselves with a different taskIdentifier). + +### `ai-chat/patterns/database-persistence.mdx` +- The reference app's `ChatSession` schema is now simpler: `{id, publicAccessToken, lastEventId?}`. Drop `runId`/`sessionId` columns from any example schemas. +- The persistence pattern itself is unchanged: persist the PAT + lastEventId, hydrate on page load via `sessions: { [chatId]: ... }` on the transport. + +### `ai-chat/patterns/branching-conversations.mdx` +- Should be mostly unchanged. Branching is a customer-side concern (multiple chatIds, each one its own session). + +### `ai-chat/patterns/code-sandbox.mdx` +- Audit for stale references. Probably fine. + +### `ai-chat/patterns/human-in-the-loop.mdx` +- Should be unchanged. + +### `ai-chat/patterns/skills.mdx` +- Should be unchanged. + +## NEW page: upgrade guide for chat.agent prerelease users + +Filename: `docs/ai-chat/upgrade-guide.mdx` (or `migration-from-prerelease.mdx` — pick whichever fits the docs style). Add to `docs.json` near the top of the AI Chat group, between `overview` and `quick-start`. + +Contents: + +```mdx +--- +title: "Upgrade guide: prerelease → Sessions-as-run-manager" +description: "Migrating chat.agent code from the prerelease API to the Sessions-as-run-manager release." +--- + +# Upgrade guide + +This guide is for customers who tried `chat.agent` during the prerelease period +(any `@trigger.dev/sdk` build before vX.Y.Z). The public surface is largely +unchanged — `chat.agent({...})`, `useTriggerChatTransport`, `chat.store` / +`chat.defer` / `chat.history`, `AgentChat` — but the transport callbacks and a +few server-side helpers were renamed. + +## TL;DR + +- **`getStartToken` is gone.** Replace with `startSession`, a server-action + callback that returns `{publicAccessToken}`. +- **`chat.createStartSessionAction(taskId, options?)` is the canonical + server-side entry point.** Replaces ad-hoc `auth.createTriggerPublicToken` + + manual session create. +- **`ChatSession` persistable shape changed.** Drop the `runId` field; + store only `{publicAccessToken, lastEventId?}`. +- **`transport.preload(chatId)` no longer takes per-call options.** + Trigger config (machine, idleTimeoutInSeconds, tags) lives server-side in + `chat.createStartSessionAction(taskId, options)`. +- **Wire URLs changed.** Anything that hit + `/realtime/v1/streams/{runId}/chat` directly should use + `/realtime/v1/sessions/{chatId}/out` (subscribe) or + `/realtime/v1/sessions/{chatId}/in/append` (send). + +## Transport: replace `getStartToken` with `startSession` + +### Before + +```ts +const transport = useTriggerChatTransport({ + task: "my-agent", + accessToken: async ({ chatId }) => mintToken(chatId), + getStartToken: async ({ taskId }) => mintTriggerToken(taskId), + triggerConfig: { basePayload: { /* ... */ } }, + triggerOptions: { tags: [...], machine: "small-1x" }, +}); +``` + +The browser called `auth.createTriggerPublicToken(taskId)` server-side to get +a one-shot trigger JWT, then `POST /api/v1/sessions` from the browser. + +### After + +```ts +const transport = useTriggerChatTransport({ + task: "my-agent", + accessToken: ({ chatId }) => mintAccessToken(chatId), + startSession: ({ chatId, taskId, clientData }) => + startChatSession({ chatId, taskId, clientData }), +}); +``` + +Where `startChatSession` is a server action wrapping +`chat.createStartSessionAction`: + +```ts +"use server"; +import { chat } from "@trigger.dev/sdk/ai"; + +export const startChatSession = chat.createStartSessionAction("my-agent", { + triggerConfig: { + machine: "small-1x", + tags: ["my-tag"], + }, +}); +``` + +The browser never holds a `trigger:tasks:{taskId}` JWT now. All session +creation goes through the customer's server, where authorization decisions +live alongside the customer's own DB writes. + +## Server actions: replace ad-hoc helpers with `chat.createStartSessionAction` + +### Before + +```ts +"use server"; +import { auth, sessions } from "@trigger.dev/sdk"; + +export async function startChatSession({ chatId, taskId }) { + const session = await sessions.create({ + type: "chat.agent", + externalId: chatId, + }); + // ... separately trigger the agent task ... + const publicAccessToken = await auth.createPublicToken({ + scopes: { read: { sessions: chatId }, write: { sessions: chatId } }, + }); + return { publicAccessToken }; +} +``` + +### After + +```ts +"use server"; +import { chat } from "@trigger.dev/sdk/ai"; + +export const startChatSession = chat.createStartSessionAction("my-agent"); +``` + +The new helper handles session creation + first-run trigger + PAT mint +atomically. It's idempotent on `(env, externalId)` — concurrent calls for the +same `chatId` converge to the same session. + +## `ChatSession` shape: drop `runId` + +Persistable session state is now just the PAT + last event ID: + +```ts +// before +type ChatSession = { runId: string; publicAccessToken: string; lastEventId?: string }; + +// after +type ChatSession = { publicAccessToken: string; lastEventId?: string }; +``` + +If your DB schema has a `runId` column on a session-state table, drop it (or +keep it for telemetry — but the transport doesn't read it). The current run +ID is server-side state on the Session row; the transport doesn't need to +know it. + +## `clientData`: typed and threaded automatically + +If your agent uses `chat.agent(...).withClientData({schema})`, the transport +infers the `clientData` type from `useTriggerChatTransport` +and threads it through `startSession`'s params. Set it once on the +transport: + +```ts +useTriggerChatTransport({ + // ... + clientData: { userId: currentUser.id, plan: currentUser.plan }, +}); +``` + +The same value also merges into per-turn `metadata` on the wire, and your +`startSession` callback receives it as `params.clientData`. Pass through to +`chat.createStartSessionAction` via `triggerConfig.basePayload.metadata` and +the agent's first run sees it in `payload.metadata`. + +## `chat.requestUpgrade()`: server-orchestrated now + +The behaviour didn't change from the customer's perspective — call +`chat.requestUpgrade()` inside `onTurnStart` / `onValidateMessages` and the +current run will exit so the next message starts on the latest version. + +What changed under the hood: + +- **Before:** the agent emitted a `trigger:upgrade-required` chunk on + `.out`, the transport consumed it browser-side and triggered a new run. +- **After:** the agent calls `endAndContinueSession` server-to-server, the + webapp triggers a new run and atomically swaps `Session.currentRunId`, + the browser's existing SSE subscription keeps receiving chunks across + the swap. Faster handoff, no browser-side bookkeeping. + +The `SessionRun` audit table records every run, including upgrade-driven +ones (with `reason: "upgrade"`). + +## Going to URLs directly? + +Anyone hitting raw URLs (instead of going through the SDK) should switch: + +| Before | After | +|---|---| +| `/realtime/v1/streams/{runId}/chat` (subscribe) | `/realtime/v1/sessions/{chatId}/out` | +| `/realtime/v1/streams/{runId}/{target}/chat-messages/append` | `/realtime/v1/sessions/{chatId}/in/append` (`{kind: "message", payload}` body) | +| `/realtime/v1/streams/{runId}/{target}/chat-stop/append` | `/realtime/v1/sessions/{chatId}/in/append` (`{kind: "stop"}` body) | + +The session-scoped PAT (`read:sessions:{chatId} + write:sessions:{chatId}`) +authorizes both the `externalId` form (e.g. `/sessions/my-chat-id/out`) +and the `friendlyId` form (e.g. `/sessions/session_abc.../out`). + +## Things that didn't change + +- `chat.agent({...})` definition shape and all callbacks. +- `chat.store` / `chat.defer` / `chat.history` APIs. +- `AgentChat` (server-side chat client) — same constructor, same methods. +- `useTriggerChatTransport`'s React semantics (created once, kept in a ref, + callbacks updated via `setOnSessionChange` / `setClientData` under the hood). +- Multi-tab coordination, pending-messages / steering, background injection. +- Per-turn `metadata` flowing through `sendMessage({ text }, { metadata })`. +``` + +## Other doc surfaces touched + +- `docs/ai/prompts.mdx` — only mentions `chat.agent` in passing. Audit but probably no change. +- `docs/realtime/backend/streams.mdx`, `docs/realtime/backend/input-streams.mdx` — these are the older streams API docs. Verify they don't reference `CHAT_STREAM_KEY` or `CHAT_MESSAGES_STREAM_ID` (those constants were removed). +- `docs/mcp-tools.mdx` — likely mentions the chat MCP tools. Audit for `getStartToken`-shaped examples. +- `docs/guides/example-projects/anchor-browser-web-scraper.mdx` — example project. Likely uses `chat.agent`. Audit. +- `docs/tasks/schemaTask.mdx` — only matched on the term "session" probably. Audit. + +## Update sequence + +Suggested order to minimise stale-state windows for readers: + +1. **Add the upgrade guide** (`ai-chat/upgrade-guide.mdx`) and its nav entry. This is the most-needed doc and stands alone from the rest. +2. **Update transport-shape pages** in this order: `quick-start` → `frontend` → `backend` → `server-chat` → `types` → `reference`. They all show the same callback shape; readers cross-reference between them, so they should ship together. +3. **Update peripheral pages**: `overview`, `changelog`, `client-protocol`, `error-handling`, `testing`, `features`, patterns. +4. **Remove `docs/sessions/`** + nav group last. Until step 2 lands the standalone Sessions docs are still less misleading than half-stale chat.agent docs. + +## Out of scope for this pass + +- Re-adding standalone Sessions docs (deferred until the primitive is stable for non-chat use). +- Diagrams / illustrations — text-first pass; designer can layer visuals after. +- Sample customer projects — the `references/ai-chat` reference repo is the in-source example; if marketing wants a polished standalone sample, that's a separate effort. diff --git a/.claude/review-guides/chat-agent-sessions-row-agnostic.md b/.claude/review-guides/chat-agent-sessions-row-agnostic.md new file mode 100644 index 000000000..7fb9851f3 --- /dev/null +++ b/.claude/review-guides/chat-agent-sessions-row-agnostic.md @@ -0,0 +1,287 @@ +# Review guide — chat.agent on Sessions, row-agnostic addressing + +Scope: the 12 uncommitted files. **No new behaviour beyond the public surface +already on this branch** — this is plumbing cleanup that: + +1. Eliminates the transport's session-creation step +2. Makes `chatId` the universal addressing string everywhere +3. Makes the server-side stream/append/wait routes row-agnostic + +## The two design moves + +**Move 1 — agent owns session lifecycle.** `chat.agent` and +`chat.customAgent` upsert the backing `Session` row at bind, fire-and-forget, +keyed on `externalId = payload.chatId`. The transport, server-side +`AgentChat`, and `chat.createTriggerAction` no longer create sessions at all. +Browsers cannot mint sessions either (`POST /api/v1/sessions` is now +secret-key-only). One owner, one path. + +**Move 2 — `chatId` is the only address.** The transport, server-side +`AgentChat`, JWT scopes, and S2 stream paths all use `chatId` directly. The +Session's friendlyId is informational. To make this safe, the three stream +routes (`.in/.out` PUT, GET, POST append, plus the run-engine `wait` +endpoint) became "row-optional" and derive a *canonical addressing key* +(`row.externalId ?? row.friendlyId`, fallback to the URL param when the row +hasn't been upserted yet). Same canonical key is used to build the S2 stream +path, the waitpoint cache key, and the JWT resource set — so any caller +addressing by either form converges on the same physical stream. + +Together these remove an entire class of "did the row land yet?" races. The +transport can subscribe to `/sessions/{chatId}/out` before the agent boots, +the agent's `void sessions.create({externalId: chatId})` lands a moment +later, and any earlier reads/writes are already on the right S2 key. + +--- + +## Read in this order + +### 1. `apps/webapp/app/services/realtime/sessions.server.ts` (+34 lines) + +The new primitive. Two helpers: + +- `isSessionFriendlyIdForm(value)` — `value.startsWith("session_")`. Used to + decide whether a missing row is a hard 404 (opaque friendlyId) or a soft + "row will land later" (externalId form). +- `canonicalSessionAddressingKey(row, paramSession)` — `row.externalId ?? + row.friendlyId` if the row exists, else `paramSession`. **This is the load- + bearing function.** Read its docstring. + +**Question to ask:** can two callers addressing the "same" session ever get +different canonical keys? Only if the row exists for one and not the other, +*and* the URL forms differ — but in that case the row-less caller used the +externalId form (friendlyId-form would have 404'd earlier), and the row-ful +caller computes `row.externalId ?? row.friendlyId`. If the row's externalId +matches the URL, they converge. If it doesn't, there's no row to find by +that string anyway. The interesting edge is "row exists with no externalId", +addressed via friendlyId — both sides read `row.friendlyId`. ✓ + +### 2. `apps/webapp/app/routes/realtime.v1.sessions.$session.$io.ts` (+47/-12) + +PUT initialize + GET subscribe (SSE). Both use the helper. The interesting +part is the loader's `findResource` + `authorization.resource`: + +```ts +findResource: async (params, auth) => { + const row = await resolveSessionByIdOrExternalId(...); + if (!row && isSessionFriendlyIdForm(params.session)) return undefined; // 404 + return { row, addressingKey: canonicalSessionAddressingKey(row, params.session) }; +}, +authorization: { + resource: ({ row, addressingKey }) => { + const ids = new Set([addressingKey]); + if (row) { + ids.add(row.friendlyId); + if (row.externalId) ids.add(row.externalId); + } + return { sessions: [...ids] }; + }, + superScopes: ["read:sessions", "read:all", "admin"], +}, +``` + +**Why three IDs in the resource set?** `checkAuthorization` is "any-match" +across the resource values. We want a JWT scoped to *either* form to +authorize *either* URL form. Smoke test verified the 4-cell matrix passes. + +**The PUT path** (action handler) is simpler — it just resolves the row, +builds an addressing key, and hands it to `initializeSessionStream`. Worth +noting the `closedAt` check is now `maybeSession?.closedAt` — no row means +no closedAt to enforce. + +### 3. `apps/webapp/app/routes/realtime.v1.sessions.$session.$io.append.ts` (+22/-13) + +POST append (browser writes a record to `.in` or server writes to `.out`). +Same row-optional pattern. Both the S2 append and the waitpoint drain use +`addressingKey`. + +**Question to ask:** what fires the waitpoint? An agent's +`session.in.wait()` registers a waitpoint keyed on `(addressingKey, io)` via +the wait endpoint (file 4). The append handler drains by the *same* key — +even if the agent registered with externalId form and the transport +appended via friendlyId form, both compute the same canonical key, so they +converge. ✓ + +### 4. `apps/webapp/app/routes/api.v1.runs.$runFriendlyId.session-streams.wait.ts` (+18/-13) + +The agent's `.in.wait()` endpoint. Run-engine creates the waitpoint, then +registers it in Redis under `(addressingKey, io)`. The race-check that runs +right after creation reads from S2 by the same key. Three call sites — +`addSessionStreamWaitpoint`, `readSessionStreamRecords`, +`removeSessionStreamWaitpoint` — all consistent. + +### 5. `apps/webapp/app/routes/api.v1.sessions.ts` (+4/-2) + +**Security tightening.** Removed `allowJWT: true` and `corsStrategy: "all"` +from the `POST /api/v1/sessions` action — secret-key only now. + +**Question to ask:** was the JWT path actually used? Until this branch, the +transport called it via `ensureSession` (now deleted). After this branch, +nobody reaches it from the browser. `chat.createTriggerAction` (server +secret key) is the only browser-adjacent path. + +### 6. `packages/trigger-sdk/src/v3/ai.ts` (+62/-39) + +Two near-identical edits — one in `chatAgent`, one in `chatCustomAgent`. +Both bind on `payload.chatId` and fire-and-forget the upsert: + +```ts +locals.set(chatSessionHandleKey, sessions.open(payload.chatId)); +void sessions + .create({ type: "chat.agent", externalId: payload.chatId }) + .catch(() => { /* best effort */ }); +``` + +**Question to ask:** why `void`-and-`catch`? Awaiting the upsert would gate +the agent's bind on a network round-trip that doesn't unblock anything +user-visible — `.in/.out` routes are row-agnostic and the waitpoint cache +is keyed on the addressing string, not the row id. If the upsert genuinely +fails, the next bind retries the same idempotent call (`sessions.create` +upserts on `externalId`, so concurrent triggers on one chatId converge to +one row). The row matters for downstream metadata + listing, not for live +addressing. + +The PAT scope minting in `chatAgent` (two call sites — preload and +sendMessage) now uses `payload.chatId` for the `sessions:` resource. That +matches what the transport/AgentChat use as the JWT resource and what the +JWT's resource set in the loader includes. Cross-form addressing works +either way (smoke-tested), but using `chatId` keeps the chain tight. + +`createChatTriggerAction` is the most visibly trimmed: no pre-create, no +threading `sessionId` into payload, scope mint uses `chatId`. Return type +no longer carries `sessionId` — note `TriggerChatTaskResult.sessionId` was +already declared optional, so this isn't a public-API break. + +**Stale docstring to flag:** `chat.ts:59` and `chat.ts:112` still describe +PAT scopes as `read:sessions:{sessionId}` and +`write:sessions:{sessionId}`. Functionally either ID works (row lookup +canonicalises), but the doc text is now out of date — it should say +`{chatId}`. Worth a tidy-up before merge but not blocking. + +### 7. `packages/trigger-sdk/src/v3/chat.ts` (+63/-117) + +**The biggest mechanical edit.** Net -54 lines from deleting `ensureSession` +and untangling its callers. + +What disappeared: +- `private async ensureSession(chatId)` — gone +- The "lazy upsert from the browser if no triggerTask callback" branch in + `sendMessages` and `preload` — gone +- The "throw if neither path surfaced a sessionId" guard — gone +- All `state.sessionId` URL params replaced with `chatId` +- `subscribeToSessionStream`'s `chatId?` (optional) is now `chatId` (required) + +What stayed: +- `state.sessionId` in `ChatSessionState` — optional, informational +- The `restore from external storage` branch in the constructor still + hydrates `sessionId` if persisted, just doesn't *require* it +- `notifySessionChange` still surfaces `sessionId` if known + +**Question to ask:** does the transport ever still need the friendlyId? The +only place is the `onSessionChange` callback's payload (so consumers +persisting state can save it for later display). The transport itself never +puts it in a URL or a waitpoint key. + +The `sendMessages` path is worth re-reading: when state.runId is set, it +appends to `.in/append` and subscribes to `.out`. If the append fails with +a non-auth error, it falls through to triggering a new run (legacy "run is +dead" detection — unchanged from pre-Sessions, doesn't depend on +addressing). + +### 8. `packages/trigger-sdk/src/v3/chat-client.ts` (+34/-33) + +Server-side `AgentChat`. Mirrors the transport changes — every URL uses +`this.chatId`. `triggerNewRun` no longer pre-creates a session. `ChatSession` +and internal `SessionState` types now have optional `sessionId`. + +The shape of the diff is identical to the transport: delete the upsert, +swap addressing identifiers, optionalise the friendlyId. If you've read +`chat.ts` carefully, this one is mostly mechanical confirmation that both +client surfaces (browser transport + server-side AgentChat) speak the same +addressing protocol. + +### 9. Test infrastructure — `sessions.ts` (+18) + `mock-chat-agent.ts` (+25) + +`__setSessionCreateImplForTests` mirrors the existing +`__setSessionOpenImplForTests`. `mockChatAgent` installs a no-op create stub +returning a synthetic `CreatedSessionResponseBody` so the agent's bind-time +`void sessions.create(...)` doesn't try to hit a real API. Cleanup runs in +the same `.finally` as the open override. + +**Question to ask:** is the synthetic response shape correct? It mirrors +`CreatedSessionResponseBody` — `id`, `externalId`, `type`, `tags`, +`metadata`, `closedAt`, `closedReason`, `expiresAt`, `createdAt`, +`updatedAt`, `isCached`. Tests don't currently assert on this object, so +the bar is "doesn't crash + matches the type". Met. + +### 10. `packages/trigger-sdk/src/v3/chat.test.ts` (+13/-12) + +Three classes of test edits, all consequences: + +- Stream URL assertion: `chat-1` (the chatId) instead of + `session_streamurl` (the friendlyId) +- `renewRunAccessToken` callback: `sessionId: undefined` (was + `DEFAULT_SESSION_ID` because the mocked trigger doesn't surface it) +- Token resolve count: `1` (was `2` — second resolve was for `ensureSession`) +- One `onSessionChange` matchObject loses `sessionId` + +### 11. `apps/webapp/app/routes/_app.../playground/.../route.tsx` (1 line) + +`sessionId: string` → `sessionId?: string` in the playground sidebar prop +to track the transport type change. + +--- + +## Edge cases I checked, so you don't have to + +- **Cross-form JWT auth (curl matrix).** JWT scoped to externalId can call + externalId URL ✓ and friendlyId URL ✓. JWT scoped to friendlyId can call + externalId URL ✓ and friendlyId URL ✓. Smoke-tested. +- **Row materialises after subscribe.** Transport opens + `GET /sessions/{chatId}/out` before agent's bind upsert lands → 200 OK, + `addressingKey = chatId` (paramSession fallback). Once the row lands + with `externalId = chatId`, addressingKey resolves to the same value via + `row.externalId`. Same S2 key throughout. +- **Concurrent triggers on one chatId.** Two browser tabs trigger two runs + → two binds → two `sessions.create({externalId: chatId})` calls. Upsert + semantics: both return the same row. +- **Closed session enforcement.** Still enforced when a row exists. + `maybeSession?.closedAt` is null-safe; no row = no close-state to honour. +- **Agent run cancellation.** Frontend doesn't auto-detect — unchanged from + pre-Sessions; messages sit in S2 until the next trigger (the existing + run-PAT auth-error path is the only reaper). Out of scope for this branch. +- **Idle timeout in dev.** Runs stay `EXECUTING_WITH_WAITPOINTS` past the + configured idle because dev runs don't snapshot/restore; the in-process + idle clock advances locally without touching the row. Expected, not a + regression. + +## Things explicitly **not** in this branch + +- Run-state subscription on the transport side (the "run died, re-trigger + silently" UX gap) +- Session auto-close on agent exit (still client-driven by design) +- Any change to `Session` schema, `sessions.create` semantics, or + `chatAccessTokenTTL` +- Docstring updates for `read:sessions:{sessionId}` / `write:sessions:{sessionId}` + in `chat.ts:59` and `chat.ts:112` (functional but textually stale — + follow-up nit) + +--- + +## What I'd be ready to answer cold + +- Why fire-and-forget upsert (vs. `await`) in the agent's bind step +- Why the route's authorization resource set has three IDs (cross-form JWT + auth) +- Why `POST /api/v1/sessions` lost `allowJWT` (security tightening — no + caller needs it after the transport's `ensureSession` is gone) +- What converges two callers using different URL forms onto the same S2 + stream (`canonicalSessionAddressingKey`, identical computation on both + sides for any given row) +- What makes `sessions.create` race-safe under concurrent triggers + (`externalId` upsert) +- Why `state.sessionId` stayed on `ChatSessionState` at all (pure + informational, surfaced via `onSessionChange` for consumer persistence; + zero addressing role) +- Why the chat-client (server-side AgentChat) and chat (transport) edits + look near-identical (they implement the same client protocol against the + same row-agnostic routes) diff --git a/packages/trigger-sdk/src/v3/chat.test.ts b/packages/trigger-sdk/src/v3/chat.test.ts index 4c1dde06e..d7d445ef0 100644 --- a/packages/trigger-sdk/src/v3/chat.test.ts +++ b/packages/trigger-sdk/src/v3/chat.test.ts @@ -808,6 +808,7 @@ describe("TriggerChatTransport", () => { expect(renewSpy).toHaveBeenCalledWith({ chatId: "chat-renew-sse", runId: "run_renew_sse", + sessionId: DEFAULT_SESSION_ID, }); const patStreamCall = (global.fetch as ReturnType).mock.calls.find( @@ -893,6 +894,7 @@ describe("TriggerChatTransport", () => { expect(renewSpy).toHaveBeenCalledWith({ chatId: "chat-fail-renew", runId: "run_fail_renew", + sessionId: DEFAULT_SESSION_ID, }); }); @@ -975,6 +977,7 @@ describe("TriggerChatTransport", () => { expect(renewSpy).toHaveBeenCalledWith({ chatId: "chat-first", runId: "run_input_renew", + sessionId: DEFAULT_SESSION_ID, }); expect(inputCalls).toBe(2); }); diff --git a/packages/trigger-sdk/src/v3/chat.ts b/packages/trigger-sdk/src/v3/chat.ts index ef85d71e6..b010ad8f7 100644 --- a/packages/trigger-sdk/src/v3/chat.ts +++ b/packages/trigger-sdk/src/v3/chat.ts @@ -56,6 +56,15 @@ export type RenewRunAccessTokenParams = { chatId: string; /** The durable Trigger.dev run backing this chat session. */ runId: string; + /** + * The durable Session friendlyId backing this chat. Present whenever + * the transport has observed a session for the chat (i.e. after the + * first trigger). Servers should mint tokens with both `read:runs:{runId}` + * + `write:inputStreams:{runId}` AND `read:sessions:{sessionId}` + + * `write:sessions:{sessionId}` so the PAT covers the run's live input + * stream AND the Session's `.in` / `.out` channels. + */ + sessionId?: string; }; /** @@ -1158,7 +1167,16 @@ export class TriggerChatTransport implements ChatTransport { if (pending) return pending; const doPreload = async () => { - const state = await this.ensureSession(chatId); + // Matches sendMessages: on the `triggerTask` callback path, the + // server action (e.g. `chat.createTriggerAction`) creates the + // Session with its secret key and returns `sessionId` alongside + // the run PAT — the browser never needs `write:sessions` itself. + // On the direct `accessToken` path, do the lazy upsert here so + // `payload.sessionId` is populated before the run starts. + let state: ChatSessionState | undefined = this.sessions.get(chatId); + if (!state?.sessionId && !this.triggerTaskFn) { + state = await this.ensureSession(chatId); + } const mergedMetadata = this.defaultMetadata || options?.metadata @@ -1168,7 +1186,7 @@ export class TriggerChatTransport implements ChatTransport { const payload = { messages: [] as never[], chatId, - sessionId: state.sessionId, + ...(state?.sessionId ? { sessionId: state.sessionId } : {}), trigger: "preload" as const, metadata: mergedMetadata, ...(options?.idleTimeoutInSeconds !== undefined @@ -1176,12 +1194,28 @@ export class TriggerChatTransport implements ChatTransport { : {}), }; - const { runId, publicAccessToken } = await this.triggerNewRun(chatId, payload, "preload"); + const result = await this.triggerNewRun(chatId, payload, "preload"); - state.runId = runId; - state.publicAccessToken = publicAccessToken; - this.sessions.set(chatId, state); - this.notifySessionChange(chatId, state); + // The server action's result carries the `sessionId` it created + // for the triggerTask callback path; adopt it here. + const adoptedSessionId = result.sessionId ?? state?.sessionId; + if (!adoptedSessionId) { + // Neither path surfaced a sessionId — the server action is + // misconfigured. Keep the preload run but don't notify; the + // first sendMessage will recover via its own ensureSession. + return; + } + + const nextState: ChatSessionState = { + sessionId: adoptedSessionId, + runId: result.runId, + publicAccessToken: result.publicAccessToken, + lastEventId: state?.lastEventId, + isStreaming: state?.isStreaming, + skipToTurnComplete: state?.skipToTurnComplete, + }; + this.sessions.set(chatId, nextState); + this.notifySessionChange(chatId, nextState); }; const promise = doPreload().finally(() => { @@ -1279,7 +1313,8 @@ export class TriggerChatTransport implements ChatTransport { } try { - const token = await renew({ chatId, runId }); + const sessionId = this.sessions.get(chatId)?.sessionId; + const token = await renew({ chatId, runId, sessionId }); if (typeof token !== "string" || token.length === 0) { return undefined; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 40b1af9b6..9d2bf1c96 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2220,9 +2220,6 @@ importers: react-dom: specifier: ^19.0.0 version: 19.1.0(react@19.1.0) - secure-exec: - specifier: 0.1.0 - version: 0.1.0(bufferutil@4.0.9) serialize-error: specifier: ^11.0.3 version: 11.0.3 @@ -2271,7 +2268,7 @@ importers: version: 5.5.4 vitest: specifier: ^3.1.4 - version: 3.1.4(@types/debug@4.1.12)(@types/node@20.14.14)(lightningcss@1.29.2)(terser@5.44.1) + version: 3.1.4(@types/debug@4.1.12)(@types/node@20.14.14)(jiti@2.6.1)(lightningcss@1.29.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.3) references/bun-catalog: dependencies: @@ -4493,12 +4490,6 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.27.7': - resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/android-arm64@0.17.6': resolution: {integrity: sha512-YnYSCceN/dUzUr5kdtUzB+wZprCafuD89Hs0Aqv9QSdwhYQybhXTaSTcrl6X/aWThn1a/j0eEpUBGOE7269REg==} engines: {node: '>=12'} @@ -4535,12 +4526,6 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.27.7': - resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm@0.15.18': resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==} engines: {node: '>=12'} @@ -4583,12 +4568,6 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.27.7': - resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-x64@0.17.6': resolution: {integrity: sha512-MVcYcgSO7pfu/x34uX9u2QIZHmXAB7dEiLQC5bBl5Ryqtpj9lT2sg3gNDEsrPEmimSJW2FXIaxqSQ501YLDsZQ==} engines: {node: '>=12'} @@ -4625,12 +4604,6 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.27.7': - resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/darwin-arm64@0.17.6': resolution: {integrity: sha512-bsDRvlbKMQMt6Wl08nHtFz++yoZHsyTOxnjfB2Q95gato+Yi4WnRl13oC2/PJJA9yLCoRv9gqT/EYX0/zDsyMA==} engines: {node: '>=12'} @@ -4667,12 +4640,6 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.27.7': - resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-x64@0.17.6': resolution: {integrity: sha512-xh2A5oPrYRfMFz74QXIQTQo8uA+hYzGWJFoeTE8EvoZGHb+idyV4ATaukaUvnnxJiauhs/fPx3vYhU4wiGfosg==} engines: {node: '>=12'} @@ -4709,12 +4676,6 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.27.7': - resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/freebsd-arm64@0.17.6': resolution: {integrity: sha512-EnUwjRc1inT4ccZh4pB3v1cIhohE2S4YXlt1OvI7sw/+pD+dIE4smwekZlEPIwY6PhU6oDWwITrQQm5S2/iZgg==} engines: {node: '>=12'} @@ -4751,12 +4712,6 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.27.7': - resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-x64@0.17.6': resolution: {integrity: sha512-Uh3HLWGzH6FwpviUcLMKPCbZUAFzv67Wj5MTwK6jn89b576SR2IbEp+tqUHTr8DIl0iDmBAf51MVaP7pw6PY5Q==} engines: {node: '>=12'} @@ -4793,12 +4748,6 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.7': - resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/linux-arm64@0.17.6': resolution: {integrity: sha512-bUR58IFOMJX523aDVozswnlp5yry7+0cRLCXDsxnUeQYJik1DukMY+apBsLOZJblpH+K7ox7YrKrHmJoWqVR9w==} engines: {node: '>=12'} @@ -4835,12 +4784,6 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.27.7': - resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm@0.17.6': resolution: {integrity: sha512-7YdGiurNt7lqO0Bf/U9/arrPWPqdPqcV6JCZda4LZgEn+PTQ5SMEI4MGR52Bfn3+d6bNEGcWFzlIxiQdS48YUw==} engines: {node: '>=12'} @@ -4877,12 +4820,6 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.27.7': - resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-ia32@0.17.6': resolution: {integrity: sha512-ujp8uoQCM9FRcbDfkqECoARsLnLfCUhKARTP56TFPog8ie9JG83D5GVKjQ6yVrEVdMie1djH86fm98eY3quQkQ==} engines: {node: '>=12'} @@ -4919,12 +4856,6 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.27.7': - resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-loong64@0.15.18': resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==} engines: {node: '>=12'} @@ -4967,12 +4898,6 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.27.7': - resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-mips64el@0.17.6': resolution: {integrity: sha512-09AXKB1HDOzXD+j3FdXCiL/MWmZP0Ex9eR8DLMBVcHorrWJxWmY8Nms2Nm41iRM64WVx7bA/JVHMv081iP2kUA==} engines: {node: '>=12'} @@ -5009,12 +4934,6 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.27.7': - resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-ppc64@0.17.6': resolution: {integrity: sha512-AmLhMzkM8JuqTIOhxnX4ubh0XWJIznEynRnZAVdA2mMKE6FAfwT2TWKTwdqMG+qEaeyDPtfNoZRpJbD4ZBv0Tg==} engines: {node: '>=12'} @@ -5051,12 +4970,6 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.27.7': - resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-riscv64@0.17.6': resolution: {integrity: sha512-Y4Ri62PfavhLQhFbqucysHOmRamlTVK10zPWlqjNbj2XMea+BOs4w6ASKwQwAiqf9ZqcY9Ab7NOU4wIgpxwoSQ==} engines: {node: '>=12'} @@ -5093,12 +5006,6 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.27.7': - resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-s390x@0.17.6': resolution: {integrity: sha512-SPUiz4fDbnNEm3JSdUW8pBJ/vkop3M1YwZAVwvdwlFLoJwKEZ9L98l3tzeyMzq27CyepDQ3Qgoba44StgbiN5Q==} engines: {node: '>=12'} @@ -5135,12 +5042,6 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.27.7': - resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-x64@0.17.6': resolution: {integrity: sha512-a3yHLmOodHrzuNgdpB7peFGPx1iJ2x6m+uDvhP2CKdr2CwOaqEFMeSqYAHU7hG+RjCq8r2NFujcd/YsEsFgTGw==} engines: {node: '>=12'} @@ -5177,12 +5078,6 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.27.7': - resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/netbsd-arm64@0.24.2': resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} engines: {node: '>=18'} @@ -5195,12 +5090,6 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.27.7': - resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-x64@0.17.6': resolution: {integrity: sha512-EanJqcU/4uZIBreTrnbnre2DXgXSa+Gjap7ifRfllpmyAU7YMvaXmljdArptTHmjrkkKm9BK6GH5D5Yo+p6y5A==} engines: {node: '>=12'} @@ -5237,12 +5126,6 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.7': - resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/openbsd-arm64@0.23.0': resolution: {integrity: sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==} engines: {node: '>=18'} @@ -5261,12 +5144,6 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.27.7': - resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-x64@0.17.6': resolution: {integrity: sha512-xaxeSunhQRsTNGFanoOkkLtnmMn5QbA0qBhNet/XLVsc+OVkpIWPHcr3zTW2gxVU5YOHFbIHR9ODuaUdNza2Vw==} engines: {node: '>=12'} @@ -5303,18 +5180,6 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.7': - resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.27.7': - resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - '@esbuild/sunos-x64@0.17.6': resolution: {integrity: sha512-gnMnMPg5pfMkZvhHee21KbKdc6W3GR8/JuE0Da1kjwpK6oiFU3nqfHuVPgUX2rsOx9N2SadSQTIYV1CIjYG+xw==} engines: {node: '>=12'} @@ -5351,12 +5216,6 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.27.7': - resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/win32-arm64@0.17.6': resolution: {integrity: sha512-G95n7vP1UnGJPsVdKXllAJPtqjMvFYbN20e8RK8LVLhlTiSOH1sd7+Gt7rm70xiG+I5tM58nYgwWrLs6I1jHqg==} engines: {node: '>=12'} @@ -5393,12 +5252,6 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.27.7': - resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-ia32@0.17.6': resolution: {integrity: sha512-96yEFzLhq5bv9jJo5JhTs1gI+1cKQ83cUpyxHuGqXVwQtY5Eq54ZEsKs8veKtiKwlrNimtckHEkj4mRh4pPjsg==} engines: {node: '>=12'} @@ -5435,12 +5288,6 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.27.7': - resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-x64@0.17.6': resolution: {integrity: sha512-n6d8MOyUrNp6G4VSpRcgjs5xj4A91svJSaiwLIDWVWEsZtpN5FA9NlBbZHDmAJc2e8e6SF4tkBD3HAvPF+7igA==} engines: {node: '>=12'} @@ -5477,12 +5324,6 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.27.7': - resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@eslint-community/eslint-utils@4.4.0': resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -9886,18 +9727,6 @@ packages: '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - '@secure-exec/browser@0.1.0': - resolution: {integrity: sha512-nZRLB+ts0RqI5Rj2DBY8Cy87lrZQatGncjzEgSv5iaQcAVudcdcqy2VKBBB5VGZ87fPe79aAYplEVB4dG27WaQ==} - - '@secure-exec/core@0.1.0': - resolution: {integrity: sha512-d6eEiQkGeIgBQyaPxPXpWox3tqZYqRHTyIQVQi7p8Do55OtOAYtfTR8rU00djt16OK+GuWfCOWxSDRRUMGqIIg==} - - '@secure-exec/node@0.1.0': - resolution: {integrity: sha512-hAZIB3rOsCBK9MYQ4UTaXoccZGCRpjDYWoTuOWsOx73ehE5SPEKM3GUQy0nzioLHAXo7OxGPkLX/sTcegrEMCg==} - - '@secure-exec/python@0.1.0': - resolution: {integrity: sha512-BADZOZz98tRith0tyQNfuwEyc4dD2JWALaLp66X3mRdoID4IZRrEY+BYpDSYu46JFS2EzFsPgxynJ9uzX89gZQ==} - '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} @@ -12160,9 +11989,6 @@ packages: asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - asn1.js@4.10.1: - resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} - asn1@0.2.6: resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} @@ -12173,9 +11999,6 @@ packages: resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} - assert@2.1.0: - resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -12369,12 +12192,6 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - bn.js@4.12.3: - resolution: {integrity: sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==} - - bn.js@5.2.3: - resolution: {integrity: sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==} - body-parser@1.20.3: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -12413,35 +12230,9 @@ packages: breakword@1.0.5: resolution: {integrity: sha512-ex5W9DoOQ/LUEU3PMdLs9ua/CYZl1678NUkKOdUSi8Aw5F1idieaiRURCBFJCwVcrD1J8Iy3vfWSloaMwO2qFg==} - brorand@1.1.0: - resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} - - browser-resolve@2.0.0: - resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} - - browserify-aes@1.2.0: - resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} - - browserify-cipher@1.0.1: - resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} - - browserify-des@1.0.2: - resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} - - browserify-rsa@4.1.1: - resolution: {integrity: sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==} - engines: {node: '>= 0.10'} - - browserify-sign@4.2.5: - resolution: {integrity: sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==} - engines: {node: '>= 0.10'} - browserify-zlib@0.1.4: resolution: {integrity: sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==} - browserify-zlib@0.2.0: - resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} - browserslist@4.21.4: resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -12473,9 +12264,6 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer-xor@1.0.3: - resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} - buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -12490,9 +12278,6 @@ packages: resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==} engines: {node: '>=10.0.0'} - builtin-status-codes@3.0.0: - resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} - builtins@1.0.3: resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} @@ -12733,10 +12518,6 @@ packages: resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} engines: {node: '>=8'} - cipher-base@1.0.7: - resolution: {integrity: sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==} - engines: {node: '>= 0.10'} - citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} @@ -12940,12 +12721,6 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} - console-browserify@1.2.0: - resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} - - constants-browserify@1.0.0: - resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} - content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -13058,18 +12833,6 @@ packages: resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} engines: {node: '>= 14'} - create-ecdh@4.0.4: - resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} - - create-hash@1.2.0: - resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} - - create-hmac@1.1.7: - resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} - - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - crelt@1.0.6: resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} @@ -13106,10 +12869,6 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - crypto-browserify@3.12.1: - resolution: {integrity: sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==} - engines: {node: '>= 0.10'} - crypto-js@4.1.1: resolution: {integrity: sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==} @@ -13570,9 +13329,6 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - des.js@1.1.0: - resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} - destr@2.0.3: resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==} @@ -13623,9 +13379,6 @@ packages: resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} engines: {node: '>=0.3.1'} - diffie-hellman@5.0.3: - resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} - dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -13679,10 +13432,6 @@ packages: dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - domain-browser@4.22.0: - resolution: {integrity: sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==} - engines: {node: '>=10'} - domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} @@ -13796,9 +13545,6 @@ packages: electron-to-chromium@1.5.252: resolution: {integrity: sha512-53uTpjtRgS7gjIxZ4qCgFdNO2q+wJt/Z8+xAvxbCqXPJrY6h7ighUkadQmNMXH96crtpa6gPFNP7BF4UBGDuaA==} - elliptic@6.6.1: - resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} - emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -14073,11 +13819,6 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.27.7: - resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} - engines: {node: '>=18'} - hasBin: true - escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -14383,9 +14124,6 @@ packages: resolution: {integrity: sha512-fvIkb9qZzdMxgZrEQDyll+9oJsyaVvY92I2Re+qK0qEJ+w5s0X3dtz+M0VAPOjP1gtU3iqWyjQ0G3nvd5CLZ2g==} engines: {node: '>=20.0.0'} - evp_bytestokey@1.0.3: - resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} - evt@2.4.13: resolution: {integrity: sha512-haTVOsmjzk+28zpzvVwan9Zw2rLQF2izgi7BKjAPRzZAfcv+8scL0TpM8MzvGNKFYHiy+Bq3r6FYIIUPl9kt3A==} @@ -14654,10 +14392,6 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} @@ -15057,17 +14791,6 @@ packages: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} - hash-base@3.0.5: - resolution: {integrity: sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==} - engines: {node: '>= 0.10'} - - hash-base@3.1.2: - resolution: {integrity: sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==} - engines: {node: '>= 0.8'} - - hash.js@1.1.7: - resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} - hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -15127,9 +14850,6 @@ packages: highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - hmac-drbg@1.0.1: - resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} - hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} @@ -15194,9 +14914,6 @@ packages: http-status-codes@2.3.0: resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} - https-browserify@1.0.0: - resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} - https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -15485,10 +15202,6 @@ packages: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} - is-nan@1.3.2: - resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} - engines: {node: '>= 0.4'} - is-negative-zero@2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} @@ -15577,10 +15290,6 @@ packages: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} @@ -15624,14 +15333,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isolated-vm@6.1.2: - resolution: {integrity: sha512-GGfsHqtlZiiurZaxB/3kY7LLAXR3sgzDul0fom4cSyBjx6ZbjpTrFWiH3z/nUfLJGJ8PIq9LQmQFiAxu24+I7A==} - engines: {node: '>=22.0.0'} - - isomorphic-timers-promises@1.0.1: - resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} - engines: {node: '>=10'} - isomorphic-ws@5.0.0: resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} peerDependencies: @@ -16348,9 +16049,6 @@ packages: peerDependencies: react: 18.x - md5.js@1.3.5: - resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} - mdast-util-definitions@5.1.1: resolution: {integrity: sha512-rQ+Gv7mHttxHOBx2dkF4HWTg+EE+UR78ptQWDylzPKaQuVGdG4HIoY3SrS/pCp80nZ04greFvXbVFHT+uf0JVQ==} @@ -16660,10 +16358,6 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - miller-rabin@4.0.1: - resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} - hasBin: true - mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -16733,12 +16427,6 @@ packages: minimal-polyfills@2.2.3: resolution: {integrity: sha512-oxdmJ9cL+xV72h0xYxp4tP2d5/fTBpP45H8DIOn9pASuF8a3IYTf+25fMGDYGiWW+MFsuog6KD6nfmhZJQ+uUw==} - minimalistic-assert@1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - - minimalistic-crypto-utils@1.0.1: - resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - minimatch@10.0.1: resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} engines: {node: 20 || >=22} @@ -17159,10 +16847,6 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - node-stdlib-browser@1.3.1: - resolution: {integrity: sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw==} - engines: {node: '>=10'} - nodemailer@8.0.6: resolution: {integrity: sha512-Nm2XeuDwwy2wi5A+8jPWwQwNzcjNjhWdE3pVLoXEusxJqCnAPAgnBGkSmiLknbnWuOF9qraRpYZjfxqtKZ4tPw==} engines: {node: '>=6.0.0'} @@ -17273,10 +16957,6 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} - object-is@1.1.6: - resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} - engines: {node: '>= 0.4'} - object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -17427,9 +17107,6 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} - os-browserify@0.3.0: - resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} - os-paths@7.4.0: resolution: {integrity: sha512-Ux1J4NUqC6tZayBqLN1kUlDAEvLiQlli/53sSddU4IN+h+3xxnv2HmRSMpVSvr1hvJzotfMs3ERvETGK+f4OwA==} engines: {node: '>= 4.0'} @@ -17561,17 +17238,10 @@ packages: pako@0.2.9: resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} - pako@1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse-asn1@5.1.9: - resolution: {integrity: sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==} - engines: {node: '>= 0.10'} - parse-duration@2.1.4: resolution: {integrity: sha512-b98m6MsCh+akxfyoz9w9dt0AlH2dfYLOBss5SdDsr9pkhKNvkWBXU/r8A4ahmIGByBOLV2+4YwfCuFxbDDaGyg==} @@ -17621,9 +17291,6 @@ packages: partysocket@1.0.2: resolution: {integrity: sha512-rAFOUKImaq+VBk2B+2RTBsWEvlnarEP53nchoUHzpVs8V6fG2/estihOTslTQUWHVuHEKDL5k8htG8K3TngyFA==} - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - path-data-parser@0.1.0: resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} @@ -17691,10 +17358,6 @@ packages: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} - pbkdf2@3.1.5: - resolution: {integrity: sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==} - engines: {node: '>= 0.10'} - peberminta@0.9.0: resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} @@ -17858,10 +17521,6 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} - pkg-dir@5.0.0: - resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} - engines: {node: '>=10'} - pkg-types@1.1.3: resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} @@ -18338,9 +17997,6 @@ packages: psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - public-encrypt@4.0.3: - resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} - pump@2.0.1: resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==} @@ -18357,10 +18013,6 @@ packages: resolution: {integrity: sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==} engines: {node: '>=6'} - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - puppeteer-core@24.15.0: resolution: {integrity: sha512-2iy0iBeWbNyhgiCGd/wvGrDSo73emNFjSxYOcyAqYiagkYt5q4cPfVXaVDKBsukgc2fIIfLAalBZlaxldxdDYg==} engines: {node: '>=18'} @@ -18372,10 +18024,6 @@ packages: resolution: {integrity: sha512-BE5CROfVGsx2XIhxGuZAT7rTH9lLeQx/6M0P7DTXQH4IUc3BBzs9JUzt4yzGf3JrH9enkeq6YJBe9CTtkm1WmQ==} hasBin: true - pyodide@0.28.3: - resolution: {integrity: sha512-rtCsyTU55oNGpLzSVuAd55ZvruJDEX8o6keSdWKN9jPeBVSNlynaKFG7eRqkiIgU7i2M6HEgYtm0atCEQX3u4A==} - engines: {node: '>=18.0.0'} - qrcode.react@4.2.0: resolution: {integrity: sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==} peerDependencies: @@ -18388,10 +18036,6 @@ packages: quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - querystring-es3@0.2.1: - resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} - engines: {node: '>=0.4.x'} - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -18415,9 +18059,6 @@ packages: randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - randomfill@1.0.4: - resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} - range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -19024,10 +18665,6 @@ packages: engines: {node: 20 || >=22} hasBin: true - ripemd160@2.0.3: - resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} - engines: {node: '>= 0.8'} - robot3@0.4.1: resolution: {integrity: sha512-hzjy826lrxzx8eRgv80idkf8ua1JAepRc9Efdtj03N3KNJuznQCPlyCJ7gnUmDFwZCLQjxy567mQVKmdv2BsXQ==} @@ -19132,9 +18769,6 @@ packages: resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} engines: {node: '>=0.10.0'} - secure-exec@0.1.0: - resolution: {integrity: sha512-NTpdYqSJVmU2MowfZsW1NWrLpZufpvsbMO0rH060GNM9Aa9SqUPJdmpuX38UQiT+1kiop9ewxeP7l09XzsLEZA==} - secure-json-parse@2.7.0: resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} @@ -19227,17 +18861,9 @@ packages: resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==} engines: {node: '>=6.9'} - setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - sha.js@2.4.12: - resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} - engines: {node: '>= 0.10'} - hasBin: true - sharp@0.33.5: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -19542,16 +19168,10 @@ packages: std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} - stream-browserify@3.0.0: - resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} - stream-buffers@3.0.2: resolution: {integrity: sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==} engines: {node: '>= 0.10.0'} - stream-http@3.2.0: - resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} - stream-shift@1.0.3: resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} @@ -19946,9 +19566,6 @@ packages: text-decoder@1.2.0: resolution: {integrity: sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==} - text-encoding-utf-8@1.0.2: - resolution: {integrity: sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==} - text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -19973,10 +19590,6 @@ packages: through2@2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} - timers-browserify@2.0.12: - resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} - engines: {node: '>=0.6.0'} - tiny-case@1.0.3: resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} @@ -20061,10 +19674,6 @@ packages: resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} engines: {node: '>=14.14'} - to-buffer@1.2.2: - resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} - engines: {node: '>= 0.4'} - to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -20112,10 +19721,6 @@ packages: tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} - tr46@6.0.0: - resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} - engines: {node: '>=20'} - tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -20270,9 +19875,6 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - tty-browserify@0.0.1: - resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} - tty-table@4.1.6: resolution: {integrity: sha512-kRj5CBzOrakV4VRRY5kUWbNYvo/FpOsz65DzI5op9P+cHov3+IqPbo1JE1ZnQGkHdZgNFDsrEjrfqqy/Ply9fw==} engines: {node: '>=8.0.0'} @@ -20376,10 +19978,6 @@ packages: resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} engines: {node: '>= 0.4'} - typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} - engines: {node: '>= 0.4'} - typed-array-byte-length@1.0.1: resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} engines: {node: '>= 0.4'} @@ -20581,10 +20179,6 @@ packages: resolution: {integrity: sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==} engines: {node: '>=4'} - url@0.11.4: - resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} - engines: {node: '>= 0.4'} - urlpattern-polyfill@9.0.0: resolution: {integrity: sha512-WHN8KDQblxd32odxeIgo83rdVDE2bvdkb86it7bMhYZwWKJz0+O0RK/eZiHYnM+zgt/U7hAHOlCQGfjjvSkw2g==} @@ -20840,9 +20434,6 @@ packages: jsdom: optional: true - vm-browserify@1.1.2: - resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} - vscode-jsonrpc@8.2.0: resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} engines: {node: '>=14.0.0'} @@ -20907,10 +20498,6 @@ packages: webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - webidl-conversions@8.0.1: - resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} - engines: {node: '>=20'} - webpack-bundle-analyzer@4.10.1: resolution: {integrity: sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==} engines: {node: '>= 10.13.0'} @@ -20944,10 +20531,6 @@ packages: webpack-cli: optional: true - whatwg-url@15.1.0: - resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==} - engines: {node: '>=20'} - whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -20968,10 +20551,6 @@ packages: resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} engines: {node: '>= 0.4'} - which-typed-array@1.1.20: - resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} - engines: {node: '>= 0.4'} - which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -24187,7 +23766,7 @@ snapshots: '@epic-web/test-server@0.1.0(bufferutil@4.0.9)': dependencies: - '@hono/node-server': 1.12.2(hono@4.5.11) + '@hono/node-server': 1.12.2(hono@4.12.15) '@hono/node-ws': 1.0.4(@hono/node-server@1.12.2(hono@4.5.11))(bufferutil@4.0.9) '@open-draft/deferred-promise': 2.2.0 '@types/ws': 8.5.12 @@ -24224,9 +23803,6 @@ snapshots: '@esbuild/aix-ppc64@0.25.1': optional: true - '@esbuild/aix-ppc64@0.27.7': - optional: true - '@esbuild/android-arm64@0.17.6': optional: true @@ -24245,9 +23821,6 @@ snapshots: '@esbuild/android-arm64@0.25.1': optional: true - '@esbuild/android-arm64@0.27.7': - optional: true - '@esbuild/android-arm@0.15.18': optional: true @@ -24269,9 +23842,6 @@ snapshots: '@esbuild/android-arm@0.25.1': optional: true - '@esbuild/android-arm@0.27.7': - optional: true - '@esbuild/android-x64@0.17.6': optional: true @@ -24290,9 +23860,6 @@ snapshots: '@esbuild/android-x64@0.25.1': optional: true - '@esbuild/android-x64@0.27.7': - optional: true - '@esbuild/darwin-arm64@0.17.6': optional: true @@ -24311,9 +23878,6 @@ snapshots: '@esbuild/darwin-arm64@0.25.1': optional: true - '@esbuild/darwin-arm64@0.27.7': - optional: true - '@esbuild/darwin-x64@0.17.6': optional: true @@ -24332,9 +23896,6 @@ snapshots: '@esbuild/darwin-x64@0.25.1': optional: true - '@esbuild/darwin-x64@0.27.7': - optional: true - '@esbuild/freebsd-arm64@0.17.6': optional: true @@ -24353,9 +23914,6 @@ snapshots: '@esbuild/freebsd-arm64@0.25.1': optional: true - '@esbuild/freebsd-arm64@0.27.7': - optional: true - '@esbuild/freebsd-x64@0.17.6': optional: true @@ -24374,9 +23932,6 @@ snapshots: '@esbuild/freebsd-x64@0.25.1': optional: true - '@esbuild/freebsd-x64@0.27.7': - optional: true - '@esbuild/linux-arm64@0.17.6': optional: true @@ -24395,9 +23950,6 @@ snapshots: '@esbuild/linux-arm64@0.25.1': optional: true - '@esbuild/linux-arm64@0.27.7': - optional: true - '@esbuild/linux-arm@0.17.6': optional: true @@ -24416,9 +23968,6 @@ snapshots: '@esbuild/linux-arm@0.25.1': optional: true - '@esbuild/linux-arm@0.27.7': - optional: true - '@esbuild/linux-ia32@0.17.6': optional: true @@ -24437,9 +23986,6 @@ snapshots: '@esbuild/linux-ia32@0.25.1': optional: true - '@esbuild/linux-ia32@0.27.7': - optional: true - '@esbuild/linux-loong64@0.15.18': optional: true @@ -24461,9 +24007,6 @@ snapshots: '@esbuild/linux-loong64@0.25.1': optional: true - '@esbuild/linux-loong64@0.27.7': - optional: true - '@esbuild/linux-mips64el@0.17.6': optional: true @@ -24482,9 +24025,6 @@ snapshots: '@esbuild/linux-mips64el@0.25.1': optional: true - '@esbuild/linux-mips64el@0.27.7': - optional: true - '@esbuild/linux-ppc64@0.17.6': optional: true @@ -24503,9 +24043,6 @@ snapshots: '@esbuild/linux-ppc64@0.25.1': optional: true - '@esbuild/linux-ppc64@0.27.7': - optional: true - '@esbuild/linux-riscv64@0.17.6': optional: true @@ -24524,9 +24061,6 @@ snapshots: '@esbuild/linux-riscv64@0.25.1': optional: true - '@esbuild/linux-riscv64@0.27.7': - optional: true - '@esbuild/linux-s390x@0.17.6': optional: true @@ -24545,9 +24079,6 @@ snapshots: '@esbuild/linux-s390x@0.25.1': optional: true - '@esbuild/linux-s390x@0.27.7': - optional: true - '@esbuild/linux-x64@0.17.6': optional: true @@ -24566,18 +24097,12 @@ snapshots: '@esbuild/linux-x64@0.25.1': optional: true - '@esbuild/linux-x64@0.27.7': - optional: true - '@esbuild/netbsd-arm64@0.24.2': optional: true '@esbuild/netbsd-arm64@0.25.1': optional: true - '@esbuild/netbsd-arm64@0.27.7': - optional: true - '@esbuild/netbsd-x64@0.17.6': optional: true @@ -24596,9 +24121,6 @@ snapshots: '@esbuild/netbsd-x64@0.25.1': optional: true - '@esbuild/netbsd-x64@0.27.7': - optional: true - '@esbuild/openbsd-arm64@0.23.0': optional: true @@ -24608,9 +24130,6 @@ snapshots: '@esbuild/openbsd-arm64@0.25.1': optional: true - '@esbuild/openbsd-arm64@0.27.7': - optional: true - '@esbuild/openbsd-x64@0.17.6': optional: true @@ -24629,12 +24148,6 @@ snapshots: '@esbuild/openbsd-x64@0.25.1': optional: true - '@esbuild/openbsd-x64@0.27.7': - optional: true - - '@esbuild/openharmony-arm64@0.27.7': - optional: true - '@esbuild/sunos-x64@0.17.6': optional: true @@ -24653,9 +24166,6 @@ snapshots: '@esbuild/sunos-x64@0.25.1': optional: true - '@esbuild/sunos-x64@0.27.7': - optional: true - '@esbuild/win32-arm64@0.17.6': optional: true @@ -24674,9 +24184,6 @@ snapshots: '@esbuild/win32-arm64@0.25.1': optional: true - '@esbuild/win32-arm64@0.27.7': - optional: true - '@esbuild/win32-ia32@0.17.6': optional: true @@ -24695,9 +24202,6 @@ snapshots: '@esbuild/win32-ia32@0.25.1': optional: true - '@esbuild/win32-ia32@0.27.7': - optional: true - '@esbuild/win32-x64@0.17.6': optional: true @@ -24716,9 +24220,6 @@ snapshots: '@esbuild/win32-x64@0.25.1': optional: true - '@esbuild/win32-x64@0.27.7': - optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@8.31.0)': dependencies: eslint: 8.31.0 @@ -24944,9 +24445,9 @@ snapshots: dependencies: react: 18.2.0 - '@hono/node-server@1.12.2(hono@4.5.11)': + '@hono/node-server@1.12.2(hono@4.12.15)': dependencies: - hono: 4.5.11 + hono: 4.12.15 '@hono/node-server@1.19.11(hono@4.12.15)': dependencies: @@ -24962,7 +24463,7 @@ snapshots: '@hono/node-ws@1.0.4(@hono/node-server@1.12.2(hono@4.5.11))(bufferutil@4.0.9)': dependencies: - '@hono/node-server': 1.12.2(hono@4.5.11) + '@hono/node-server': 1.12.2(hono@4.12.15) ws: 8.18.3(bufferutil@4.0.9) transitivePeerDependencies: - bufferutil @@ -30619,37 +30120,6 @@ snapshots: '@sec-ant/readable-stream@0.4.1': {} - '@secure-exec/browser@0.1.0': - dependencies: - '@secure-exec/core': 0.1.0 - sucrase: 3.35.0 - optional: true - - '@secure-exec/core@0.1.0': - dependencies: - buffer: 6.0.3 - esbuild: 0.27.7 - node-stdlib-browser: 1.3.1 - sucrase: 3.35.0 - text-encoding-utf-8: 1.0.2 - whatwg-url: 15.1.0 - - '@secure-exec/node@0.1.0': - dependencies: - '@secure-exec/core': 0.1.0 - esbuild: 0.27.7 - isolated-vm: 6.1.2 - node-stdlib-browser: 1.3.1 - - '@secure-exec/python@0.1.0(bufferutil@4.0.9)': - dependencies: - '@secure-exec/core': 0.1.0 - pyodide: 0.28.3(bufferutil@4.0.9) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - optional: true - '@selderee/plugin-htmlparser2@0.11.0': dependencies: domhandler: 5.0.3 @@ -33587,12 +33057,6 @@ snapshots: asap@2.0.6: {} - asn1.js@4.10.1: - dependencies: - bn.js: 4.12.3 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - asn1@0.2.6: dependencies: safer-buffer: 2.1.2 @@ -33601,14 +33065,6 @@ snapshots: assert-plus@1.0.0: {} - assert@2.1.0: - dependencies: - call-bind: 1.0.8 - is-nan: 1.3.2 - object-is: 1.1.6 - object.assign: 4.1.5 - util: 0.12.5 - assertion-error@2.0.1: {} ast-types-flow@0.0.7: {} @@ -33800,10 +33256,6 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - bn.js@4.12.3: {} - - bn.js@5.2.3: {} - body-parser@1.20.3: dependencies: bytes: 3.1.2 @@ -33876,60 +33328,10 @@ snapshots: dependencies: wcwidth: 1.0.1 - brorand@1.1.0: {} - - browser-resolve@2.0.0: - dependencies: - resolve: 1.22.8 - - browserify-aes@1.2.0: - dependencies: - buffer-xor: 1.0.3 - cipher-base: 1.0.7 - create-hash: 1.2.0 - evp_bytestokey: 1.0.3 - inherits: 2.0.4 - safe-buffer: 5.2.1 - - browserify-cipher@1.0.1: - dependencies: - browserify-aes: 1.2.0 - browserify-des: 1.0.2 - evp_bytestokey: 1.0.3 - - browserify-des@1.0.2: - dependencies: - cipher-base: 1.0.7 - des.js: 1.1.0 - inherits: 2.0.4 - safe-buffer: 5.2.1 - - browserify-rsa@4.1.1: - dependencies: - bn.js: 5.2.3 - randombytes: 2.1.0 - safe-buffer: 5.2.1 - - browserify-sign@4.2.5: - dependencies: - bn.js: 5.2.3 - browserify-rsa: 4.1.1 - create-hash: 1.2.0 - create-hmac: 1.1.7 - elliptic: 6.6.1 - inherits: 2.0.4 - parse-asn1: 5.1.9 - readable-stream: 2.3.8 - safe-buffer: 5.2.1 - browserify-zlib@0.1.4: dependencies: pako: 0.2.9 - browserify-zlib@0.2.0: - dependencies: - pako: 1.0.11 - browserslist@4.21.4: dependencies: caniuse-lite: 1.0.30001577 @@ -33962,8 +33364,6 @@ snapshots: buffer-from@1.1.2: {} - buffer-xor@1.0.3: {} - buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -33981,8 +33381,6 @@ snapshots: buildcheck@0.0.6: optional: true - builtin-status-codes@3.0.0: {} - builtins@1.0.3: {} builtins@5.0.1: @@ -34267,12 +33665,6 @@ snapshots: ci-info@3.8.0: {} - cipher-base@1.0.7: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - to-buffer: 1.2.2 - citty@0.1.6: dependencies: consola: 3.4.2 @@ -34494,10 +33886,6 @@ snapshots: consola@3.4.2: {} - console-browserify@1.2.0: {} - - constants-browserify@1.0.0: {} - content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 @@ -34604,30 +33992,6 @@ snapshots: crc-32: 1.2.2 readable-stream: 4.7.0 - create-ecdh@4.0.4: - dependencies: - bn.js: 4.12.3 - elliptic: 6.6.1 - - create-hash@1.2.0: - dependencies: - cipher-base: 1.0.7 - inherits: 2.0.4 - md5.js: 1.3.5 - ripemd160: 2.0.3 - sha.js: 2.4.12 - - create-hmac@1.1.7: - dependencies: - cipher-base: 1.0.7 - create-hash: 1.2.0 - inherits: 2.0.4 - ripemd160: 2.0.3 - safe-buffer: 5.2.1 - sha.js: 2.4.12 - - create-require@1.1.1: {} - crelt@1.0.6: {} cron-parser@4.9.0: @@ -34668,21 +34032,6 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - crypto-browserify@3.12.1: - dependencies: - browserify-cipher: 1.0.1 - browserify-sign: 4.2.5 - create-ecdh: 4.0.4 - create-hash: 1.2.0 - create-hmac: 1.1.7 - diffie-hellman: 5.0.3 - hash-base: 3.0.5 - inherits: 2.0.4 - pbkdf2: 3.1.5 - public-encrypt: 4.0.3 - randombytes: 2.1.0 - randomfill: 1.0.4 - crypto-js@4.1.1: {} crypto-js@4.2.0: {} @@ -35102,11 +34451,6 @@ snapshots: dequal@2.0.3: {} - des.js@1.1.0: - dependencies: - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - destr@2.0.3: {} destr@2.0.5: {} @@ -35144,12 +34488,6 @@ snapshots: diff@5.1.0: {} - diffie-hellman@5.0.3: - dependencies: - bn.js: 4.12.3 - miller-rabin: 4.0.1 - randombytes: 2.1.0 - dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -35232,8 +34570,6 @@ snapshots: domhandler: 5.0.3 entities: 4.5.0 - domain-browser@4.22.0: {} - domelementtype@2.3.0: {} domhandler@5.0.3: @@ -35367,16 +34703,6 @@ snapshots: electron-to-chromium@1.5.252: {} - elliptic@6.6.1: - dependencies: - bn.js: 4.12.3 - brorand: 1.1.0 - hash.js: 1.1.7 - hmac-drbg: 1.0.1 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -35826,35 +35152,6 @@ snapshots: '@esbuild/win32-ia32': 0.25.1 '@esbuild/win32-x64': 0.25.1 - esbuild@0.27.7: - optionalDependencies: - '@esbuild/aix-ppc64': 0.27.7 - '@esbuild/android-arm': 0.27.7 - '@esbuild/android-arm64': 0.27.7 - '@esbuild/android-x64': 0.27.7 - '@esbuild/darwin-arm64': 0.27.7 - '@esbuild/darwin-x64': 0.27.7 - '@esbuild/freebsd-arm64': 0.27.7 - '@esbuild/freebsd-x64': 0.27.7 - '@esbuild/linux-arm': 0.27.7 - '@esbuild/linux-arm64': 0.27.7 - '@esbuild/linux-ia32': 0.27.7 - '@esbuild/linux-loong64': 0.27.7 - '@esbuild/linux-mips64el': 0.27.7 - '@esbuild/linux-ppc64': 0.27.7 - '@esbuild/linux-riscv64': 0.27.7 - '@esbuild/linux-s390x': 0.27.7 - '@esbuild/linux-x64': 0.27.7 - '@esbuild/netbsd-arm64': 0.27.7 - '@esbuild/netbsd-x64': 0.27.7 - '@esbuild/openbsd-arm64': 0.27.7 - '@esbuild/openbsd-x64': 0.27.7 - '@esbuild/openharmony-arm64': 0.27.7 - '@esbuild/sunos-x64': 0.27.7 - '@esbuild/win32-arm64': 0.27.7 - '@esbuild/win32-ia32': 0.27.7 - '@esbuild/win32-x64': 0.27.7 - escalade@3.2.0: {} escape-html@1.0.3: {} @@ -36245,11 +35542,6 @@ snapshots: dependencies: eventsource-parser: 3.0.3 - evp_bytestokey@1.0.3: - dependencies: - md5.js: 1.3.5 - safe-buffer: 5.2.1 - evt@2.4.13: dependencies: minimal-polyfills: 2.2.2 @@ -36656,10 +35948,6 @@ snapshots: dependencies: is-callable: 1.2.7 - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 - foreground-child@3.1.1: dependencies: cross-spawn: 7.0.6 @@ -37114,23 +36402,6 @@ snapshots: dependencies: function-bind: 1.1.2 - hash-base@3.0.5: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - - hash-base@3.1.2: - dependencies: - inherits: 2.0.4 - readable-stream: 2.3.8 - safe-buffer: 5.2.1 - to-buffer: 1.2.2 - - hash.js@1.1.7: - dependencies: - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -37287,12 +36558,6 @@ snapshots: highlight.js@10.7.3: {} - hmac-drbg@1.0.1: - dependencies: - hash.js: 1.1.7 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - hoist-non-react-statics@3.3.2: dependencies: react-is: 16.13.1 @@ -37365,8 +36630,6 @@ snapshots: http-status-codes@2.3.0: {} - https-browserify@1.0.0: {} - https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 @@ -37629,11 +36892,6 @@ snapshots: is-interactive@1.0.0: {} - is-nan@1.3.2: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - is-negative-zero@2.0.2: {} is-negative-zero@2.0.3: {} @@ -37699,10 +36957,6 @@ snapshots: dependencies: which-typed-array: 1.1.15 - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.20 - is-typedarray@1.0.0: {} is-unicode-supported@0.1.0: {} @@ -37733,12 +36987,6 @@ snapshots: isexe@2.0.0: {} - isolated-vm@6.1.2: - dependencies: - node-gyp-build: 4.8.4 - - isomorphic-timers-promises@1.0.1: {} - isomorphic-ws@5.0.0(ws@8.16.0(bufferutil@4.0.9)): dependencies: ws: 8.16.0(bufferutil@4.0.9) @@ -38371,12 +37619,6 @@ snapshots: marked: 7.0.4 react: 18.3.1 - md5.js@1.3.5: - dependencies: - hash-base: 3.0.5 - inherits: 2.0.4 - safe-buffer: 5.2.1 - mdast-util-definitions@5.1.1: dependencies: '@types/mdast': 3.0.10 @@ -39149,11 +38391,6 @@ snapshots: braces: 3.0.3 picomatch: 2.3.2 - miller-rabin@4.0.1: - dependencies: - bn.js: 4.12.3 - brorand: 1.1.0 - mime-db@1.52.0: {} mime-db@1.53.0: {} @@ -39194,10 +38431,6 @@ snapshots: minimal-polyfills@2.2.3: {} - minimalistic-assert@1.0.1: {} - - minimalistic-crypto-utils@1.0.1: {} - minimatch@10.0.1: dependencies: brace-expansion: 2.0.1 @@ -39638,36 +38871,6 @@ snapshots: node-releases@2.0.27: {} - node-stdlib-browser@1.3.1: - dependencies: - assert: 2.1.0 - browser-resolve: 2.0.0 - browserify-zlib: 0.2.0 - buffer: 5.7.1 - console-browserify: 1.2.0 - constants-browserify: 1.0.0 - create-require: 1.1.1 - crypto-browserify: 3.12.1 - domain-browser: 4.22.0 - events: 3.3.0 - https-browserify: 1.0.0 - isomorphic-timers-promises: 1.0.1 - os-browserify: 0.3.0 - path-browserify: 1.0.1 - pkg-dir: 5.0.0 - process: 0.11.10 - punycode: 1.4.1 - querystring-es3: 0.2.1 - readable-stream: 3.6.2 - stream-browserify: 3.0.0 - stream-http: 3.2.0 - string_decoder: 1.3.0 - timers-browserify: 2.0.12 - tty-browserify: 0.0.1 - url: 0.11.4 - util: 0.12.5 - vm-browserify: 1.1.2 - nodemailer@8.0.6: {} non.geist@1.0.2: {} @@ -39785,11 +38988,6 @@ snapshots: object-inspect@1.13.4: {} - object-is@1.1.6: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - object-keys@1.1.1: {} object.assign@4.1.5: @@ -40012,8 +39210,6 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 - os-browserify@0.3.0: {} - os-paths@7.4.0: optionalDependencies: fsevents: 2.3.3 @@ -40142,20 +39338,10 @@ snapshots: pako@0.2.9: {} - pako@1.0.11: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 - parse-asn1@5.1.9: - dependencies: - asn1.js: 4.10.1 - browserify-aes: 1.2.0 - evp_bytestokey: 1.0.3 - pbkdf2: 3.1.5 - safe-buffer: 5.2.1 - parse-duration@2.1.4: {} parse-entities@4.0.0: @@ -40210,8 +39396,6 @@ snapshots: dependencies: event-target-shim: 6.0.2 - path-browserify@1.0.1: {} - path-data-parser@0.1.0: {} path-exists@4.0.0: {} @@ -40256,15 +39440,6 @@ snapshots: pathval@2.0.0: {} - pbkdf2@3.1.5: - dependencies: - create-hash: 1.2.0 - create-hmac: 1.1.7 - ripemd160: 2.0.3 - safe-buffer: 5.2.1 - sha.js: 2.4.12 - to-buffer: 1.2.2 - peberminta@0.9.0: {} peek-readable@5.4.2: {} @@ -40420,10 +39595,6 @@ snapshots: dependencies: find-up: 4.1.0 - pkg-dir@5.0.0: - dependencies: - find-up: 5.0.0 - pkg-types@1.1.3: dependencies: confbox: 0.1.8 @@ -40865,15 +40036,6 @@ snapshots: psl@1.9.0: {} - public-encrypt@4.0.3: - dependencies: - bn.js: 4.12.3 - browserify-rsa: 4.1.1 - create-hash: 1.2.0 - parse-asn1: 5.1.9 - randombytes: 2.1.0 - safe-buffer: 5.2.1 - pump@2.0.1: dependencies: end-of-stream: 1.4.4 @@ -40894,8 +40056,6 @@ snapshots: punycode@2.2.0: {} - punycode@2.3.1: {} - puppeteer-core@24.15.0(bufferutil@4.0.9): dependencies: '@puppeteer/browsers': 2.10.6 @@ -40920,14 +40080,6 @@ snapshots: postcss: 7.0.32 postcss-selector-parser: 6.1.2 - pyodide@0.28.3(bufferutil@4.0.9): - dependencies: - ws: 8.18.3(bufferutil@4.0.9) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - optional: true - qrcode.react@4.2.0(react@18.2.0): dependencies: react: 18.2.0 @@ -40938,8 +40090,6 @@ snapshots: quansync@0.2.11: {} - querystring-es3@0.2.1: {} - queue-microtask@1.2.3: {} quick-format-unescaped@4.0.4: {} @@ -40961,11 +40111,6 @@ snapshots: dependencies: safe-buffer: 5.2.1 - randomfill@1.0.4: - dependencies: - randombytes: 2.1.0 - safe-buffer: 5.2.1 - range-parser@1.2.1: {} raw-body@2.5.2: @@ -41938,11 +41083,6 @@ snapshots: glob: 11.0.0 package-json-from-dist: 1.0.0 - ripemd160@2.0.3: - dependencies: - hash-base: 3.1.2 - inherits: 2.0.4 - robot3@0.4.1: {} robust-predicates@3.0.2: {} @@ -42088,17 +41228,6 @@ snapshots: screenfull@5.2.0: {} - secure-exec@0.1.0(bufferutil@4.0.9): - dependencies: - '@secure-exec/core': 0.1.0 - '@secure-exec/node': 0.1.0 - optionalDependencies: - '@secure-exec/browser': 0.1.0 - '@secure-exec/python': 0.1.0(bufferutil@4.0.9) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - secure-json-parse@2.7.0: {} secure-json-parse@4.0.0: {} @@ -42254,16 +41383,8 @@ snapshots: set-harmonic-interval@1.0.1: {} - setimmediate@1.0.5: {} - setprototypeof@1.2.0: {} - sha.js@2.4.12: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - to-buffer: 1.2.2 - sharp@0.33.5: dependencies: color: 4.2.3 @@ -42686,20 +41807,8 @@ snapshots: std-env@3.9.0: {} - stream-browserify@3.0.0: - dependencies: - inherits: 2.0.4 - readable-stream: 3.6.2 - stream-buffers@3.0.2: {} - stream-http@3.2.0: - dependencies: - builtin-status-codes: 3.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - xtend: 4.0.2 - stream-shift@1.0.3: {} stream-slice@0.1.2: {} @@ -43303,8 +42412,6 @@ snapshots: dependencies: b4a: 1.6.6 - text-encoding-utf-8@1.0.2: {} - text-table@0.2.0: {} thenify-all@1.6.0: @@ -43328,10 +42435,6 @@ snapshots: readable-stream: 2.3.8 xtend: 4.0.2 - timers-browserify@2.0.12: - dependencies: - setimmediate: 1.0.5 - tiny-case@1.0.3: {} tiny-glob@0.2.9: @@ -43404,12 +42507,6 @@ snapshots: tmp@0.2.5: {} - to-buffer@1.2.2: - dependencies: - isarray: 2.0.5 - safe-buffer: 5.2.1 - typed-array-buffer: 1.0.3 - to-fast-properties@2.0.0: {} to-readable-stream@1.0.0: {} @@ -43446,10 +42543,6 @@ snapshots: dependencies: punycode: 2.2.0 - tr46@6.0.0: - dependencies: - punycode: 2.3.1 - tree-kill@1.2.2: {} trim-lines@3.0.1: {} @@ -43615,8 +42708,6 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - tty-browserify@0.0.1: {} - tty-table@4.1.6: dependencies: chalk: 4.1.2 @@ -43709,12 +42800,6 @@ snapshots: es-errors: 1.3.0 is-typed-array: 1.1.13 - typed-array-buffer@1.0.3: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-typed-array: 1.1.15 - typed-array-byte-length@1.0.1: dependencies: call-bind: 1.0.8 @@ -43944,11 +43029,6 @@ snapshots: dependencies: prepend-http: 2.0.0 - url@0.11.4: - dependencies: - punycode: 1.4.1 - qs: 6.14.1 - urlpattern-polyfill@9.0.0: {} use-callback-ref@1.3.3(@types/react@18.2.69)(react@18.2.0): @@ -44261,11 +43341,11 @@ snapshots: '@vitest/spy': 3.1.4 '@vitest/utils': 3.1.4 chai: 5.2.0 - debug: 4.4.3(supports-color@10.0.0) + debug: 4.4.1 expect-type: 1.2.1 magic-string: 0.30.21 pathe: 2.0.3 - std-env: 3.10.0 + std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 tinyglobby: 0.2.13 @@ -44331,8 +43411,6 @@ snapshots: - tsx - yaml - vm-browserify@1.1.2: {} - vscode-jsonrpc@8.2.0: {} vscode-languageserver-protocol@3.17.5: @@ -44390,8 +43468,6 @@ snapshots: webidl-conversions@4.0.2: {} - webidl-conversions@8.0.1: {} - webpack-bundle-analyzer@4.10.1(bufferutil@4.0.9): dependencies: '@discoveryjs/json-ext': 0.5.7 @@ -44478,11 +43554,6 @@ snapshots: - esbuild - uglify-js - whatwg-url@15.1.0: - dependencies: - tr46: 6.0.0 - webidl-conversions: 8.0.1 - whatwg-url@5.0.0: dependencies: tr46: 0.0.3 @@ -44517,16 +43588,6 @@ snapshots: gopd: 1.2.0 has-tostringtag: 1.0.2 - which-typed-array@1.1.20: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - which@1.3.1: dependencies: isexe: 2.0.0 diff --git a/references/ai-chat/package.json b/references/ai-chat/package.json index 3f15c192d..1d2b123fc 100644 --- a/references/ai-chat/package.json +++ b/references/ai-chat/package.json @@ -20,7 +20,6 @@ "@prisma/client": "^7.4.2", "@e2b/code-interpreter": "^2.4.0", "@trigger.dev/sdk": "workspace:*", - "secure-exec": "0.1.0", "serialize-error": "^11.0.3", "ai": "^6.0.0", "next": "15.3.3", diff --git a/references/ai-chat/src/app/actions.ts b/references/ai-chat/src/app/actions.ts index ac9484406..8f9f91c8e 100644 --- a/references/ai-chat/src/app/actions.ts +++ b/references/ai-chat/src/app/actions.ts @@ -49,20 +49,34 @@ export const triggerChat = chat.createTriggerAction("ai-chat", { }); /** - * Mint a fresh run-scoped PAT for an existing chat run (same scopes as the task’s turn token). - * Used by TriggerChatTransport when the stored PAT expires (401 on realtime / input stream). - * Persists `publicAccessToken` (and `runId`) on `ChatSession` for this `chatId`. - * Requires TRIGGER_SECRET_KEY (or configured secret) in the server environment. + * Mint a fresh PAT for an existing chat (same scopes as the task's turn + * token). Used by TriggerChatTransport when the stored PAT expires (401 + * on realtime / input stream). Persists `publicAccessToken` (and + * `runId`) on `ChatSession` for this `chatId`. Requires + * TRIGGER_SECRET_KEY (or configured secret) in the server environment. + * + * Scopes match the initial `chat.createTriggerAction` mint so the PAT + * stays valid against both run-scoped endpoints (run PAT renewal, input + * streams) and session-scoped endpoints (`/realtime/v1/sessions/…/in` + + * `/realtime/v1/sessions/…/out`). Without the session scopes, the + * renewed token 401s on the session append path the transport uses. */ export async function renewRunAccessTokenForChat( chatId: string, - runId: string + runId: string, + sessionId?: string ): Promise { try { const token = await auth.createPublicToken({ scopes: { - read: { runs: runId }, - write: { inputStreams: runId }, + read: { + runs: runId, + ...(sessionId ? { sessions: sessionId } : {}), + }, + write: { + inputStreams: runId, + ...(sessionId ? { sessions: sessionId } : {}), + }, }, expirationTime: CHAT_EXAMPLE_PAT_TTL, }); diff --git a/references/ai-chat/src/components/chat-app.tsx b/references/ai-chat/src/components/chat-app.tsx index 7b453463f..de8e4f464 100644 --- a/references/ai-chat/src/components/chat-app.tsx +++ b/references/ai-chat/src/components/chat-app.tsx @@ -8,7 +8,7 @@ import { Chat } from "@/components/chat"; import { ChatSidebar } from "@/components/chat-sidebar"; import { DEFAULT_MODEL } from "@/lib/models"; import { - getChatToken, + triggerChat, getChatList, getChatMessages, deleteChat as deleteChatAction, @@ -73,8 +73,14 @@ export function ChatApp({ const transport = useTriggerChatTransport({ task: taskMode, - accessToken: (params) => getChatToken({ ...params, taskId: taskMode }), - renewRunAccessToken: ({ chatId, runId }) => renewRunAccessTokenForChat(chatId, runId), + // Server-side trigger action creates the backing Session with the + // project's secret key + threads `sessionId` into the run payload. + // Returned PAT already has `read:runs` + `read:sessions` + + // `write:sessions` scopes (from `chat.createTriggerAction` Phase E), + // so the browser never needs to mint write:sessions itself. + triggerTask: triggerChat, + renewRunAccessToken: ({ chatId, runId, sessionId }) => + renewRunAccessTokenForChat(chatId, runId, sessionId), baseURL: process.env.NEXT_PUBLIC_TRIGGER_API_URL, sessions: initialSessions, onSessionChange: handleSessionChange, diff --git a/references/ai-chat/src/components/chat-view.tsx b/references/ai-chat/src/components/chat-view.tsx index 5f72a8b0f..c629326ec 100644 --- a/references/ai-chat/src/components/chat-view.tsx +++ b/references/ai-chat/src/components/chat-view.tsx @@ -6,7 +6,7 @@ import { Chat } from "@/components/chat"; import { useChatSettings } from "@/components/chat-settings-context"; import { DEFAULT_MODEL } from "@/lib/models"; import { - getChatToken, + triggerChat, getChatList, updateChatTitle, deleteSessionAction, @@ -57,8 +57,15 @@ export function ChatView({ const transport = useTriggerChatTransport({ task: taskMode, - accessToken: (params) => getChatToken({ ...params, taskId: taskMode }), - renewRunAccessToken: ({ chatId, runId }) => renewRunAccessTokenForChat(chatId, runId), + // Server-side trigger action creates the backing Session with the + // project's secret key + threads `sessionId` into the run payload. + // Returned PAT already has `read:runs` + `read:sessions` + + // `write:sessions` scopes (from `chat.createTriggerAction` Phase E), + // so the browser never needs `write:sessions` itself — and no CORS + // preflight hits `/api/v1/sessions` from the browser. + triggerTask: triggerChat, + renewRunAccessToken: ({ chatId, runId, sessionId }) => + renewRunAccessTokenForChat(chatId, runId, sessionId), baseURL: process.env.NEXT_PUBLIC_TRIGGER_API_URL, sessions, onSessionChange: handleSessionChange, diff --git a/references/ai-chat/src/lib/chat-tools.ts b/references/ai-chat/src/lib/chat-tools.ts index 9057e4c3b..8277ca4d9 100644 --- a/references/ai-chat/src/lib/chat-tools.ts +++ b/references/ai-chat/src/lib/chat-tools.ts @@ -6,7 +6,6 @@ import { z } from "zod"; import os from "node:os"; import TurndownService from "turndown"; import { codeSandboxRun, runWithCodeSandbox } from "@/lib/code-sandbox"; -import { runInSecureSandbox } from "@/lib/secure-sandbox"; const turndown = new TurndownService(); @@ -284,30 +283,6 @@ export const executeCode = tool({ }, }); -export const executeJs = tool({ - description: - "Run JavaScript code in an isolated V8 sandbox (secure-exec). " + - "Use for calculations, data transformations, or quick JS snippets. " + - "The code runs as a CommonJS module — assign results to module.exports. " + - "Example: module.exports = { sum: 1 + 2 };", - inputSchema: z.object({ - code: z.string().describe("JavaScript code to execute. Assign results to module.exports."), - }), - execute: async ({ code }) => { - return runInSecureSandbox(async (runtime) => { - const result = await runtime.run(code); - - if (result.code !== 0) { - return { - error: result.errorMessage ?? `Exit code ${result.code}`, - }; - } - - return { result: result.exports }; - }); - }, -}); - export const sendEmail = tool({ description: "Send an email to a recipient. Requires human approval before sending. " + @@ -352,7 +327,6 @@ export const chatTools = { deepResearch, posthogQuery, executeCode, - executeJs, sendEmail, askUser, }; diff --git a/references/ai-chat/src/lib/pr-review-sandbox.ts b/references/ai-chat/src/lib/pr-review-sandbox.ts deleted file mode 100644 index f9877d0ff..000000000 --- a/references/ai-chat/src/lib/pr-review-sandbox.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * secure-exec V8 sandbox for PR review — with Node filesystem + network access. - * - * Unlike the default secure-sandbox.ts (restricted fs/network), this sandbox - * grants full filesystem and network access so the agent can read cloned repo - * files and make HTTP requests from within sandboxed code. - * - * The sandbox receives a `cwd` (the cloned repo path) which is injected as - * a global `__cwd` constant so sandboxed code can reference the repo root. - * - * 256MB memory, 30s CPU time limit. - */ -import { - NodeRuntime, - NodeFileSystem, - createNodeDriver, - createNodeRuntimeDriverFactory, - allowAllFs, - allowAllNetwork, -} from "secure-exec"; - -export async function runInPRReviewSandbox( - cwd: string, - runner: (runtime: NodeRuntime) => Promise -): Promise { - const runtime = new NodeRuntime({ - systemDriver: createNodeDriver({ - filesystem: new NodeFileSystem(), - permissions: { - ...allowAllFs, - ...allowAllNetwork, - }, - }), - runtimeDriverFactory: createNodeRuntimeDriverFactory(), - memoryLimit: 256, - cpuTimeLimitMs: 30_000, - }); - - try { - // Inject the repo cwd as a global so sandboxed code can use it - await runtime.run(`globalThis.__cwd = ${JSON.stringify(cwd)};`); - return await runner(runtime); - } catch (err) { - return { error: err instanceof Error ? err.message : String(err) }; - } finally { - runtime.dispose(); - } -} diff --git a/references/ai-chat/src/lib/pr-review-tools.ts b/references/ai-chat/src/lib/pr-review-tools.ts index de153dc1b..31efe3355 100644 --- a/references/ai-chat/src/lib/pr-review-tools.ts +++ b/references/ai-chat/src/lib/pr-review-tools.ts @@ -6,7 +6,6 @@ import { z } from "zod"; import { resolve } from "node:path"; import { readFile as fsReadFile } from "node:fs/promises"; import { git, githubApi } from "@/lib/pr-review-helpers"; -import { runInPRReviewSandbox } from "@/lib/pr-review-sandbox"; // #region Repo context — shared across tools, survives snapshot/restore export const repo = chat.local<{ @@ -183,93 +182,8 @@ export const readFile = tool({ }); // #endregion -// #region Tool: Execute Code -export const executeCode = tool({ - description: - "Run JavaScript code in an isolated V8 sandbox to verify claims about the code. " + - "Use this to PROVE claims (e.g., test a regex, validate parsing logic, check edge cases) " + - "before including them in your review. The sandbox has filesystem and network access. " + - "The repo is cloned at the provided cwd path — use it for absolute file paths. " + - "Assign results to module.exports.", - inputSchema: z.object({ - code: z - .string() - .describe( - "JavaScript code to execute. Assign results to module.exports." - ), - description: z - .string() - .describe("Brief description of what this code is testing/verifying"), - }), - execute: async ({ code, description }) => { - const { cwd } = repo; - - const result = await runInPRReviewSandbox(cwd, async (runtime) => { - const execResult = await runtime.run(code); - - if (execResult.code !== 0) { - return { - description, - success: false as const, - error: execResult.errorMessage ?? `Exit code ${execResult.code}`, - }; - } - - return { - description, - success: true as const, - // Sanitize the sandbox's `module.exports` so the value matches the - // strict JSON shape that AI SDK's `jsonValueSchema` accepts. Raw JS - // can produce `Infinity`, `NaN`, `undefined`, `BigInt`, etc., none - // of which survive Zod v4's `z.number()` (which rejects non-finite - // numbers). The full message history is re-validated at the start - // of every subsequent `streamText` call, so an unsanitized value - // here would crash the agent on the *next* turn even though the - // current turn appears to succeed. - result: toJsonValue(execResult.exports), - }; - }); - - // runInPRReviewSandbox returns { error } on catch - if (result && typeof result === "object" && "error" in result && !("success" in result)) { - return { description, success: false, error: result.error }; - } - - return result; - }, -}); - -/** - * Coerce arbitrary JS to a value compatible with AI SDK's `jsonValueSchema` - * (`null | string | number | boolean | object | array`, where `number` must - * be finite). - * - * Uses `JSON.parse(JSON.stringify(...))` with a replacer so non-finite - * numbers become `null` (matching `JSON.stringify`'s default loss for - * `NaN`/`Infinity` when encountered as object values), `BigInt` is - * stringified, and `undefined` / functions are dropped — same coercions - * `JSON.stringify` already applies, but called explicitly so the result - * is a plain JSON value tree the SDK can re-validate on later turns. - */ -function toJsonValue(value: unknown): unknown { - try { - return JSON.parse( - JSON.stringify(value, (_key, v) => { - if (typeof v === "number" && !Number.isFinite(v)) return null; - if (typeof v === "bigint") return v.toString(); - return v; - }) - ); - } catch { - // Circular references or other JSON.stringify failures — fall back to a - // descriptive placeholder so the tool result is still valid JSON. - return { error: "Result was not JSON-serializable" }; - } -} -// #endregion - // #region Exports -export const prReviewTools = { fetchPR, readFile, executeCode }; +export const prReviewTools = { fetchPR, readFile }; type PRReviewToolSet = typeof prReviewTools; export type PRReviewUiTools = InferUITools; diff --git a/references/ai-chat/src/lib/secure-sandbox.ts b/references/ai-chat/src/lib/secure-sandbox.ts deleted file mode 100644 index 21823fba0..000000000 --- a/references/ai-chat/src/lib/secure-sandbox.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * secure-exec V8 sandbox — runs JavaScript in-process via V8 isolates. - * - * No external API key needed. ~14ms cold start, ~3MB per isolate. - * A fresh runtime is created per execution — no warm/dispose lifecycle needed. - */ -import { - NodeRuntime, - createNodeDriver, - createNodeRuntimeDriverFactory, -} from "secure-exec"; - -export async function runInSecureSandbox( - runner: (runtime: NodeRuntime) => Promise -): Promise { - const runtime = new NodeRuntime({ - systemDriver: createNodeDriver({ - permissions: { - fs: (req) => ({ - allow: req.path.startsWith("/root") || req.path.startsWith("/tmp"), - }), - network: (req) => ({ - allow: req.hostname === "127.0.0.1" || req.hostname === "localhost", - }), - }, - }), - runtimeDriverFactory: createNodeRuntimeDriverFactory(), - memoryLimit: 128, - cpuTimeLimitMs: 60_000, - }); - - try { - return await runner(runtime); - } catch (err) { - return { error: err instanceof Error ? err.message : String(err) }; - } finally { - runtime.dispose(); - } -} diff --git a/references/ai-chat/trigger.config.ts b/references/ai-chat/trigger.config.ts index 7f67a1b73..47592c760 100644 --- a/references/ai-chat/trigger.config.ts +++ b/references/ai-chat/trigger.config.ts @@ -1,9 +1,5 @@ import { defineConfig } from "@trigger.dev/sdk"; import { prismaExtension } from "@trigger.dev/build/extensions/prisma"; -import { esbuildPlugin } from "@trigger.dev/build/extensions"; -import { createRequire } from "node:module"; -import fs from "node:fs"; -import path from "node:path"; export default defineConfig({ project: process.env.TRIGGER_PROJECT_REF!, @@ -19,61 +15,6 @@ export default defineConfig({ prismaExtension({ mode: "modern", }), - // Trigger's ESM shim anchors require.resolve() to the chunk path, so - // node-stdlib-browser's runtime require.resolve("./mock/empty.js") breaks. - // Fix: load the real node-stdlib-browser at build time (where require.resolve - // works), capture the resolved path map, and inline it as a static export. - esbuildPlugin({ - name: "node-stdlib-browser-stub", - setup(build) { - build.onResolve({ filter: /^node-stdlib-browser$/ }, () => ({ - path: "node-stdlib-browser", - namespace: "nsb-resolved", - })); - build.onLoad({ filter: /.*/, namespace: "nsb-resolved" }, () => { - const buildRequire = createRequire(import.meta.url); - const resolved = buildRequire("node-stdlib-browser"); - return { - contents: `export default ${JSON.stringify(resolved)};`, - loader: "js", - }; - }); - }, - }), - // @secure-exec/node's bridge-loader.js runs require.resolve("@secure-exec/core") - // at module scope to locate dist/bridge.js on disk. This fails in Trigger's - // Docker container where the code is bundled into chunks and the package - // isn't on disk. Fix: inline bridge.js content at build time so no runtime - // filesystem access or package resolution is needed. - esbuildPlugin({ - name: "inline-secure-exec-bridge", - setup(build) { - build.onLoad( - { filter: /[\\/]@secure-exec[\\/]node[\\/]dist[\\/]bridge-loader\.js$/ }, - (args) => { - const buildRequire = createRequire(args.path); - const coreEntry = buildRequire.resolve("@secure-exec/core"); - const coreRoot = path.resolve(path.dirname(coreEntry), ".."); - const bridgeCode = fs.readFileSync(path.join(coreRoot, "dist", "bridge.js"), "utf8"); - return { - contents: [ - `import { getIsolateRuntimeSource } from "@secure-exec/core";`, - `const bridgeCodeCache = ${JSON.stringify(bridgeCode)};`, - `export function getRawBridgeCode() { return bridgeCodeCache; }`, - `export function getBridgeAttachCode() { return getIsolateRuntimeSource("bridgeAttach"); }`, - ].join("\n"), - loader: "js", - }; - }, - ); - }, - }), - ], - external: [ - // esbuild must not be bundled — it locates its native binary via a - // relative path from its JS API entry point. secure-exec uses esbuild - // at runtime to bundle polyfills for sandbox code. - "esbuild", ], keepNames: false, },