@workerdeck/server 3.4.0 → 3.5.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.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import { createHash, randomBytes, randomUUID } from "node:crypto";
2
2
  import { createServer } from "node:http";
3
3
  import { WebSocketServer } from "ws";
4
- import { BrowserBridgeExecutor, PEER_MESSAGE_MAX_CHARS, PEER_RECENT_DEFAULT, PEER_RECENT_MAX, SHELL_REFUSAL, SHELL_REFUSAL as SHELL_REFUSAL$1, SessionRunner, TTY_REDRAW_CARRY, agentMayWrite, attachmentKind, checkClaudeAuth, clampShellTail, contextResetDirectoryHandle, createEngineSession, encodeShellKeys, getEngineAdapter, installContextResetDirectory, installPeerDirectory, installShellDirectory, isSlashCommand, normalizeMediaType, peerDirectoryHandle, peerSummary, recentLines, shellDirectoryHandle, shellOwnershipRefusal, shellSummary, shellTail, shellToolNeedsCard, ttyRedraws, ttyText, withoutGatewaySecrets } from "@workerdeck/core";
4
+ import { BrowserBridgeExecutor, PEER_MESSAGE_MAX_CHARS, PEER_RECENT_DEFAULT, PEER_RECENT_MAX, SHELL_REFUSAL, SHELL_REFUSAL as SHELL_REFUSAL$1, SessionRunner, TTY_REDRAW_CARRY, agentMayWrite, attachmentKind, checkClaudeAuth, clampShellTail, composeInstructions, contextResetDirectoryHandle, createEngineSession, encodeShellKeys, getEngineAdapter, installContextResetDirectory, installPeerDirectory, installShellDirectory, isSlashCommand, normalizeMediaType, peerDirectoryHandle, peerSummary, recentLines, shellDirectoryHandle, shellOwnershipRefusal, shellSummary, shellTail, shellToolNeedsCard, ttyRedraws, ttyText, withoutGatewaySecrets } from "@workerdeck/core";
5
5
  import { JobQueue } from "@workerdeck/queue";
