@workerdeck/server 0.7.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { IncomingMessage, Server, ServerResponse } from "node:http";
2
- import { BridgeAnswer, BrowserBridgeExecutor, ClaudeAuthProbe, ParkedExecution, Runner, RunnerSnapshot, SessionRunnerConfig, ToolExecutionResult } from "@workerdeck/core";
2
+ import { AttachmentInput, BridgeAnswer, BrowserBridgeExecutor, ClaudeAuthProbe, EngineAdapter, ParkedExecution, Runner, RunnerSnapshot, SessionRunnerConfig, ToolExecutionResult } from "@workerdeck/core";
3
3
  import { JobQueue, QueueAdapter } from "@workerdeck/queue";
4
- import { CreateSessionRequest, JobEvent, ProfileInfo, SdkSessionSummary, ServerFrame, SessionInfo, SessionNotification, SessionWebhookConfig } from "@workerdeck/protocol";
4
+ import { CreateSessionRequest, JobEvent, MessageAttachment, ProfileEngine, ProfileInfo, SdkSessionSummary, ServerFrame, SessionInfo, SessionNotification, SessionWebhookConfig } from "@workerdeck/protocol";
5
5
 
6
6
  //#region src/registry.d.ts
7
7
  type SessionRegistryOptions = {
@@ -369,6 +369,20 @@ type WorkerServerOptions = {
369
369
  * that keeps a per-keystroke search cheap on a real source tree. */
370
370
  ignore?: string[];
371
371
  };
372
+ /**
373
+ * Message attachments (`{basePath}/sessions/:id/attachments`) — the photos and
374
+ * files a client sends alongside a message. Always on; these knobs only size it.
375
+ *
376
+ * There is no grant to make here the way `hostFiles.write` is one: an upload
377
+ * lands in the session's own in-memory hold and reaches the model as message
378
+ * content, which is exactly what typing does. What it *can* do is cost memory,
379
+ * so both caps default low enough that a phone camera roll cannot fill the
380
+ * gateway.
381
+ */
382
+ attachments?: {
383
+ /** Largest single upload; over it is a 413. Default 10 MiB. */maxFileBytes?: number; /** Ceiling on what one session holds at once. Default 64 MiB. */
384
+ maxSessionBytes?: number;
385
+ };
372
386
  /**
373
387
  * Named Claude Code config directories sessions can run under (each becomes the
374
388
  * session's CLAUDE_CONFIG_DIR — settings, memory, skills, and the credentials the
@@ -455,8 +469,11 @@ type WorkerServerOptions = {
455
469
  probe?: ClaudeAuthProbe;
456
470
  timeoutMs?: number;
457
471
  };
458
- /** Injectable lister for GET /sdk-sessions (tests). Defaults to the SDK's listSessions,
459
- * which reads the Agent SDK's on-disk session store. */
472
+ /** Injectable lister for GET /sdk-sessions (tests) honored for the CLAUDE
473
+ * engine only, like the injectable claude auth probe (it predates the adapter
474
+ * layer). Defaults to the claude adapter's lister (the SDK's on-disk session
475
+ * store); other engines always answer through their adapter's
476
+ * `listSessions`. */
460
477
  listSdkSessions?: SdkSessionLister;
461
478
  /** Enable the job queue (`/jobs` + `/queue` routes). Jobs run as ordinary registry
462
479
  * sessions — attachable over the sessions WS — governed by these limits. */
@@ -509,6 +526,8 @@ type WorkerServerOptions = {
509
526
  * Kept as a host hook so the server package neither imports a model SDK nor
510
527
  * decides how provider credentials are resolved: the factory reads them from
511
528
  * the operator's environment, exactly like the Claude credential chain.
529
+ * `claude` and `codex` profiles never come through here — those engines ship
530
+ * as in-repo adapters (`@workerdeck/core`'s `getEngineAdapter`).
512
531
  *
513
532
  * May be async: assembly that has to await — a per-session MCP connect, a
514
533
  * credential lookup — belongs here, with `AiSdkRunnerConfig.onClose` as the
@@ -516,6 +535,13 @@ type WorkerServerOptions = {
516
535
  * the message, a job goes straight to `failed`.
517
536
  */
518
537
  createEngineRunner?: (context: EngineRunnerContext) => Runner | Promise<Runner>;
538
+ /**
539
+ * Adapter overrides, keyed by engine — **for tests only** (the server
540
+ * integration suite injects a fake codex engine so `pnpm test` spawns no
541
+ * binary). Not a public extension point: third engines belong in core as
542
+ * adapters, or behind `createEngineRunner` as provider profiles.
543
+ */
544
+ engines?: Partial<Record<ProfileEngine, EngineAdapter>>;
519
545
  };
520
546
  type EngineRunnerContext = {
521
547
  /** The session config, with profile defaults already applied. */config: SessionRunnerConfig; /** The profile that selected this engine. */
@@ -568,5 +594,119 @@ type WorkerServer = {
568
594
  };
569
595
  declare function createWorkerServer(options?: WorkerServerOptions): WorkerServer;
570
596
  //#endregion
571
- export { type Authenticator, BridgeHub, type BridgeHubOptions, type EngineRunnerContext, type FileSessionStoreOptions, MemorySessionStore, type ParkedSessionRecord, type ProfileStore, type QueueServerOptions, type SdkSessionLister, type SessionNotificationOptions, SessionNotifier, SessionParkManager, type SessionParkOptions, SessionRegistry, type SessionRegistryOptions, type SessionStore, type WorkerServer, type WorkerServerOptions, createFileProfileStore, createFileSessionStore, createMemoryProfileStore, createWorkerServer, toDurableRecord };
597
+ //#region src/attachments.d.ts
598
+ type AttachmentStoreOptions = {
599
+ /** Largest single upload. Default 10 MiB. */maxFileBytes?: number; /** Ceiling on everything one session is holding. Default 64 MiB. */
600
+ maxSessionBytes?: number;
601
+ };
602
+ type AttachmentRejection = {
603
+ code: 'too_large';
604
+ message: string;
605
+ } | {
606
+ code: 'session_full';
607
+ message: string;
608
+ } | {
609
+ code: 'unsupported_type';
610
+ message: string;
611
+ } | {
612
+ code: 'empty';
613
+ message: string;
614
+ };
615
+ type PutResult = {
616
+ ok: true;
617
+ attachment: MessageAttachment;
618
+ } | {
619
+ ok: false;
620
+ error: AttachmentRejection;
621
+ };
622
+ /**
623
+ * Per-session hold for files the user attached to a message.
624
+ *
625
+ * In memory, and deliberately so. An attachment is only *needed* for the instant
626
+ * between the upload and the message that names it; everything after that is
627
+ * convenience (a client re-rendering a thumbnail after a reattach). That is the
628
+ * same bargain `GET /sessions/:id/files` makes — the session's lifetime, no
629
+ * durability tier — and it keeps the gateway from accumulating a photo library
630
+ * on disk that nobody asked it to look after.
631
+ *
632
+ * Both caps are enforced here rather than at the route, so a host embedding the
633
+ * server cannot forget one: a single file that is too big is a 413, and so is a
634
+ * session whose total would go over.
635
+ */
636
+ declare class AttachmentStore {
637
+ #private;
638
+ constructor(options?: AttachmentStoreOptions);
639
+ get maxFileBytes(): number;
640
+ put(sessionId: string, name: string, mediaType: string, body: Buffer): PutResult;
641
+ /** The stored record, bytes included — for the download route and for the send
642
+ * path that turns ids into content blocks. */
643
+ get(sessionId: string, id: string): AttachmentInput | undefined;
644
+ /**
645
+ * Resolve the ids a `user_message` named, in the order given.
646
+ *
647
+ * Missing ids are reported rather than skipped: a message that quietly lost its
648
+ * picture reads as the model ignoring it, which is a far worse failure than a
649
+ * command that errors.
650
+ */
651
+ resolve(sessionId: string, ids: readonly string[]): {
652
+ ok: true;
653
+ attachments: AttachmentInput[];
654
+ } | {
655
+ ok: false;
656
+ missing: string[];
657
+ };
658
+ drop(sessionId: string): void;
659
+ }
660
+ //#endregion
661
+ //#region src/produced-files.d.ts
662
+ /** One host file an engine reported writing, as the store holds it. */
663
+ type ProducedFile = {
664
+ fileId: string; /** Absolute host path, exactly as the runner reported it. */
665
+ path: string;
666
+ mediaType?: string; /** Size when the runner announced it — advisory, and re-read at serve time. */
667
+ bytes?: number;
668
+ sessionId: string;
669
+ };
670
+ /**
671
+ * The paths this gateway will serve from `GET /sessions/:id/produced/:fileId`.
672
+ *
673
+ * **This is the whole access-control model, so it is worth being precise about
674
+ * what it is.** The store is an allowlist built from one source and one only:
675
+ * `file_produced` events, which a runner emits about a file its own engine just
676
+ * wrote. It is not a directory grant. Nothing else can add to it — not a
677
+ * request, not a config, and in particular not the agent, whose own path claims
678
+ * go through `/fs/*` and that route's root allowlist.
679
+ *
680
+ * That is why the route needs neither `hostFiles.roots` nor `maxFileBytes`:
681
+ * "somewhere under a root the operator declared" is a guess about which paths
682
+ * are safe, while "the exact path this session's runner reported producing" is
683
+ * a fact about one file. A 2 MB generated PNG is the common case, and making
684
+ * the operator raise a byte cap to see their own picture was the bug this
685
+ * replaces.
686
+ *
687
+ * Lifetime is the session's, like `AttachmentStore`'s: in memory, dropped when
688
+ * the session is removed. The bytes are never held here — only the path, so a
689
+ * gateway serving a long session accumulates a few hundred bytes per picture
690
+ * rather than the pictures.
691
+ */
692
+ declare class ProducedFileStore {
693
+ #private;
694
+ /**
695
+ * Register a runner's produced files for its lifetime.
696
+ *
697
+ * Subscribes from seq 0, which is the opposite of what `SessionNotifier` wants
698
+ * and correct for the same reason: registration is idempotent (a `fileId` is
699
+ * derived from its path, so re-registering overwrites with itself), and a
700
+ * session rebuilt from a park must re-learn every file it produced before the
701
+ * park — otherwise a client's transcript keeps rendering image cards whose
702
+ * bytes have quietly become unreachable.
703
+ */
704
+ watch(runner: Runner): void;
705
+ get(sessionId: string, fileId: string): ProducedFile | undefined;
706
+ /** Everything one session has produced, newest registration last. */
707
+ list(sessionId: string): ProducedFile[];
708
+ drop(sessionId: string): void;
709
+ }
710
+ //#endregion
711
+ export { AttachmentStore, type AttachmentStoreOptions, type Authenticator, BridgeHub, type BridgeHubOptions, type EngineRunnerContext, type FileSessionStoreOptions, MemorySessionStore, type ParkedSessionRecord, type ProducedFile, ProducedFileStore, type ProfileStore, type QueueServerOptions, type SdkSessionLister, type SessionNotificationOptions, SessionNotifier, SessionParkManager, type SessionParkOptions, SessionRegistry, type SessionRegistryOptions, type SessionStore, type WorkerServer, type WorkerServerOptions, createFileProfileStore, createFileSessionStore, createMemoryProfileStore, createWorkerServer, toDurableRecord };
572
712
  //# sourceMappingURL=index.d.mts.map