6
- import { ENGINE_CAPABILITIES, PEER_MENTION_MAX, PROTOCOL_VERSION, SHELL_ARTIFACT_MAX_BYTES, SHELL_ARTIFACT_TTL_MS, SHELL_ATTACH_REPLAY_BYTES, SHELL_COLS, SHELL_COMMAND_MAX, SHELL_INPUT_MAX, SHELL_LABEL_MAX, SHELL_LINGER_MS, SHELL_MAX_COLS, SHELL_MAX_ROWS, SHELL_MAX_RUNNING_PER_SESSION, SHELL_MAX_RUNNING_TOTAL, SHELL_MIN_COLS, SHELL_MIN_ROWS, SHELL_READ_MAX_LINES, SHELL_ROWS, SHELL_SPILL_BYTES, SHELL_TAIL_FLUSH_MS, SHELL_TAIL_RING_BYTES, SHELL_WAIT_DEFAULT_MS, SHELL_WAIT_MAX_MS, costOfByModel, imagePartRef, isJobRun, mergeByModel, mergePricing, peerMentionKey, peerMentionSlug, pickCreateSessionRequest, scanPeerMentions, sessionState, subscriptionComparison, supportsPermissionMode } from "@workerdeck/protocol";
6
+ import { AGENT_SLEEP_AFTER_MS_DEFAULT, ENGINE_CAPABILITIES, PEER_MENTION_MAX, PROTOCOL_VERSION, SHELL_ARTIFACT_MAX_BYTES, SHELL_ARTIFACT_TTL_MS, SHELL_ATTACH_REPLAY_BYTES, SHELL_COLS, SHELL_COMMAND_MAX, SHELL_INPUT_MAX, SHELL_LABEL_MAX, SHELL_LINGER_MS, SHELL_MAX_COLS, SHELL_MAX_ROWS, SHELL_MAX_RUNNING_PER_SESSION, SHELL_MAX_RUNNING_TOTAL, SHELL_MIN_COLS, SHELL_MIN_ROWS, SHELL_READ_MAX_LINES, SHELL_ROWS, SHELL_SPILL_BYTES, SHELL_TAIL_FLUSH_MS, SHELL_TAIL_RING_BYTES, SHELL_WAIT_DEFAULT_MS, SHELL_WAIT_MAX_MS, agentRef, costOfByModel, imagePartRef, isJobRun, mergeByModel, mergePricing, peerMentionKey, peerMentionSlug, pickCreateSessionRequest, scanPeerMentions, sessionState, subscriptionComparison, supportsPermissionMode, teamReaches } from "@workerdeck/protocol";
7
7
  import { chmodSync, closeSync, constants, createReadStream, existsSync, fstatSync, ftruncateSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
8
8
  import { arch, homedir, hostname, platform, tmpdir } from "node:os";
9
9
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
@@ -520,7 +520,10 @@ const DEFAULT_PER_MINUTE = 10;
520
520
  const DEFAULT_MAX_HOPS = 12;
521
521
  const WINDOW_MS = 6e4;
522
522
  function visible(from, to) {
523
- return to.id !== from.id && scopeMatches(from.scope, to.scope);
523
+ return to.id !== from.id && scopeMatches(from.scope, to.scope) && teamReaches(from.agent, to.agent);
524
+ }
525
+ function relayable(info, exposed) {
526
+ return info.status !== "closed" && info.agent?.lead === void 0 && scopeMatches(exposed, info.scope);
524
527
  }
525
528
  function relayEntry(info, live) {
526
529
  const items = info.checklist;
@@ -681,11 +684,11 @@ function createPeerService(deps) {
681
684
  };
682
685
  const exposedInfo = async (sessionId, exposed) => {
683
686
  const info = await infoOf(sessionId);
684
- return info && info.status !== "closed" && scopeMatches(exposed, info.scope) ? info : void 0;
687
+ return info && relayable(info, exposed) ? info : void 0;
685
688
  };
686
689
  const relayEntries = async (exposed) => {
687
690
  const live = new Set(registry().list().map((info) => info.id));
688
- return (await allSessions()).filter((info) => info.status !== "closed" && scopeMatches(exposed, info.scope)).map((info) => relayEntry(info, live.has(info.id)));
691
+ return (await allSessions()).filter((info) => relayable(info, exposed)).map((info) => relayEntry(info, live.has(info.id)));
689
692
  };
690
693
  const relayPeek = async (sessionId, recent, exposed) => {
691
694
  const info = await exposedInfo(sessionId, exposed);
@@ -743,8 +746,9 @@ function resolvePeerMentions(rows, text) {
743
746
  else byKey.set(key, [row]);
744
747
  };
745
748
  for (const row of rows) {
746
- add(peerMentionKey(peerMentionSlug(row.title, row.id)), row);
747
- add(peerMentionKey(row.id), row);
749
+ const keys = /* @__PURE__ */ new Set([peerMentionKey(peerMentionSlug(row.title, row.id)), peerMentionKey(row.id)]);
750
+ if (row.agent) keys.add(peerMentionKey(peerMentionSlug(row.agent, row.id)));
751
+ for (const key of keys) add(key, row);
748
752
  }
749
753
  const resolved = [];
750
754
  const seen = /* @__PURE__ */ new Set();
@@ -760,7 +764,7 @@ function resolvePeerMentions(rows, text) {
760
764
  resolved.push({
761
765
  typed: token.body,
762
766
  id: target.id,
763
- name: target.title,
767
+ name: target.agent ?? target.title,
764
768
  engine: target.engine,
765
769
  status: target.status,
766
770
  cwd: target.cwd,
@@ -2372,6 +2376,576 @@ function machineId() {
2372
2376
  return cached;
2373
2377
  }
2374
2378
  //#endregion
2379
+ //#region src/services/agents.ts
2380
+ const NAMES = [
2381
+ "Atlas",
2382
+ "Juno",
2383
+ "Pip",
2384
+ "Rook",
2385
+ "Marlow",
2386
+ "Orbit",
2387
+ "Fern",
2388
+ "Quill",
2389
+ "Sable",
2390
+ "Wren",
2391
+ "Ember",
2392
+ "Moss",
2393
+ "Nova",
2394
+ "Otto",
2395
+ "Pike",
2396
+ "Rune",
2397
+ "Sage",
2398
+ "Tansy",
2399
+ "Vale",
2400
+ "Yarrow",
2401
+ "Basil",
2402
+ "Cedar",
2403
+ "Dune",
2404
+ "Flint",
2405
+ "Hazel",
2406
+ "Iris",
2407
+ "Kestrel",
2408
+ "Lark",
2409
+ "Maple",
2410
+ "Nettle",
2411
+ "Onyx",
2412
+ "Piper",
2413
+ "Reed",
2414
+ "Sorrel",
2415
+ "Thistle",
2416
+ "Umber",
2417
+ "Willow",
2418
+ "Zephyr"
2419
+ ];
2420
+ const MAX_NAME = 64;
2421
+ const MAX_BRIEF = 16e3;
2422
+ const CONFIG_KEYS = [
2423
+ "cwd",
2424
+ "profile",
2425
+ "model",
2426
+ "reasoningEffort",
2427
+ "permissionMode",
2428
+ "brief",
2429
+ "agentContextReset",
2430
+ "sleepAfterMs"
2431
+ ];
2432
+ var AgentService = class {
2433
+ #store;
2434
+ #basePath;
2435
+ #avatars;
2436
+ #sleepAfterMs;
2437
+ #now;
2438
+ #agents = /* @__PURE__ */ new Map();
2439
+ constructor(options) {
2440
+ this.#store = options.store;
2441
+ this.#basePath = options.basePath;
2442
+ this.#avatars = options.avatars ?? false;
2443
+ this.#sleepAfterMs = options.sleepAfterMs ?? AGENT_SLEEP_AFTER_MS_DEFAULT;
2444
+ this.#now = options.now ?? Date.now;
2445
+ }
2446
+ async hydrate() {
2447
+ this.#agents = new Map((await this.#store.list()).map((agent) => [agent.id, agent]));
2448
+ }
2449
+ list() {
2450
+ return [...this.#agents.values()].map((agent) => this.public(agent));
2451
+ }
2452
+ get(id) {
2453
+ return this.#agents.get(id);
2454
+ }
2455
+ bySession(sessionId) {
2456
+ for (const agent of this.#agents.values()) if (agent.sessionId === sessionId) return agent;
2457
+ }
2458
+ decorate(info) {
2459
+ const agent = this.bySession(info.id);
2460
+ if (!agent) return info;
2461
+ const leadName = agent.lead === void 0 ? void 0 : this.#agents.get(agent.lead)?.name;
2462
+ return {
2463
+ ...info,
2464
+ agent: agentRef(this.public(agent), {
2465
+ leadName,
2466
+ leads: this.hasMembers(agent.id)
2467
+ })
2468
+ };
2469
+ }
2470
+ briefFor(sessionId) {
2471
+ return (sessionId === void 0 ? void 0 : this.bySession(sessionId)?.config.brief?.trim()) || void 0;
2472
+ }
2473
+ sleepAfterFor(sessionId) {
2474
+ const agent = this.bySession(sessionId);
2475
+ return agent ? agent.config.sleepAfterMs ?? this.#sleepAfterMs : void 0;
2476
+ }
2477
+ public(agent) {
2478
+ const { avatarSeed: _seed, avatarRecipe: _recipe, avatar: _avatar, ...info } = agent;
2479
+ return this.#avatars ? {
2480
+ ...info,
2481
+ avatar: `${this.#basePath}/agents/${agent.id}/avatar.png`
2482
+ } : info;
2483
+ }
2484
+ draft(input) {
2485
+ const config = readConfig(input.config);
2486
+ if ("error" in config) return config;
2487
+ const name = input.name === void 0 ? this.#suggestName() : readName(input.name);
2488
+ if (typeof name !== "string") return name;
2489
+ const id = randomUUID();
2490
+ const at = this.#now();
2491
+ const agent = {
2492
+ id,
2493
+ name,
2494
+ createdAt: at,
2495
+ updatedAt: at,
2496
+ avatarSeed: id,
2497
+ pastSessions: [],
2498
+ config
2499
+ };
2500
+ if (input.lead !== void 0 && input.lead !== null) {
2501
+ const refused = typeof input.lead === "string" ? this.leadRefusal(agent, input.lead) : {
2502
+ status: 400,
2503
+ error: "lead must be an agent id"
2504
+ };
2505
+ if (refused) return refused;
2506
+ agent.lead = input.lead;
2507
+ }
2508
+ return agent;
2509
+ }
2510
+ async save(agent) {
2511
+ const stored = {
2512
+ ...agent,
2513
+ updatedAt: this.#now()
2514
+ };
2515
+ await this.#store.save(stored);
2516
+ this.#agents.set(stored.id, stored);
2517
+ return stored;
2518
+ }
2519
+ async bind(agent, sessionId) {
2520
+ const past = agent.sessionId !== void 0 && agent.sessionId !== sessionId ? [...agent.pastSessions, agent.sessionId] : agent.pastSessions;
2521
+ return this.save({
2522
+ ...agent,
2523
+ sessionId,
2524
+ pastSessions: past
2525
+ });
2526
+ }
2527
+ hasMembers(id) {
2528
+ for (const agent of this.#agents.values()) if (agent.lead === id) return true;
2529
+ return false;
2530
+ }
2531
+ members(id) {
2532
+ return [...this.#agents.values()].filter((agent) => agent.lead === id).sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
2533
+ }
2534
+ leadRefusal(mover, leadId) {
2535
+ if (leadId === mover.id) return {
2536
+ status: 409,
2537
+ error: "an agent cannot lead its own team"
2538
+ };
2539
+ const lead = this.#agents.get(leadId);
2540
+ if (!lead) return {
2541
+ status: 404,
2542
+ error: `no such agent: ${leadId}`
2543
+ };
2544
+ if (lead.lead !== void 0) return {
2545
+ status: 409,
2546
+ error: `${lead.name} is a member of a team; teams are one level deep`
2547
+ };
2548
+ if (this.hasMembers(mover.id)) return {
2549
+ status: 409,
2550
+ error: `${mover.name} leads a team; teams are one level deep`
2551
+ };
2552
+ return null;
2553
+ }
2554
+ async update(id, patch) {
2555
+ const agent = this.#agents.get(id);
2556
+ if (!agent) return {
2557
+ status: 404,
2558
+ error: `no such agent: ${id}`
2559
+ };
2560
+ const next = { ...agent };
2561
+ if (patch.name !== void 0) {
2562
+ const name = readName(patch.name);
2563
+ if (typeof name !== "string") return name;
2564
+ next.name = name;
2565
+ }
2566
+ if (patch.config !== void 0) {
2567
+ if (!isRecord$1(patch.config)) return {
2568
+ status: 400,
2569
+ error: "config must be an object"
2570
+ };
2571
+ const config = readConfig({
2572
+ ...agent.config,
2573
+ ...patch.config
2574
+ });
2575
+ if ("error" in config) return config;
2576
+ next.config = config;
2577
+ }
2578
+ if (patch.lead === null) delete next.lead;
2579
+ else if (patch.lead !== void 0) {
2580
+ if (typeof patch.lead !== "string") return {
2581
+ status: 400,
2582
+ error: "lead must be an agent id or null"
2583
+ };
2584
+ const refused = this.leadRefusal(agent, patch.lead);
2585
+ if (refused) return refused;
2586
+ next.lead = patch.lead;
2587
+ }
2588
+ if (patch.order !== void 0) {
2589
+ if (typeof patch.order !== "number" || !Number.isFinite(patch.order)) return {
2590
+ status: 400,
2591
+ error: "order must be a number"
2592
+ };
2593
+ next.order = patch.order;
2594
+ }
2595
+ return this.save(next);
2596
+ }
2597
+ async retire(id, members) {
2598
+ const agent = this.#agents.get(id);
2599
+ if (!agent) return {
2600
+ status: 404,
2601
+ error: `no such agent: ${id}`
2602
+ };
2603
+ const crew = this.members(id);
2604
+ const retired = [agent, ...members === "retire" ? crew : []];
2605
+ const released = [];
2606
+ if (members === "release") for (const member of crew) {
2607
+ const { lead: _lead, ...rest } = member;
2608
+ released.push(await this.save(rest));
2609
+ }
2610
+ for (const gone of retired) {
2611
+ await this.#store.delete(gone.id);
2612
+ this.#agents.delete(gone.id);
2613
+ }
2614
+ return {
2615
+ retired,
2616
+ released
2617
+ };
2618
+ }
2619
+ #suggestName() {
2620
+ const taken = new Set([...this.#agents.values()].map((agent) => agent.name.toLowerCase()));
2621
+ const free = NAMES.find((name) => !taken.has(name.toLowerCase()));
2622
+ if (free) return free;
2623
+ for (let n = 2;; n++) {
2624
+ const name = `${NAMES[n % NAMES.length]} ${n}`;
2625
+ if (!taken.has(name.toLowerCase())) return name;
2626
+ }
2627
+ }
2628
+ };
2629
+ function isAgentRefusal(value) {
2630
+ return isRecord$1(value) && typeof value.status === "number" && typeof value.error === "string";
2631
+ }
2632
+ function readName(value) {
2633
+ if (typeof value !== "string" || value.trim() === "") return {
2634
+ status: 400,
2635
+ error: "name must be a non-empty string"
2636
+ };
2637
+ const name = value.trim();
2638
+ return name.length > MAX_NAME ? {
2639
+ status: 400,
2640
+ error: `name is longer than ${MAX_NAME} characters`
2641
+ } : name;
2642
+ }
2643
+ function readConfig(value) {
2644
+ if (value === void 0) return {};
2645
+ if (!isRecord$1(value)) return {
2646
+ status: 400,
2647
+ error: "config must be an object"
2648
+ };
2649
+ const config = {};
2650
+ for (const key of CONFIG_KEYS) if (value[key] !== void 0) config[key] = value[key];
2651
+ for (const key of [
2652
+ "cwd",
2653
+ "profile",
2654
+ "model",
2655
+ "reasoningEffort",
2656
+ "permissionMode",
2657
+ "brief"
2658
+ ]) if (config[key] !== void 0 && typeof config[key] !== "string") return {
2659
+ status: 400,
2660
+ error: `config.${key} must be a string`
2661
+ };
2662
+ if (config.agentContextReset !== void 0 && typeof config.agentContextReset !== "boolean") return {
2663
+ status: 400,
2664
+ error: "config.agentContextReset must be a boolean"
2665
+ };
2666
+ const sleep = config.sleepAfterMs;
2667
+ if (sleep !== void 0 && (typeof sleep !== "number" || !Number.isFinite(sleep) || sleep < 0)) return {
2668
+ status: 400,
2669
+ error: "config.sleepAfterMs must be a non-negative number"
2670
+ };
2671
+ if (typeof config.brief === "string" && config.brief.length > MAX_BRIEF) return {
2672
+ status: 400,
2673
+ error: `config.brief is longer than ${MAX_BRIEF} characters`
2674
+ };
2675
+ return config;
2676
+ }
2677
+ function isRecord$1(value) {
2678
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2679
+ }
2680
+ //#endregion
2681
+ //#region src/lib/host-only-keys.ts
2682
+ const HOST_ONLY_KEY_TABLE = {
2683
+ epoch: "durable",
2684
+ pricing: "transient",
2685
+ queryFn: "transient",
2686
+ env: "transient",
2687
+ pathToClaudeCodeExecutable: "durable",
2688
+ extraOptions: "transient",
2689
+ instructions: "transient",
2690
+ defaultApprovalTimeoutMs: "durable",
2691
+ backfillHistory: "durable",
2692
+ historyFn: "transient",
2693
+ sessionInfoFn: "transient",
2694
+ peers: "transient",
2695
+ contextReset: "transient",
2696
+ shells: "transient",
2697
+ shellAgentWrite: "transient",
2698
+ createdByOperator: "durable",
2699
+ fallbackTitle: "transient",
2700
+ startAsleep: "transient",
2701
+ effortDefaults: "transient",
2702
+ connectFn: "transient",
2703
+ codexHome: "durable",
2704
+ codexPathOverride: "durable",
2705
+ languageModel: "transient",
2706
+ tools: "transient",
2707
+ maxSteps: "durable",
2708
+ executor: "transient",
2709
+ executableTools: "transient",
2710
+ vfs: "transient",
2711
+ executionLimits: "durable",
2712
+ executionBackend: "transient",
2713
+ toolTitles: "transient",
2714
+ shouldApprove: "transient",
2715
+ resolveModel: "transient",
2716
+ contextWindow: "transient",
2717
+ reportMcpServers: "transient",
2718
+ onClose: "transient",
2719
+ restore: "transient"
2720
+ };
2721
+ const HOST_ONLY_KEYS = new Set(Object.keys(HOST_ONLY_KEY_TABLE));
2722
+ const DURABLE_HOST_KEYS = Object.entries(HOST_ONLY_KEY_TABLE).filter(([, lifetime]) => lifetime === "durable").map(([key]) => key);
2723
+ //#endregion
2724
+ //#region src/routes/create-vet.ts
2725
+ function vetCreateRequest(ctx, body, auth) {
2726
+ if (typeof body !== "object" || body === null || Array.isArray(body)) return {
2727
+ ok: false,
2728
+ status: 400,
2729
+ error: "request body must be a JSON object"
2730
+ };
2731
+ const hostOnly = Object.keys(body).filter((key) => HOST_ONLY_KEYS.has(key));
2732
+ if (hostOnly.length > 0) return {
2733
+ ok: false,
2734
+ status: 400,
2735
+ error: `host-only runner config is not accepted on a session request: ${hostOnly.join(", ")} (set it from the gateway's buildRunnerConfig hook)`
2736
+ };
2737
+ const req = pickCreateSessionRequest(body);
2738
+ const { availability, factory } = ctx;
2739
+ const early = factory.applyScope(req, auth) ?? refusal(403, factory.applyBypassPolicy(req));
2740
+ if (early) return {
2741
+ ok: false,
2742
+ ...early
2743
+ };
2744
+ const resolved = factory.resolveProfile(req.profile, auth.allowedProfiles);
2745
+ if (!resolved.ok) return resolved;
2746
+ const refused = refusal(403, refuseHostAuthority(ctx, req, resolved.profile, auth)) ?? availability.checkAvailable(resolved.profile) ?? factory.checkCwd(req, resolved.profile) ?? refusal(400, factory.checkPermissionMode(req.permissionMode, resolved.profile) ?? factory.checkEngineGrants(req, resolved.profile));
2747
+ if (refused) return {
2748
+ ok: false,
2749
+ ...refused
2750
+ };
2751
+ factory.stripInertFields(req, resolved.profile);
2752
+ req.profile = resolved.profile?.name;
2753
+ return {
2754
+ ok: true,
2755
+ request: req
2756
+ };
2757
+ }
2758
+ function refusal(status, error) {
2759
+ return error === null ? null : {
2760
+ status,
2761
+ error
2762
+ };
2763
+ }
2764
+ function refuseHostAuthority(ctx, req, profile, auth) {
2765
+ if (ctx.auth.isOperator(auth)) return null;
2766
+ const engine = engineOf(profile);
2767
+ const name = profile?.name ?? "default";
2768
+ if ((engine === "claude" || engine === "codex") && !auth.allowedProfiles?.includes(name)) return `profile '${name}' runs the ${engine} engine on the host; a non-operator caller needs it named in its allowedProfiles`;
2769
+ const mode = refusePermissionMode(req.permissionMode, { operator: false });
2770
+ if (mode) return mode;
2771
+ if (req.allowDangerouslySkipPermissions) return "allowDangerouslySkipPermissions is reserved to operators";
2772
+ if (req.settingSources !== void 0 && req.settingSources.length > 0) return "settingSources is reserved to operators: it loads hooks and settings from the host";
2773
+ if (!(typeof req.mcpServers === "object" && req.mcpServers !== null ? Object.values(req.mcpServers) : []).every(isRemoteMcpServer)) return "a stdio MCP server is reserved to operators: it runs a command on the host";
2774
+ if (req.resume !== void 0 && !ownsSdkSession(ctx, auth, req.resume)) return "resume is limited to a live session this caller can see";
2775
+ return null;
2776
+ }
2777
+ function ownsSdkSession(ctx, auth, sdkSessionId) {
2778
+ return ctx.registry.list().some((info) => info.sdkSessionId === sdkSessionId && ctx.auth.canSee(auth, info));
2779
+ }
2780
+ function isRemoteMcpServer(server) {
2781
+ const type = server?.type;
2782
+ return type === "http" || type === "sse";
2783
+ }
2784
+ //#endregion
2785
+ //#region src/routes/agents.ts
2786
+ async function handleAgents(ctx, req, res, pathname, auth) {
2787
+ const agents = ctx.agents;
2788
+ const rest = pathname.slice((ctx.basePath + "/agents").length).replace(/^\//, "");
2789
+ if (rest === "") {
2790
+ requireMethod(req, "GET", "POST");
2791
+ if (req.method === "GET") {
2792
+ json(res, 200, { agents: agents.list() });
2793
+ return;
2794
+ }
2795
+ await createAgent(ctx, agents, req, res, auth);
2796
+ return;
2797
+ }
2798
+ const [id, action, ...extra] = rest.split("/").map(decodeURIComponent);
2799
+ const agent = id === void 0 ? void 0 : agents.get(id);
2800
+ if (!agent || extra.length > 0) fail(404, "agent not found");
2801
+ if (action === "restart") {
2802
+ requireMethod(req, "POST");
2803
+ const body = await readJsonBody(req, ctx.maxBodyBytes) ?? {};
2804
+ const previous = agent.sessionId === void 0 ? void 0 : ctx.registry.get(agent.sessionId);
2805
+ const created = await startSession(ctx, agent, typeof body.prompt === "string" ? body.prompt : void 0, auth);
2806
+ const status = previous?.info().status;
2807
+ if (previous && status !== "closed" && status !== "failed") previous.close("server");
2808
+ await respond(ctx, res, 200, await bindWithAvatar(ctx, agent, created), created);
2809
+ return;
2810
+ }
2811
+ if (action === "avatar.png" || action === "avatar-busy.png") {
2812
+ requireMethod(req, "GET");
2813
+ await sendAvatar(ctx, req, res, agent, action === "avatar-busy.png");
2814
+ return;
2815
+ }
2816
+ if (action !== void 0) fail(404, "agent not found");
2817
+ requireMethod(req, "GET", "PATCH", "DELETE");
2818
+ if (req.method === "GET") {
2819
+ await respond(ctx, res, 200, agent);
2820
+ return;
2821
+ }
2822
+ if (req.method === "PATCH") {
2823
+ const patch = await readJsonBody(req, ctx.maxBodyBytes);
2824
+ const updated = await agents.update(agent.id, isRecord(patch) ? patch : {});
2825
+ if (isAgentRefusal(updated)) fail(updated.status, updated.error);
2826
+ await respond(ctx, res, 200, updated);
2827
+ return;
2828
+ }
2829
+ const members = (await readJsonBody(req, ctx.maxBodyBytes) ?? {}).members === "retire" ? "retire" : "release";
2830
+ const outcome = await agents.retire(agent.id, members);
2831
+ if (isAgentRefusal(outcome)) fail(outcome.status, outcome.error);
2832
+ for (const gone of outcome.retired) {
2833
+ const runner = gone.sessionId === void 0 ? void 0 : ctx.registry.get(gone.sessionId);
2834
+ const status = runner?.info().status;
2835
+ if (runner && status !== "closed" && status !== "failed") runner.close("client");
2836
+ }
2837
+ json(res, 200, {
2838
+ retired: outcome.retired.map((a) => a.id),
2839
+ released: outcome.released.map((a) => a.id)
2840
+ });
2841
+ }
2842
+ async function createAgent(ctx, agents, req, res, auth) {
2843
+ const body = await readJsonBody(req, ctx.maxBodyBytes);
2844
+ if (!isRecord(body)) fail(400, "request body must be a JSON object");
2845
+ if (body.adopt !== void 0) {
2846
+ await adoptSession(ctx, agents, res, body);
2847
+ return;
2848
+ }
2849
+ const draft = agents.draft(body);
2850
+ if (isAgentRefusal(draft)) fail(draft.status, draft.error);
2851
+ const created = await startSession(ctx, draft, typeof body.prompt === "string" ? body.prompt : void 0, auth);
2852
+ await respond(ctx, res, 201, await bindWithAvatar(ctx, draft, created), created);
2853
+ }
2854
+ async function adoptSession(ctx, agents, res, body) {
2855
+ if (typeof body.adopt !== "string") fail(400, "adopt must be a session id");
2856
+ const info = await sessionInfoOf(ctx, body.adopt);
2857
+ if (!info) fail(404, "session not found");
2858
+ if (agents.bySession(info.id)) fail(409, "that session already belongs to an agent");
2859
+ const draft = agents.draft({
2860
+ name: body.name ?? info.title,
2861
+ config: {
2862
+ ...configOf(info),
2863
+ ...body.config
2864
+ },
2865
+ lead: body.lead
2866
+ });
2867
+ if (isAgentRefusal(draft)) fail(draft.status, draft.error);
2868
+ await respond(ctx, res, 201, await bindWithAvatar(ctx, draft, info));
2869
+ }
2870
+ async function bindWithAvatar(ctx, agent, session) {
2871
+ const recipe = agent.avatarRecipe ?? await rollAvatar(ctx, agent, session);
2872
+ return ctx.agents.bind(recipe === void 0 ? agent : {
2873
+ ...agent,
2874
+ avatarRecipe: recipe
2875
+ }, session.id);
2876
+ }
2877
+ async function rollAvatar(ctx, agent, session) {
2878
+ if (!ctx.avatars) return;
2879
+ const project = session ? ctx.projects.withProject(session).project?.root ?? session.cwd : void 0;
2880
+ try {
2881
+ return await ctx.avatars.roll(agent.avatarSeed, session?.engine, project || void 0);
2882
+ } catch {
2883
+ return;
2884
+ }
2885
+ }
2886
+ async function sendAvatar(ctx, req, res, agent, busy) {
2887
+ const avatars = ctx.avatars;
2888
+ if (!avatars) fail(404, "this gateway draws no avatars");
2889
+ let recipe = agent.avatarRecipe;
2890
+ if (recipe === void 0) {
2891
+ recipe = await rollAvatar(ctx, agent, agent.sessionId === void 0 ? void 0 : await sessionInfoOf(ctx, agent.sessionId));
2892
+ if (recipe === void 0) fail(503, "the avatar pack could not be loaded");
2893
+ await ctx.agents.save({
2894
+ ...agent,
2895
+ avatarRecipe: recipe
2896
+ });
2897
+ }
2898
+ const image = busy ? await avatars.busy(recipe) : await avatars.still(recipe);
2899
+ if (!image) fail(404, "this avatar pack has no busy animation");
2900
+ const headers = {
2901
+ "content-type": "image/png",
2902
+ etag: image.etag,
2903
+ "cache-control": "private, max-age=86400"
2904
+ };
2905
+ if (image.durations) headers["x-frame-durations"] = image.durations.join(",");
2906
+ if (req.headers["if-none-match"] === image.etag) {
2907
+ res.writeHead(304, headers).end();
2908
+ return;
2909
+ }
2910
+ res.writeHead(200, headers).end(Buffer.from(image.png));
2911
+ }
2912
+ async function startSession(ctx, agent, prompt, auth) {
2913
+ const { config } = agent;
2914
+ const vetted = vetCreateRequest(ctx, stripUndefined({
2915
+ cwd: config.cwd,
2916
+ profile: config.profile,
2917
+ model: config.model,
2918
+ reasoningEffort: config.reasoningEffort,
2919
+ permissionMode: config.permissionMode,
2920
+ allowDangerouslySkipPermissions: config.permissionMode === "bypassPermissions" ? true : void 0,
2921
+ agentContextReset: config.agentContextReset ?? true,
2922
+ prompt
2923
+ }), auth);
2924
+ if (!vetted.ok) fail(vetted.status, vetted.error);
2925
+ return (await ctx.factory.createRunner(ctx.factory.buildRunnerConfig(vetted.request, { operator: ctx.auth.isOperator(auth) }), { brief: config.brief?.trim() || void 0 })).info();
2926
+ }
2927
+ async function respond(ctx, res, status, agent, session) {
2928
+ const info = session ?? (agent.sessionId === void 0 ? void 0 : await sessionInfoOf(ctx, agent.sessionId));
2929
+ const body = { agent: ctx.agents.public(agent) };
2930
+ if (info) body.session = ctx.projects.withProject(info);
2931
+ json(res, status, body);
2932
+ }
2933
+ function configOf(info) {
2934
+ return stripUndefined({
2935
+ cwd: info.cwd || void 0,
2936
+ profile: info.profile,
2937
+ model: info.model,
2938
+ reasoningEffort: info.effort ?? void 0,
2939
+ permissionMode: info.permissionMode
2940
+ });
2941
+ }
2942
+ function stripUndefined(value) {
2943
+ return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== void 0));
2944
+ }
2945
+ function isRecord(value) {
2946
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2947
+ }
2948
+ //#endregion
2375
2949
  //#region src/routes/executions.ts
2376
2950
  async function handleExecutionResult(ctx, req, res, pathname, auth) {
2377
2951
  const { auth: authSvc, basePath, parking } = ctx;
@@ -2819,110 +3393,6 @@ async function handleWrite({ ctx, hostFiles, req, res }) {
2819
3393
  });
2820
3394
  }
2821
3395
  //#endregion
2822
- //#region src/lib/host-only-keys.ts
2823
- const HOST_ONLY_KEY_TABLE = {
2824
- epoch: "durable",
2825
- pricing: "transient",
2826
- queryFn: "transient",
2827
- env: "transient",
2828
- pathToClaudeCodeExecutable: "durable",
2829
- extraOptions: "transient",
2830
- instructions: "transient",
2831
- defaultApprovalTimeoutMs: "durable",
2832
- backfillHistory: "durable",
2833
- historyFn: "transient",
2834
- sessionInfoFn: "transient",
2835
- peers: "transient",
2836
- contextReset: "transient",
2837
- shells: "transient",
2838
- shellAgentWrite: "transient",
2839
- createdByOperator: "durable",
2840
- fallbackTitle: "transient",
2841
- startAsleep: "transient",
2842
- effortDefaults: "transient",
2843
- connectFn: "transient",
2844
- codexHome: "durable",
2845
- codexPathOverride: "durable",
2846
- languageModel: "transient",
2847
- tools: "transient",
2848
- maxSteps: "durable",
2849
- executor: "transient",
2850
- executableTools: "transient",
2851
- vfs: "transient",
2852
- executionLimits: "durable",
2853
- executionBackend: "transient",
2854
- toolTitles: "transient",
2855
- shouldApprove: "transient",
2856
- resolveModel: "transient",
2857
- contextWindow: "transient",
2858
- reportMcpServers: "transient",
2859
- onClose: "transient",
2860
- restore: "transient"
2861
- };
2862
- const HOST_ONLY_KEYS = new Set(Object.keys(HOST_ONLY_KEY_TABLE));
2863
- const DURABLE_HOST_KEYS = Object.entries(HOST_ONLY_KEY_TABLE).filter(([, lifetime]) => lifetime === "durable").map(([key]) => key);
2864
- //#endregion
2865
- //#region src/routes/create-vet.ts
2866
- function vetCreateRequest(ctx, body, auth) {
2867
- if (typeof body !== "object" || body === null || Array.isArray(body)) return {
2868
- ok: false,
2869
- status: 400,
2870
- error: "request body must be a JSON object"
2871
- };
2872
- const hostOnly = Object.keys(body).filter((key) => HOST_ONLY_KEYS.has(key));
2873
- if (hostOnly.length > 0) return {
2874
- ok: false,
2875
- status: 400,
2876
- error: `host-only runner config is not accepted on a session request: ${hostOnly.join(", ")} (set it from the gateway's buildRunnerConfig hook)`
2877
- };
2878
- const req = pickCreateSessionRequest(body);
2879
- const { availability, factory } = ctx;
2880
- const early = factory.applyScope(req, auth) ?? refusal(403, factory.applyBypassPolicy(req));
2881
- if (early) return {
2882
- ok: false,
2883
- ...early
2884
- };
2885
- const resolved = factory.resolveProfile(req.profile, auth.allowedProfiles);
2886
- if (!resolved.ok) return resolved;
2887
- const refused = refusal(403, refuseHostAuthority(ctx, req, resolved.profile, auth)) ?? availability.checkAvailable(resolved.profile) ?? factory.checkCwd(req, resolved.profile) ?? refusal(400, factory.checkPermissionMode(req.permissionMode, resolved.profile) ?? factory.checkEngineGrants(req, resolved.profile));
2888
- if (refused) return {
2889
- ok: false,
2890
- ...refused
2891
- };
2892
- factory.stripInertFields(req, resolved.profile);
2893
- req.profile = resolved.profile?.name;
2894
- return {
2895
- ok: true,
2896
- request: req
2897
- };
2898
- }
2899
- function refusal(status, error) {
2900
- return error === null ? null : {
2901
- status,
2902
- error
2903
- };
2904
- }
2905
- function refuseHostAuthority(ctx, req, profile, auth) {
2906
- if (ctx.auth.isOperator(auth)) return null;
2907
- const engine = engineOf(profile);
2908
- const name = profile?.name ?? "default";
2909
- if ((engine === "claude" || engine === "codex") && !auth.allowedProfiles?.includes(name)) return `profile '${name}' runs the ${engine} engine on the host; a non-operator caller needs it named in its allowedProfiles`;
2910
- const mode = refusePermissionMode(req.permissionMode, { operator: false });
2911
- if (mode) return mode;
2912
- if (req.allowDangerouslySkipPermissions) return "allowDangerouslySkipPermissions is reserved to operators";
2913
- if (req.settingSources !== void 0 && req.settingSources.length > 0) return "settingSources is reserved to operators: it loads hooks and settings from the host";
2914
- if (!(typeof req.mcpServers === "object" && req.mcpServers !== null ? Object.values(req.mcpServers) : []).every(isRemoteMcpServer)) return "a stdio MCP server is reserved to operators: it runs a command on the host";
2915
- if (req.resume !== void 0 && !ownsSdkSession(ctx, auth, req.resume)) return "resume is limited to a live session this caller can see";
2916
- return null;
2917
- }
2918
- function ownsSdkSession(ctx, auth, sdkSessionId) {
2919
- return ctx.registry.list().some((info) => info.sdkSessionId === sdkSessionId && ctx.auth.canSee(auth, info));
2920
- }
2921
- function isRemoteMcpServer(server) {
2922
- const type = server?.type;
2923
- return type === "http" || type === "sse";
2924
- }
2925
- //#endregion
2926
3396
  //#region src/routes/jobs.ts
2927
3397
  async function handleJobs(ctx, req, res, pathname, auth) {
2928
3398
  const { auth: authSvc, basePath, queue } = ctx;
@@ -3757,6 +4227,11 @@ function httpRoutes(ctx) {
3757
4227
  auth: "any",
3758
4228
  handler: (req, res, pathname, auth) => handleProfiles(ctx, req, res, pathname, auth)
3759
4229
  }),
4230
+ route({
4231
+ match: pathWhere(under("/agents")),
4232
+ auth: "operator",
4233
+ handler: (req, res, pathname, auth) => handleAgents(ctx, req, res, pathname, auth)
4234
+ }),
3760
4235
  route({
3761
4236
  match: pathWhere((pathname) => pathname.startsWith(base + "/executions/")),
3762
4237
  auth: "any",
@@ -3824,6 +4299,36 @@ function pathWhere(test) {
3824
4299
  return (pathname) => test(pathname) ? pathname : void 0;
3825
4300
  }
3826
4301
  //#endregion
4302
+ //#region src/services/agent-store.ts
4303
+ function createMemoryAgentStore(seed = []) {
4304
+ const agents = new Map(seed.map((a) => [a.id, a]));
4305
+ return {
4306
+ list: () => [...agents.values()],
4307
+ save: (agent) => void agents.set(agent.id, agent),
4308
+ delete: (id) => void agents.delete(id)
4309
+ };
4310
+ }
4311
+ function createFileAgentStore(path = join(process.cwd(), ".workerdeck", "agents.json")) {
4312
+ const read = () => {
4313
+ const parsed = readJsonOrSync(path, []);
4314
+ if (!Array.isArray(parsed)) return /* @__PURE__ */ new Map();
4315
+ return new Map(parsed.filter((a) => a && typeof a.id === "string" && typeof a.name === "string").map((a) => [a.id, a]));
4316
+ };
4317
+ const write = (agents) => writeJsonAtomicSync(path, [...agents.values()], { indent: 2 });
4318
+ return {
4319
+ list: () => [...read().values()],
4320
+ save: (agent) => {
4321
+ const agents = read();
4322
+ agents.set(agent.id, agent);
4323
+ write(agents);
4324
+ },
4325
+ delete: (id) => {
4326
+ const agents = read();
4327
+ if (agents.delete(id)) write(agents);
4328
+ }
4329
+ };
4330
+ }
4331
+ //#endregion
3827
4332
  //#region src/services/attachments.ts
3828
4333
  const DEFAULT_MAX_FILE_BYTES = 10485760;
3829
4334
  const DEFAULT_MAX_SESSION_BYTES = 67108864;
@@ -4044,7 +4549,7 @@ var EngineSleepTimers = class {
4044
4549
  return this.#options.afterMs > 0 && !this.#closed;
4045
4550
  }
4046
4551
  watch(runner) {
4047
- if (!this.enabled || !runner.sleep) return;
4552
+ if (this.#closed || !runner.sleep || !this.enabled && !this.#options.afterMsFor) return;
4048
4553
  this.#runners.set(runner.id, runner);
4049
4554
  const unsubscribe = runner.subscribe((event) => {
4050
4555
  if (event.type === "status_changed") this.#rearm(runner);
@@ -4066,14 +4571,18 @@ var EngineSleepTimers = class {
4066
4571
  this.#timers.clear();
4067
4572
  this.#runners.clear();
4068
4573
  }
4574
+ #afterMs(sessionId) {
4575
+ return this.#closed ? 0 : this.#options.afterMsFor?.(sessionId) ?? this.#options.afterMs;
4576
+ }
4069
4577
  #rearm(runner) {
4070
4578
  this.#disarm(runner.id);
4071
- if (!this.enabled || !this.#sleepable(runner)) return;
4579
+ const afterMs = this.#afterMs(runner.id);
4580
+ if (afterMs <= 0 || !this.#sleepable(runner)) return;
4072
4581
  const timer = setTimeout(() => {
4073
4582
  this.#timers.delete(runner.id);
4074
- if (!this.enabled || !this.#sleepable(runner)) return;
4583
+ if (this.#afterMs(runner.id) <= 0 || !this.#sleepable(runner)) return;
4075
4584
  runner.sleep?.().catch((error) => this.#options.onError?.(error, runner.id));
4076
- }, this.#options.afterMs);
4585
+ }, afterMs);
4077
4586
  timer.unref?.();
4078
4587
  this.#timers.set(runner.id, timer);
4079
4588
  }
@@ -5229,7 +5738,7 @@ function createRelayLink(options, peers, log) {
5229
5738
  const remoteTarget = parseRelayPeerId;
5230
5739
  const reachesRemote = async (from) => {
5231
5740
  const me = await peers.relaySender(from);
5232
- return me.scope === void 0 || Object.keys(me.scope).length === 0;
5741
+ return (me.scope === void 0 || Object.keys(me.scope).length === 0) && me.agent?.lead === void 0;
5233
5742
  };
5234
5743
  return {
5235
5744
  directory: {
@@ -5507,7 +6016,7 @@ function createSessionFactory(deps) {
5507
6016
  return adapterFor(profile.engine).sessionEnv?.(profile, process.env) ?? process.env;
5508
6017
  }
5509
6018
  };
5510
- const buildRunner = async (built, restore, id) => {
6019
+ const buildRunner = async (built, restore, id, options) => {
5511
6020
  const name = built.profile;
5512
6021
  const profile = name !== void 0 ? profiles.get(name) : void 0;
5513
6022
  if (name !== void 0 && !profile) throw new Error(`unknown profile: ${name}`);
@@ -5532,6 +6041,8 @@ function createSessionFactory(deps) {
5532
6041
  const write = deps.shellAgentWrite ?? "read-only";
5533
6042
  if (write !== "read-only" && built.createdByOperator === true) config.shellAgentWrite = write;
5534
6043
  }
6044
+ const brief = options?.brief ?? deps.agentBrief?.(id ?? restore?.id);
6045
+ if (brief) config.instructions = composeInstructions(config.instructions, brief);
5535
6046
  if (config.instructions !== void 0 && capabilities.systemInstructions === false) throw new Error(`the ${engineOf(profile)} engine cannot deliver system instructions`);
5536
6047
  const runner = profile && isProviderProfile(profile) ? await deps.createEngineRunner({
5537
6048
  config,
@@ -5549,8 +6060,8 @@ function createSessionFactory(deps) {
5549
6060
  if (!sameScope(reported, config.scope)) throw new Error(`runner for session ${runner.id} reports scope ${JSON.stringify(reported)}, expected ${JSON.stringify(config.scope)} - echo config.scope from info()`);
5550
6061
  return runner;
5551
6062
  };
5552
- const createRunner = async (config) => {
5553
- const runner = registry.register(await buildRunner(config));
6063
+ const createRunner = async (config, options) => {
6064
+ const runner = registry.register(await buildRunner(config, void 0, void 0, options));
5554
6065
  parking.remember(runner.id, config);
5555
6066
  parking.watch(runner);
5556
6067
  runner.start();
@@ -5669,7 +6180,13 @@ function createWorkerServer(options = {}) {
5669
6180
  }
5670
6181
  const generation = randomUUID();
5671
6182
  const shells = options.shell?.enabled === true ? shellRegistryFor(options.shell, generation) : null;
5672
- const projects = new ProjectInfoService({ decorate: (info) => shells ? shells.decorate(info) : info });
6183
+ const agents = new AgentService({
6184
+ store: options.agentStore ?? createMemoryAgentStore(),
6185
+ basePath,
6186
+ avatars: options.avatars !== void 0,
6187
+ sleepAfterMs: options.agentSleepAfterMs
6188
+ });
6189
+ const projects = new ProjectInfoService({ decorate: (info) => agents.decorate(shells ? shells.decorate(info) : info) });
5673
6190
  const notifier = new SessionNotifier({
5674
6191
  ...options.notifications,
5675
6192
  decorateInfo: (info) => projects.withProject(info),
@@ -5678,6 +6195,7 @@ function createWorkerServer(options = {}) {
5678
6195
  const producedFiles = new ProducedFileStore();
5679
6196
  const engineSleep = new EngineSleepTimers({
5680
6197
  afterMs: options.engineSleepAfterMs ?? 0,
6198
+ afterMsFor: (sessionId) => agents.sleepAfterFor(sessionId),
5681
6199
  attachedCount: (sessionId) => bridge.attachedCount(sessionId),
5682
6200
  onError: (error) => diagnose(error, "engine-sleep")
5683
6201
  });
@@ -5764,7 +6282,8 @@ function createWorkerServer(options = {}) {
5764
6282
  pricing: pricing.pricing,
5765
6283
  registry,
5766
6284
  parking,
5767
- bridge
6285
+ bridge,
6286
+ agentBrief: (sessionId) => agents.briefFor(sessionId)
5768
6287
  });
5769
6288
  const availability = new AvailabilityTracker({
5770
6289
  checkCredentials: options.checkCredentials,
@@ -5812,6 +6331,8 @@ function createWorkerServer(options = {}) {
5812
6331
  availability,
5813
6332
  auth,
5814
6333
  factory,
6334
+ agents,
6335
+ avatars: options.avatars,
5815
6336
  registry,
5816
6337
  parking,
5817
6338
  engineSleep,
@@ -5883,6 +6404,7 @@ function createWorkerServer(options = {}) {
5883
6404
  listen: async (port, host) => {
5884
6405
  await profiles.refreshStored();
5885
6406
  await profiles.seedStore();
6407
+ await agents.hydrate();
5886
6408
  await parking.hydrate();
5887
6409
  await shells?.hydrate();
5888
6410
  return new Promise((resolve, reject) => {
@@ -6078,6 +6600,6 @@ function createFileProfileStore(path = join(process.cwd(), ".workerdeck", "profi
6078
6600
  };
6079
6601
  }
6080
6602
  //#endregion
6081
- export { AttachmentStore, BridgeHub, MemorySessionStore, ProducedFileStore, ProfileUsageTracker, SHELL_REFUSAL, SessionNotifier, SessionParkManager, SessionRegistry, SpendLedger, createFileProfileStore, createFileSessionStore, createFileSpendStore, createMemoryProfileStore, createProviderRunner, createShellDirectory, createShellRegistry, createWorkerServer, dayKey, isDormant, isMissing, readJsonOr, reloadPlan, sandboxedProviderProfile, shellPermitted, toDurableRecord, writeFileAtomic, writeJsonAtomic };
6603
+ export { AttachmentStore, BridgeHub, MemorySessionStore, ProducedFileStore, ProfileUsageTracker, SHELL_REFUSAL, SessionNotifier, SessionParkManager, SessionRegistry, SpendLedger, createFileAgentStore, createFileProfileStore, createFileSessionStore, createFileSpendStore, createMemoryAgentStore, createMemoryProfileStore, createProviderRunner, createShellDirectory, createShellRegistry, createWorkerServer, dayKey, isDormant, isMissing, readJsonOr, reloadPlan, sandboxedProviderProfile, shellPermitted, toDurableRecord, writeFileAtomic, writeJsonAtomic };
6082
6604
 
6083
6605
  //# sourceMappingURL=index.mjs.map