@llui/agent 0.0.51 → 0.0.53

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.
Files changed (68) hide show
  1. package/dist/client/agentConnect.d.ts.map +1 -1
  2. package/dist/client/agentConnect.js +45 -31
  3. package/dist/client/agentConnect.js.map +1 -1
  4. package/dist/client/effect-handler.d.ts +0 -21
  5. package/dist/client/effect-handler.d.ts.map +1 -1
  6. package/dist/client/effect-handler.js +0 -26
  7. package/dist/client/effect-handler.js.map +1 -1
  8. package/dist/client/effects.d.ts +0 -23
  9. package/dist/client/effects.d.ts.map +1 -1
  10. package/dist/client/effects.js.map +1 -1
  11. package/dist/client/factory.d.ts +0 -9
  12. package/dist/client/factory.d.ts.map +1 -1
  13. package/dist/client/factory.js +0 -2
  14. package/dist/client/factory.js.map +1 -1
  15. package/dist/client/index.d.ts +0 -1
  16. package/dist/client/index.d.ts.map +1 -1
  17. package/dist/client/index.js +0 -1
  18. package/dist/client/index.js.map +1 -1
  19. package/dist/client/ws-client.d.ts +0 -9
  20. package/dist/client/ws-client.d.ts.map +1 -1
  21. package/dist/client/ws-client.js +0 -32
  22. package/dist/client/ws-client.js.map +1 -1
  23. package/dist/mcp/tools.d.ts +19 -0
  24. package/dist/mcp/tools.d.ts.map +1 -0
  25. package/dist/mcp/tools.js +176 -0
  26. package/dist/mcp/tools.js.map +1 -0
  27. package/dist/protocol.d.ts +1 -50
  28. package/dist/protocol.d.ts.map +1 -1
  29. package/dist/protocol.js.map +1 -1
  30. package/dist/server/cloudflare/durable-object.d.ts +14 -44
  31. package/dist/server/cloudflare/durable-object.d.ts.map +1 -1
  32. package/dist/server/cloudflare/durable-object.js +21 -49
  33. package/dist/server/cloudflare/durable-object.js.map +1 -1
  34. package/dist/server/cloudflare/index.d.ts +3 -10
  35. package/dist/server/cloudflare/index.d.ts.map +1 -1
  36. package/dist/server/cloudflare/index.js +3 -10
  37. package/dist/server/cloudflare/index.js.map +1 -1
  38. package/dist/server/core.d.ts +1 -11
  39. package/dist/server/core.d.ts.map +1 -1
  40. package/dist/server/core.js +0 -1
  41. package/dist/server/core.js.map +1 -1
  42. package/dist/server/factory.d.ts.map +1 -1
  43. package/dist/server/factory.js +14 -1
  44. package/dist/server/factory.js.map +1 -1
  45. package/dist/server/lap/router.d.ts.map +1 -1
  46. package/dist/server/lap/router.js +0 -3
  47. package/dist/server/lap/router.js.map +1 -1
  48. package/dist/server/mcp/router.d.ts +26 -0
  49. package/dist/server/mcp/router.d.ts.map +1 -0
  50. package/dist/server/mcp/router.js +81 -0
  51. package/dist/server/mcp/router.js.map +1 -0
  52. package/dist/server/mcp/server.d.ts +26 -0
  53. package/dist/server/mcp/server.d.ts.map +1 -0
  54. package/dist/server/mcp/server.js +116 -0
  55. package/dist/server/mcp/server.js.map +1 -0
  56. package/dist/server/mcp/session-map.d.ts +18 -0
  57. package/dist/server/mcp/session-map.d.ts.map +1 -0
  58. package/dist/server/mcp/session-map.js +13 -0
  59. package/dist/server/mcp/session-map.js.map +1 -0
  60. package/dist/server/options.d.ts +13 -0
  61. package/dist/server/options.d.ts.map +1 -1
  62. package/dist/server/options.js.map +1 -1
  63. package/dist/server/ws/pairing-registry.d.ts +0 -101
  64. package/dist/server/ws/pairing-registry.d.ts.map +1 -1
  65. package/dist/server/ws/pairing-registry.js +0 -160
  66. package/dist/server/ws/pairing-registry.js.map +1 -1
  67. package/package.json +8 -2
  68. package/styles/agent-panel.css +0 -13
@@ -13,7 +13,7 @@ import type { TokenStore } from './token-store.js';
13
13
  import type { IdentityResolver } from './identity.js';
14
14
  import type { AuditSink } from './audit.js';
15
15
  import type { RateLimiter } from './rate-limit.js';
16
- import type { PairingConnection, PairingRegistry, UserInputStorage } from './ws/pairing-registry.js';
16
+ import type { PairingConnection, PairingRegistry } from './ws/pairing-registry.js';
17
17
  /**
18
18
  * Options accepted by `createLluiAgentCore`. Strict subset of
19
19
  * `ServerOptions` — everything needed to build the router, registry,
@@ -32,16 +32,6 @@ export type CoreOptions = {
32
32
  * Durable Object that persists across isolates) pass it here.
33
33
  */
34
34
  registry?: PairingRegistry;
35
- /**
36
- * Optional persistence adapter for the chat-composer's
37
- * `wait_for_user_input` buffer. Wired automatically into the
38
- * default `InMemoryPairingRegistry`; ignored when `opts.registry`
39
- * is overridden (custom registries supply their own persistence).
40
- * See `UserInputStorage` for the contract. Cloudflare DO hosts get
41
- * a ready-made adapter from
42
- * `@llui/agent/server/cloudflare`'s `makeDurableObjectUserInputStorage`.
43
- */
44
- userInputStorage?: UserInputStorage;
45
35
  /**
46
36
  * How long, in milliseconds, a token's record stays in
47
37
  * `pending-resume` after the WS pairing closes. During this window
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/server/core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAWpG;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC;;;;;;;;;;;;;;;;;OAiBG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAC9B,CAAA;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,aAAa,GAAG,SAAS,CAAA;CAAE,CAAA;AAElE;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IAClD,QAAQ,EAAE,eAAe,CAAA;IACzB,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;IACpB;;;;;;;;;;OAUG;IACH,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAA;CACpF,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,WAAgB,GAAG,eAAe,CAmH3E"}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/server/core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAWlF;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B;;;;;;;;;;;;;;;;;OAiBG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAC9B,CAAA;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,aAAa,GAAG,SAAS,CAAA;CAAE,CAAA;AAElE;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IAClD,QAAQ,EAAE,eAAe,CAAA;IACzB,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;IACpB;;;;;;;;;;OAUG;IACH,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAA;CACpF,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,WAAgB,GAAG,eAAe,CAkH3E"}
@@ -36,7 +36,6 @@ export function createLluiAgentCore(opts = {}) {
36
36
  },
37
37
  });
38
38
  },
39
- userInputStorage: opts.userInputStorage,
40
39
  });
41
40
  const httpRouter = createHttpRouter({
42
41
  tokenStore,
@@ -1 +1 @@
1
- {"version":3,"file":"core.js","sourceRoot":"","sources":["../../src/server/core.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Runtime-neutral core of the LLui agent server. Exports everything\n * that works on any runtime with `crypto.subtle` + `Request`/`Response`\n * + long-lived connection primitives — in practice: Node, Bun, Deno,\n * Deno Deploy, Cloudflare Workers + Durable Objects.\n *\n * Intentionally does NOT import the `ws` library or any `node:*`\n * modules. Node-specific wiring lives in `./factory.ts`\n * (`createLluiAgentServer`); web runtimes use `./web/` adapters on\n * top of this core.\n */\nimport type { TokenStore } from './token-store.js'\nimport type { IdentityResolver } from './identity.js'\nimport type { AuditSink } from './audit.js'\nimport type { RateLimiter } from './rate-limit.js'\nimport type { PairingConnection, PairingRegistry, UserInputStorage } from './ws/pairing-registry.js'\nimport { InMemoryTokenStore } from './token-store.js'\nimport { consoleAuditSink } from './audit.js'\nimport { defaultRateLimiter } from './rate-limit.js'\nimport { createHttpRouter } from './http/router.js'\nimport { createLapRouter } from './lap/router.js'\nimport { InMemoryPairingRegistry } from './ws/pairing-registry.js'\nimport { tokenHashOf } from './token.js'\n\nconst ANONYMOUS_RESOLVER: IdentityResolver = async () => null\n\n/**\n * Options accepted by `createLluiAgentCore`. Strict subset of\n * `ServerOptions` — everything needed to build the router, registry,\n * and accept-connection primitive. The Node factory adds WebSocket\n * upgrade wiring on top.\n */\nexport type CoreOptions = {\n tokenStore?: TokenStore\n identityResolver?: IdentityResolver\n auditSink?: AuditSink\n rateLimiter?: RateLimiter\n lapBasePath?: string\n /**\n * Override the default `InMemoryPairingRegistry`. Web runtimes that\n * need a different pairing implementation (e.g. a Cloudflare\n * Durable Object that persists across isolates) pass it here.\n */\n registry?: PairingRegistry\n /**\n * Optional persistence adapter for the chat-composer's\n * `wait_for_user_input` buffer. Wired automatically into the\n * default `InMemoryPairingRegistry`; ignored when `opts.registry`\n * is overridden (custom registries supply their own persistence).\n * See `UserInputStorage` for the contract. Cloudflare DO hosts get\n * a ready-made adapter from\n * `@llui/agent/server/cloudflare`'s `makeDurableObjectUserInputStorage`.\n */\n userInputStorage?: UserInputStorage\n /**\n * How long, in milliseconds, a token's record stays in\n * `pending-resume` after the WS pairing closes. During this window\n * the same browser can reconnect with the same bearer token and\n * the WS re-pairs without going through the rotate-on-resume path\n * (`/resume/claim`). The agent's existing token stays valid the\n * whole time, so brief network drops, page reloads, and quick\n * server restarts don't invalidate the agent's session.\n *\n * After the window, LAP calls report `X-LLui-Reconnect: expired`\n * and the record becomes resume-claimable (rotation required).\n * Set to `0` to opt out — the WS close immediately drops the\n * record and any reconnect must go through `/resume/claim`.\n *\n * Default: 60 seconds — long enough for laptop sleep, brief Wi-Fi\n * flicker, and a server restart; short enough that a deliberately-\n * closed tab doesn't keep the record alive forever.\n */\n pendingResumeGraceMs?: number\n}\n\nexport type AcceptResult =\n | { ok: true; tid: string }\n | { ok: false; status: number; code: 'auth-failed' | 'revoked' }\n\n/**\n * Handle returned by `createLluiAgentCore`. Purely runtime-neutral —\n * `router` is a Fetch-style handler, `acceptConnection` is the\n * primitive that runtime-specific WebSocket adapters call after\n * accepting a socket in their native way.\n */\nexport type AgentCoreHandle = {\n router: (req: Request) => Promise<Response | null>\n registry: PairingRegistry\n tokenStore: TokenStore\n auditSink: AuditSink\n /**\n * Validate an agent token and register a `PairingConnection` with\n * the registry. Use this after accepting a WebSocket upgrade via\n * your runtime's native API (e.g. `WebSocketPair` on Cloudflare,\n * `Deno.upgradeWebSocket` on Deno, `server.upgrade` on Bun).\n *\n * On success: marks the token `awaiting-claude`, writes an audit\n * entry, and returns `{ok: true, tid}`. On failure: returns an\n * appropriate HTTP status for the caller to encode into the\n * upgrade response (401 for auth failure, 403 for revoked).\n */\n acceptConnection: (token: string, conn: PairingConnection) => Promise<AcceptResult>\n}\n\n/**\n * Compose the runtime-neutral agent server. The returned handle has\n * everything the LAP HTTP routes and the WebSocket acceptance\n * plumbing need; runtime adapters wire the native upgrade API on\n * top (see `@llui/agent/server` for Node, `@llui/agent/server/web`\n * for WHATWG runtimes).\n */\nexport function createLluiAgentCore(opts: CoreOptions = {}): AgentCoreHandle {\n const tokenStore = opts.tokenStore ?? new InMemoryTokenStore()\n const identityResolver = opts.identityResolver ?? ANONYMOUS_RESOLVER\n const auditSink = opts.auditSink ?? consoleAuditSink\n const rateLimiter = opts.rateLimiter ?? defaultRateLimiter({ perBucket: '30/minute' })\n const lapBasePath = opts.lapBasePath ?? '/agent/lap/v1'\n const pendingResumeGraceMs = opts.pendingResumeGraceMs ?? 60_000\n\n const registry: PairingRegistry =\n opts.registry ??\n new InMemoryPairingRegistry({\n onLogAppend: (tid, entry) => {\n void auditSink.write({\n at: entry.at,\n tid,\n uid: null,\n event: 'lap-call',\n detail: {\n source: 'client-log',\n kind: entry.kind,\n variant: entry.variant,\n intent: entry.intent,\n },\n })\n },\n userInputStorage: opts.userInputStorage,\n })\n\n const httpRouter = createHttpRouter({\n tokenStore,\n identityResolver,\n auditSink,\n lapBasePath,\n })\n\n const lapRouter = createLapRouter(\n {\n tokenStore,\n registry,\n auditSink,\n rateLimiter,\n },\n lapBasePath,\n )\n\n const router: AgentCoreHandle['router'] = async (req) => {\n const lapRes = await lapRouter(req)\n if (lapRes) return lapRes\n return httpRouter(req)\n }\n\n const acceptConnection: AgentCoreHandle['acceptConnection'] = async (token, conn) => {\n // Same hash-lookup path as the LAP HTTP routes — keeps the auth\n // story uniform across HTTP and WS surfaces.\n const hash = await tokenHashOf(token)\n if (!hash) return { ok: false, status: 401, code: 'auth-failed' }\n const rec = await tokenStore.findByTokenHash(hash)\n if (!rec) return { ok: false, status: 401, code: 'auth-failed' }\n if (rec.expiresAt <= Date.now()) return { ok: false, status: 401, code: 'auth-failed' }\n if (rec.status === 'revoked') return { ok: false, status: 403, code: 'revoked' }\n // Reject `pending-resume` records past their grace window — the\n // agent has to go through `/resume/claim` (which rotates the\n // bearer) for those, since the long-gap path can't assume the\n // previous bearer wasn't leaked.\n if (\n rec.status === 'pending-resume' &&\n rec.pendingResumeUntil !== null &&\n rec.pendingResumeUntil <= Date.now()\n ) {\n return { ok: false, status: 401, code: 'auth-failed' }\n }\n const tid = rec.tid\n const isRepair = rec.status === 'pending-resume'\n registry.register(tid, conn)\n const nowMs = Date.now()\n if (isRepair) {\n // Same browser came back within the grace window — re-pair\n // without a token rotation. Claude was already bound; its\n // existing token stays valid and the next LAP call sees a live\n // pairing again. Restore the original label so audit context\n // doesn't show a \"reconnected\" placeholder bouncing in and out.\n await tokenStore.markActive(tid, rec.label ?? '(reconnected)', nowMs)\n // Tell the browser the pairing is live again so its connect-\n // panel flips from `pending-claude` (or `reconnecting`) to\n // `active`. Without this, the page would stay on\n // \"Waiting for AI to claim\" indefinitely after a refresh —\n // ensureActive on the next LAP call wouldn't fire either,\n // since the record is already `active`.\n registry.send(tid, { t: 'active' })\n } else {\n await tokenStore.markAwaitingClaude(tid, nowMs)\n }\n // Hook the close: when the WS drops, transition the record to\n // `pending-resume` with a TTL so the next reconnect within the\n // grace window can re-pair without rotating the token. After\n // grace, LAP calls return `X-LLui-Reconnect: expired` and the\n // agent must call `/resume/claim` to start fresh. The token-\n // store guards the transition so `revoke`/`expired` don't get\n // lifted back into a grace window.\n if (pendingResumeGraceMs > 0) {\n registry.onClose(tid, () => {\n void tokenStore.markPendingResume(tid, Date.now() + pendingResumeGraceMs)\n })\n }\n await auditSink.write({\n at: nowMs,\n tid,\n uid: null,\n event: 'claim',\n detail: { transport: 'ws', repair: isRepair },\n })\n return { ok: true, tid }\n }\n\n return { router, registry, tokenStore, auditSink, acceptConnection }\n}\n"]}
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../../src/server/core.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Runtime-neutral core of the LLui agent server. Exports everything\n * that works on any runtime with `crypto.subtle` + `Request`/`Response`\n * + long-lived connection primitives — in practice: Node, Bun, Deno,\n * Deno Deploy, Cloudflare Workers + Durable Objects.\n *\n * Intentionally does NOT import the `ws` library or any `node:*`\n * modules. Node-specific wiring lives in `./factory.ts`\n * (`createLluiAgentServer`); web runtimes use `./web/` adapters on\n * top of this core.\n */\nimport type { TokenStore } from './token-store.js'\nimport type { IdentityResolver } from './identity.js'\nimport type { AuditSink } from './audit.js'\nimport type { RateLimiter } from './rate-limit.js'\nimport type { PairingConnection, PairingRegistry } from './ws/pairing-registry.js'\nimport { InMemoryTokenStore } from './token-store.js'\nimport { consoleAuditSink } from './audit.js'\nimport { defaultRateLimiter } from './rate-limit.js'\nimport { createHttpRouter } from './http/router.js'\nimport { createLapRouter } from './lap/router.js'\nimport { InMemoryPairingRegistry } from './ws/pairing-registry.js'\nimport { tokenHashOf } from './token.js'\n\nconst ANONYMOUS_RESOLVER: IdentityResolver = async () => null\n\n/**\n * Options accepted by `createLluiAgentCore`. Strict subset of\n * `ServerOptions` — everything needed to build the router, registry,\n * and accept-connection primitive. The Node factory adds WebSocket\n * upgrade wiring on top.\n */\nexport type CoreOptions = {\n tokenStore?: TokenStore\n identityResolver?: IdentityResolver\n auditSink?: AuditSink\n rateLimiter?: RateLimiter\n lapBasePath?: string\n /**\n * Override the default `InMemoryPairingRegistry`. Web runtimes that\n * need a different pairing implementation (e.g. a Cloudflare\n * Durable Object that persists across isolates) pass it here.\n */\n registry?: PairingRegistry\n /**\n * How long, in milliseconds, a token's record stays in\n * `pending-resume` after the WS pairing closes. During this window\n * the same browser can reconnect with the same bearer token and\n * the WS re-pairs without going through the rotate-on-resume path\n * (`/resume/claim`). The agent's existing token stays valid the\n * whole time, so brief network drops, page reloads, and quick\n * server restarts don't invalidate the agent's session.\n *\n * After the window, LAP calls report `X-LLui-Reconnect: expired`\n * and the record becomes resume-claimable (rotation required).\n * Set to `0` to opt out — the WS close immediately drops the\n * record and any reconnect must go through `/resume/claim`.\n *\n * Default: 60 seconds — long enough for laptop sleep, brief Wi-Fi\n * flicker, and a server restart; short enough that a deliberately-\n * closed tab doesn't keep the record alive forever.\n */\n pendingResumeGraceMs?: number\n}\n\nexport type AcceptResult =\n | { ok: true; tid: string }\n | { ok: false; status: number; code: 'auth-failed' | 'revoked' }\n\n/**\n * Handle returned by `createLluiAgentCore`. Purely runtime-neutral —\n * `router` is a Fetch-style handler, `acceptConnection` is the\n * primitive that runtime-specific WebSocket adapters call after\n * accepting a socket in their native way.\n */\nexport type AgentCoreHandle = {\n router: (req: Request) => Promise<Response | null>\n registry: PairingRegistry\n tokenStore: TokenStore\n auditSink: AuditSink\n /**\n * Validate an agent token and register a `PairingConnection` with\n * the registry. Use this after accepting a WebSocket upgrade via\n * your runtime's native API (e.g. `WebSocketPair` on Cloudflare,\n * `Deno.upgradeWebSocket` on Deno, `server.upgrade` on Bun).\n *\n * On success: marks the token `awaiting-claude`, writes an audit\n * entry, and returns `{ok: true, tid}`. On failure: returns an\n * appropriate HTTP status for the caller to encode into the\n * upgrade response (401 for auth failure, 403 for revoked).\n */\n acceptConnection: (token: string, conn: PairingConnection) => Promise<AcceptResult>\n}\n\n/**\n * Compose the runtime-neutral agent server. The returned handle has\n * everything the LAP HTTP routes and the WebSocket acceptance\n * plumbing need; runtime adapters wire the native upgrade API on\n * top (see `@llui/agent/server` for Node, `@llui/agent/server/web`\n * for WHATWG runtimes).\n */\nexport function createLluiAgentCore(opts: CoreOptions = {}): AgentCoreHandle {\n const tokenStore = opts.tokenStore ?? new InMemoryTokenStore()\n const identityResolver = opts.identityResolver ?? ANONYMOUS_RESOLVER\n const auditSink = opts.auditSink ?? consoleAuditSink\n const rateLimiter = opts.rateLimiter ?? defaultRateLimiter({ perBucket: '30/minute' })\n const lapBasePath = opts.lapBasePath ?? '/agent/lap/v1'\n const pendingResumeGraceMs = opts.pendingResumeGraceMs ?? 60_000\n\n const registry: PairingRegistry =\n opts.registry ??\n new InMemoryPairingRegistry({\n onLogAppend: (tid, entry) => {\n void auditSink.write({\n at: entry.at,\n tid,\n uid: null,\n event: 'lap-call',\n detail: {\n source: 'client-log',\n kind: entry.kind,\n variant: entry.variant,\n intent: entry.intent,\n },\n })\n },\n })\n\n const httpRouter = createHttpRouter({\n tokenStore,\n identityResolver,\n auditSink,\n lapBasePath,\n })\n\n const lapRouter = createLapRouter(\n {\n tokenStore,\n registry,\n auditSink,\n rateLimiter,\n },\n lapBasePath,\n )\n\n const router: AgentCoreHandle['router'] = async (req) => {\n const lapRes = await lapRouter(req)\n if (lapRes) return lapRes\n return httpRouter(req)\n }\n\n const acceptConnection: AgentCoreHandle['acceptConnection'] = async (token, conn) => {\n // Same hash-lookup path as the LAP HTTP routes — keeps the auth\n // story uniform across HTTP and WS surfaces.\n const hash = await tokenHashOf(token)\n if (!hash) return { ok: false, status: 401, code: 'auth-failed' }\n const rec = await tokenStore.findByTokenHash(hash)\n if (!rec) return { ok: false, status: 401, code: 'auth-failed' }\n if (rec.expiresAt <= Date.now()) return { ok: false, status: 401, code: 'auth-failed' }\n if (rec.status === 'revoked') return { ok: false, status: 403, code: 'revoked' }\n // Reject `pending-resume` records past their grace window — the\n // agent has to go through `/resume/claim` (which rotates the\n // bearer) for those, since the long-gap path can't assume the\n // previous bearer wasn't leaked.\n if (\n rec.status === 'pending-resume' &&\n rec.pendingResumeUntil !== null &&\n rec.pendingResumeUntil <= Date.now()\n ) {\n return { ok: false, status: 401, code: 'auth-failed' }\n }\n const tid = rec.tid\n const isRepair = rec.status === 'pending-resume'\n registry.register(tid, conn)\n const nowMs = Date.now()\n if (isRepair) {\n // Same browser came back within the grace window — re-pair\n // without a token rotation. Claude was already bound; its\n // existing token stays valid and the next LAP call sees a live\n // pairing again. Restore the original label so audit context\n // doesn't show a \"reconnected\" placeholder bouncing in and out.\n await tokenStore.markActive(tid, rec.label ?? '(reconnected)', nowMs)\n // Tell the browser the pairing is live again so its connect-\n // panel flips from `pending-claude` (or `reconnecting`) to\n // `active`. Without this, the page would stay on\n // \"Waiting for AI to claim\" indefinitely after a refresh —\n // ensureActive on the next LAP call wouldn't fire either,\n // since the record is already `active`.\n registry.send(tid, { t: 'active' })\n } else {\n await tokenStore.markAwaitingClaude(tid, nowMs)\n }\n // Hook the close: when the WS drops, transition the record to\n // `pending-resume` with a TTL so the next reconnect within the\n // grace window can re-pair without rotating the token. After\n // grace, LAP calls return `X-LLui-Reconnect: expired` and the\n // agent must call `/resume/claim` to start fresh. The token-\n // store guards the transition so `revoke`/`expired` don't get\n // lifted back into a grace window.\n if (pendingResumeGraceMs > 0) {\n registry.onClose(tid, () => {\n void tokenStore.markPendingResume(tid, Date.now() + pendingResumeGraceMs)\n })\n }\n await auditSink.write({\n at: nowMs,\n tid,\n uid: null,\n event: 'claim',\n detail: { transport: 'ws', repair: isRepair },\n })\n return { ok: true, tid }\n }\n\n return { router, registry, tokenStore, auditSink, acceptConnection }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/server/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAIpE;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,GAAE,aAAkB,GAAG,iBAAiB,CAiBjF"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/server/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAKpE;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,GAAE,aAAkB,GAAG,iBAAiB,CAiCjF"}
@@ -1,5 +1,6 @@
1
1
  import { createLluiAgentCore } from './core.js';
2
2
  import { createWsUpgradeHandler } from './ws/upgrade.js';
3
+ import { createMcpRouter } from './mcp/router.js';
3
4
  /**
4
5
  * Node adapter. Wraps the runtime-neutral core with a Node-specific
5
6
  * `wsUpgrade` handler that uses the `ws` library. Imports `ws`
@@ -16,8 +17,20 @@ export function createLluiAgentServer(opts = {}) {
16
17
  registry: core.registry,
17
18
  auditSink: core.auditSink,
18
19
  });
20
+ const lapBasePath = opts.lapBasePath ?? '/agent/lap/v1';
21
+ let router = core.router;
22
+ if (opts.mcp) {
23
+ const mcpOpts = opts.mcp === true ? {} : opts.mcp;
24
+ const mcpRouter = createMcpRouter({ coreRouter: core.router, tokenStore: core.tokenStore, lapBasePath }, mcpOpts);
25
+ router = async (req) => {
26
+ const mcpRes = await mcpRouter(req);
27
+ if (mcpRes)
28
+ return mcpRes;
29
+ return core.router(req);
30
+ };
31
+ }
19
32
  return {
20
- router: core.router,
33
+ router,
21
34
  wsUpgrade,
22
35
  registry: core.registry,
23
36
  tokenStore: core.tokenStore,
@@ -1 +1 @@
1
- {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/server/factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAExD;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAsB,EAAE;IAC5D,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAA;IAEtC,MAAM,SAAS,GAAG,sBAAsB,CAAC;QACvC,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC,CAAA;IAEF,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS;QACT,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;KACxC,CAAA;AACH,CAAC","sourcesContent":["import type { ServerOptions, AgentServerHandle } from './options.js'\nimport { createLluiAgentCore } from './core.js'\nimport { createWsUpgradeHandler } from './ws/upgrade.js'\n\n/**\n * Node adapter. Wraps the runtime-neutral core with a Node-specific\n * `wsUpgrade` handler that uses the `ws` library. Imports `ws`\n * eagerly, so this module only works where `ws` is available — use\n * `@llui/agent/server/web` for Cloudflare Workers, Deno, or other\n * WHATWG runtimes.\n *\n * Spec §10.1, §10.4.\n */\nexport function createLluiAgentServer(opts: ServerOptions = {}): AgentServerHandle {\n const core = createLluiAgentCore(opts)\n\n const wsUpgrade = createWsUpgradeHandler({\n tokenStore: core.tokenStore,\n registry: core.registry,\n auditSink: core.auditSink,\n })\n\n return {\n router: core.router,\n wsUpgrade,\n registry: core.registry,\n tokenStore: core.tokenStore,\n auditSink: core.auditSink,\n acceptConnection: core.acceptConnection,\n }\n}\n"]}
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/server/factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjD;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAsB,EAAE;IAC5D,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAA;IAEtC,MAAM,SAAS,GAAG,sBAAsB,CAAC;QACvC,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC,CAAA;IAEF,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,eAAe,CAAA;IAEvD,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;IACxB,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAA;QACjD,MAAM,SAAS,GAAG,eAAe,CAC/B,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,EACrE,OAAO,CACR,CAAA;QACD,MAAM,GAAG,KAAK,EAAE,GAAG,EAAE,EAAE;YACrB,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAA;YACnC,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAA;YACzB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACzB,CAAC,CAAA;IACH,CAAC;IAED,OAAO;QACL,MAAM;QACN,SAAS;QACT,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;KACxC,CAAA;AACH,CAAC","sourcesContent":["import type { ServerOptions, AgentServerHandle } from './options.js'\nimport { createLluiAgentCore } from './core.js'\nimport { createWsUpgradeHandler } from './ws/upgrade.js'\nimport { createMcpRouter } from './mcp/router.js'\n\n/**\n * Node adapter. Wraps the runtime-neutral core with a Node-specific\n * `wsUpgrade` handler that uses the `ws` library. Imports `ws`\n * eagerly, so this module only works where `ws` is available — use\n * `@llui/agent/server/web` for Cloudflare Workers, Deno, or other\n * WHATWG runtimes.\n *\n * Spec §10.1, §10.4.\n */\nexport function createLluiAgentServer(opts: ServerOptions = {}): AgentServerHandle {\n const core = createLluiAgentCore(opts)\n\n const wsUpgrade = createWsUpgradeHandler({\n tokenStore: core.tokenStore,\n registry: core.registry,\n auditSink: core.auditSink,\n })\n\n const lapBasePath = opts.lapBasePath ?? '/agent/lap/v1'\n\n let router = core.router\n if (opts.mcp) {\n const mcpOpts = opts.mcp === true ? {} : opts.mcp\n const mcpRouter = createMcpRouter(\n { coreRouter: core.router, tokenStore: core.tokenStore, lapBasePath },\n mcpOpts,\n )\n router = async (req) => {\n const mcpRes = await mcpRouter(req)\n if (mcpRes) return mcpRes\n return core.router(req)\n }\n }\n\n return {\n router,\n wsUpgrade,\n registry: core.registry,\n tokenStore: core.tokenStore,\n auditSink: core.auditSink,\n acceptConnection: core.acceptConnection,\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../src/server/lap/router.ts"],"names":[],"mappings":"AACA,OAAO,EASL,KAAK,WAAW,EACjB,MAAM,cAAc,CAAA;AAQrB,MAAM,MAAM,aAAa,GAAG,WAAW,CAAA;AAEvC,wBAAgB,eAAe,CAC7B,IAAI,EAAE,aAAa,EACnB,QAAQ,EAAE,MAAM,GACf,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAyC5C"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../src/server/lap/router.ts"],"names":[],"mappings":"AACA,OAAO,EASL,KAAK,WAAW,EACjB,MAAM,cAAc,CAAA;AAOrB,MAAM,MAAM,aAAa,GAAG,WAAW,CAAA;AAEvC,wBAAgB,eAAe,CAC7B,IAAI,EAAE,aAAa,EACnB,QAAQ,EAAE,MAAM,GACf,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAuC5C"}
@@ -2,7 +2,6 @@ import { handleLapDescribe } from './describe.js';
2
2
  import { handleLapState, handleLapQueryState, handleLapActions, handleLapQueryDom, handleLapDescribeVisible, handleLapContext, handleLapRecentActions, handleLapWouldDispatch, } from './forward.js';
3
3
  import { handleLapMessage } from './message.js';
4
4
  import { handleLapWait } from './wait.js';
5
- import { handleLapWaitForUserInput } from './wait-for-user-input.js';
6
5
  import { handleLapNarrate } from './narrate.js';
7
6
  import { handleLapConfirmResult } from './confirm-result.js';
8
7
  import { handleLapObserve } from './observe.js';
@@ -28,8 +27,6 @@ export function createLapRouter(deps, basePath) {
28
27
  return handleLapConfirmResult(req, deps);
29
28
  case '/wait':
30
29
  return handleLapWait(req, deps);
31
- case '/wait-for-user-input':
32
- return handleLapWaitForUserInput(req, deps);
33
30
  case '/narrate':
34
31
  return handleLapNarrate(req, deps);
35
32
  case '/query-dom':
@@ -1 +1 @@
1
- {"version":3,"file":"router.js","sourceRoot":"","sources":["../../../src/server/lap/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,GAEvB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAA;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAI/C,MAAM,UAAU,eAAe,CAC7B,IAAmB,EACnB,QAAgB;IAEhB,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC5B,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAA;QACzB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,GAAG,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;QACxC,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,WAAW;gBACd,OAAO,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACrC,KAAK,QAAQ;gBACX,OAAO,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAClC,KAAK,cAAc;gBACjB,OAAO,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACvC,KAAK,UAAU;gBACb,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACpC,KAAK,UAAU;gBACb,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACpC,KAAK,iBAAiB;gBACpB,OAAO,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAC1C,KAAK,OAAO;gBACV,OAAO,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACjC,KAAK,sBAAsB;gBACzB,OAAO,yBAAyB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAC7C,KAAK,UAAU;gBACb,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACpC,KAAK,YAAY;gBACf,OAAO,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACrC,KAAK,mBAAmB;gBACtB,OAAO,wBAAwB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAC5C,KAAK,UAAU;gBACb,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACpC,KAAK,UAAU;gBACb,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACpC,KAAK,iBAAiB;gBACpB,OAAO,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAC1C,KAAK,iBAAiB;gBACpB,OAAO,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAC1C;gBACE,OAAO,IAAI,CAAA;QACf,CAAC;IACH,CAAC,CAAA;AACH,CAAC","sourcesContent":["import { handleLapDescribe } from './describe.js'\nimport {\n handleLapState,\n handleLapQueryState,\n handleLapActions,\n handleLapQueryDom,\n handleLapDescribeVisible,\n handleLapContext,\n handleLapRecentActions,\n handleLapWouldDispatch,\n type ForwardDeps,\n} from './forward.js'\nimport { handleLapMessage } from './message.js'\nimport { handleLapWait } from './wait.js'\nimport { handleLapWaitForUserInput } from './wait-for-user-input.js'\nimport { handleLapNarrate } from './narrate.js'\nimport { handleLapConfirmResult } from './confirm-result.js'\nimport { handleLapObserve } from './observe.js'\n\nexport type LapRouterDeps = ForwardDeps\n\nexport function createLapRouter(\n deps: LapRouterDeps,\n basePath: string,\n): (req: Request) => Promise<Response | null> {\n return async (req) => {\n const url = new URL(req.url)\n const path = url.pathname\n if (!path.startsWith(basePath + '/')) return null\n const tail = path.slice(basePath.length)\n switch (tail) {\n case '/describe':\n return handleLapDescribe(req, deps)\n case '/state':\n return handleLapState(req, deps)\n case '/query-state':\n return handleLapQueryState(req, deps)\n case '/actions':\n return handleLapActions(req, deps)\n case '/message':\n return handleLapMessage(req, deps)\n case '/confirm-result':\n return handleLapConfirmResult(req, deps)\n case '/wait':\n return handleLapWait(req, deps)\n case '/wait-for-user-input':\n return handleLapWaitForUserInput(req, deps)\n case '/narrate':\n return handleLapNarrate(req, deps)\n case '/query-dom':\n return handleLapQueryDom(req, deps)\n case '/describe-visible':\n return handleLapDescribeVisible(req, deps)\n case '/context':\n return handleLapContext(req, deps)\n case '/observe':\n return handleLapObserve(req, deps)\n case '/recent-actions':\n return handleLapRecentActions(req, deps)\n case '/would-dispatch':\n return handleLapWouldDispatch(req, deps)\n default:\n return null\n }\n }\n}\n"]}
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../../src/server/lap/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,GAEvB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAI/C,MAAM,UAAU,eAAe,CAC7B,IAAmB,EACnB,QAAgB;IAEhB,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC5B,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAA;QACzB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,GAAG,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;QACxC,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,WAAW;gBACd,OAAO,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACrC,KAAK,QAAQ;gBACX,OAAO,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAClC,KAAK,cAAc;gBACjB,OAAO,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACvC,KAAK,UAAU;gBACb,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACpC,KAAK,UAAU;gBACb,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACpC,KAAK,iBAAiB;gBACpB,OAAO,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAC1C,KAAK,OAAO;gBACV,OAAO,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACjC,KAAK,UAAU;gBACb,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACpC,KAAK,YAAY;gBACf,OAAO,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACrC,KAAK,mBAAmB;gBACtB,OAAO,wBAAwB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAC5C,KAAK,UAAU;gBACb,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACpC,KAAK,UAAU;gBACb,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACpC,KAAK,iBAAiB;gBACpB,OAAO,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAC1C,KAAK,iBAAiB;gBACpB,OAAO,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAC1C;gBACE,OAAO,IAAI,CAAA;QACf,CAAC;IACH,CAAC,CAAA;AACH,CAAC","sourcesContent":["import { handleLapDescribe } from './describe.js'\nimport {\n handleLapState,\n handleLapQueryState,\n handleLapActions,\n handleLapQueryDom,\n handleLapDescribeVisible,\n handleLapContext,\n handleLapRecentActions,\n handleLapWouldDispatch,\n type ForwardDeps,\n} from './forward.js'\nimport { handleLapMessage } from './message.js'\nimport { handleLapWait } from './wait.js'\nimport { handleLapNarrate } from './narrate.js'\nimport { handleLapConfirmResult } from './confirm-result.js'\nimport { handleLapObserve } from './observe.js'\n\nexport type LapRouterDeps = ForwardDeps\n\nexport function createLapRouter(\n deps: LapRouterDeps,\n basePath: string,\n): (req: Request) => Promise<Response | null> {\n return async (req) => {\n const url = new URL(req.url)\n const path = url.pathname\n if (!path.startsWith(basePath + '/')) return null\n const tail = path.slice(basePath.length)\n switch (tail) {\n case '/describe':\n return handleLapDescribe(req, deps)\n case '/state':\n return handleLapState(req, deps)\n case '/query-state':\n return handleLapQueryState(req, deps)\n case '/actions':\n return handleLapActions(req, deps)\n case '/message':\n return handleLapMessage(req, deps)\n case '/confirm-result':\n return handleLapConfirmResult(req, deps)\n case '/wait':\n return handleLapWait(req, deps)\n case '/narrate':\n return handleLapNarrate(req, deps)\n case '/query-dom':\n return handleLapQueryDom(req, deps)\n case '/describe-visible':\n return handleLapDescribeVisible(req, deps)\n case '/context':\n return handleLapContext(req, deps)\n case '/observe':\n return handleLapObserve(req, deps)\n case '/recent-actions':\n return handleLapRecentActions(req, deps)\n case '/would-dispatch':\n return handleLapWouldDispatch(req, deps)\n default:\n return null\n }\n }\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import type { TokenStore } from '../token-store.js';
2
+ export type McpRouterOptions = {
3
+ /** Path prefix for the MCP endpoint. Default: '/agent/mcp'. */
4
+ path?: string;
5
+ /** MCP server name shown in Claude Desktop. Default: 'agent'. */
6
+ serverName?: string;
7
+ /** MCP server version string. Default: '1'. */
8
+ serverVersion?: string;
9
+ /** Description for the connect_session tool. */
10
+ connectDescription?: string;
11
+ };
12
+ export type McpRouterDeps = {
13
+ coreRouter: (req: Request) => Promise<Response | null>;
14
+ tokenStore: TokenStore;
15
+ lapBasePath: string;
16
+ };
17
+ /**
18
+ * Build a WHATWG-compatible MCP router that mounts at `opts.path`.
19
+ * Integrates into the agent core's fetch-style router by prepending
20
+ * this function's result in the request chain.
21
+ *
22
+ * Uses `WebStandardStreamableHTTPServerTransport` (WHATWG, runtime-
23
+ * neutral) rather than the Node-only `StreamableHTTPServerTransport`.
24
+ */
25
+ export declare function createMcpRouter(deps: McpRouterDeps, opts?: McpRouterOptions): (req: Request) => Promise<Response | null>;
26
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../src/server/mcp/router.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEnD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,+CAA+C;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,gDAAgD;IAChD,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IACtD,UAAU,EAAE,UAAU,CAAA;IACtB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAOD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,aAAa,EACnB,IAAI,GAAE,gBAAqB,GAC1B,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA0E5C"}
@@ -0,0 +1,81 @@
1
+ import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
2
+ import { McpSessionMap } from './session-map.js';
3
+ import { createAgentMcpServer } from './server.js';
4
+ const DEFAULT_CONNECT_DESCRIPTION = 'Connect to the app. Call once per chat when the user pastes a token from the app connect panel. ' +
5
+ 'Returns {state, actions, description, context} so you can start acting immediately — ' +
6
+ 'no separate observe call needed on the first turn.';
7
+ /**
8
+ * Build a WHATWG-compatible MCP router that mounts at `opts.path`.
9
+ * Integrates into the agent core's fetch-style router by prepending
10
+ * this function's result in the request chain.
11
+ *
12
+ * Uses `WebStandardStreamableHTTPServerTransport` (WHATWG, runtime-
13
+ * neutral) rather than the Node-only `StreamableHTTPServerTransport`.
14
+ */
15
+ export function createMcpRouter(deps, opts = {}) {
16
+ const mcpPath = opts.path ?? '/agent/mcp';
17
+ const serverName = opts.serverName ?? 'agent';
18
+ const serverVersion = opts.serverVersion ?? '1';
19
+ const connectDescription = opts.connectDescription ?? DEFAULT_CONNECT_DESCRIPTION;
20
+ const lapBasePath = deps.lapBasePath;
21
+ const sessionMap = new McpSessionMap();
22
+ // mcp-session-id → active transport. Populated on initialize,
23
+ // cleaned up on DELETE or transport close.
24
+ const transports = new Map();
25
+ return async (req) => {
26
+ const url = new URL(req.url);
27
+ if (!url.pathname.startsWith(mcpPath))
28
+ return null;
29
+ const sessionHeader = req.headers.get('mcp-session-id');
30
+ // ── Existing session ───────────────────────────────────────────
31
+ if (sessionHeader) {
32
+ const transport = transports.get(sessionHeader);
33
+ if (!transport) {
34
+ // Unknown session ID — reject so the client can reinitialize.
35
+ return new Response(JSON.stringify({ error: 'session not found' }), {
36
+ status: 404,
37
+ headers: { 'content-type': 'application/json' },
38
+ });
39
+ }
40
+ return transport.handleRequest(req);
41
+ }
42
+ // ── New session (no mcp-session-id) ───────────────────────────
43
+ // Only POST (initialize) should arrive without a session ID.
44
+ if (req.method !== 'POST') {
45
+ return new Response(JSON.stringify({ error: 'mcp-session-id required' }), {
46
+ status: 400,
47
+ headers: { 'content-type': 'application/json' },
48
+ });
49
+ }
50
+ const transport = new WebStandardStreamableHTTPServerTransport({
51
+ sessionIdGenerator: () => crypto.randomUUID(),
52
+ onsessioninitialized: (id) => {
53
+ transports.set(id, transport);
54
+ },
55
+ onsessionclosed: (id) => {
56
+ transports.delete(id);
57
+ sessionMap.delete(id);
58
+ },
59
+ });
60
+ transport.onclose = () => {
61
+ const id = transport.sessionId;
62
+ if (id) {
63
+ transports.delete(id);
64
+ sessionMap.delete(id);
65
+ }
66
+ };
67
+ const mcpServer = createAgentMcpServer({
68
+ coreRouter: deps.coreRouter,
69
+ tokenStore: deps.tokenStore,
70
+ sessionMap,
71
+ getSessionId: () => transport.sessionId,
72
+ lapBasePath,
73
+ serverName,
74
+ serverVersion,
75
+ connectDescription,
76
+ });
77
+ await mcpServer.connect(transport);
78
+ return transport.handleRequest(req);
79
+ };
80
+ }
81
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../../src/server/mcp/router.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js'\nimport { McpSessionMap } from './session-map.js'\nimport { createAgentMcpServer } from './server.js'\nimport type { TokenStore } from '../token-store.js'\n\nexport type McpRouterOptions = {\n /** Path prefix for the MCP endpoint. Default: '/agent/mcp'. */\n path?: string\n /** MCP server name shown in Claude Desktop. Default: 'agent'. */\n serverName?: string\n /** MCP server version string. Default: '1'. */\n serverVersion?: string\n /** Description for the connect_session tool. */\n connectDescription?: string\n}\n\nexport type McpRouterDeps = {\n coreRouter: (req: Request) => Promise<Response | null>\n tokenStore: TokenStore\n lapBasePath: string\n}\n\nconst DEFAULT_CONNECT_DESCRIPTION =\n 'Connect to the app. Call once per chat when the user pastes a token from the app connect panel. ' +\n 'Returns {state, actions, description, context} so you can start acting immediately — ' +\n 'no separate observe call needed on the first turn.'\n\n/**\n * Build a WHATWG-compatible MCP router that mounts at `opts.path`.\n * Integrates into the agent core's fetch-style router by prepending\n * this function's result in the request chain.\n *\n * Uses `WebStandardStreamableHTTPServerTransport` (WHATWG, runtime-\n * neutral) rather than the Node-only `StreamableHTTPServerTransport`.\n */\nexport function createMcpRouter(\n deps: McpRouterDeps,\n opts: McpRouterOptions = {},\n): (req: Request) => Promise<Response | null> {\n const mcpPath = opts.path ?? '/agent/mcp'\n const serverName = opts.serverName ?? 'agent'\n const serverVersion = opts.serverVersion ?? '1'\n const connectDescription = opts.connectDescription ?? DEFAULT_CONNECT_DESCRIPTION\n const lapBasePath = deps.lapBasePath\n\n const sessionMap = new McpSessionMap()\n\n // mcp-session-id → active transport. Populated on initialize,\n // cleaned up on DELETE or transport close.\n const transports = new Map<string, WebStandardStreamableHTTPServerTransport>()\n\n return async (req: Request): Promise<Response | null> => {\n const url = new URL(req.url)\n if (!url.pathname.startsWith(mcpPath)) return null\n\n const sessionHeader = req.headers.get('mcp-session-id')\n\n // ── Existing session ───────────────────────────────────────────\n if (sessionHeader) {\n const transport = transports.get(sessionHeader)\n if (!transport) {\n // Unknown session ID — reject so the client can reinitialize.\n return new Response(JSON.stringify({ error: 'session not found' }), {\n status: 404,\n headers: { 'content-type': 'application/json' },\n })\n }\n return transport.handleRequest(req)\n }\n\n // ── New session (no mcp-session-id) ───────────────────────────\n // Only POST (initialize) should arrive without a session ID.\n if (req.method !== 'POST') {\n return new Response(JSON.stringify({ error: 'mcp-session-id required' }), {\n status: 400,\n headers: { 'content-type': 'application/json' },\n })\n }\n\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: () => crypto.randomUUID(),\n onsessioninitialized: (id) => {\n transports.set(id, transport)\n },\n onsessionclosed: (id) => {\n transports.delete(id)\n sessionMap.delete(id)\n },\n })\n\n transport.onclose = () => {\n const id = transport.sessionId\n if (id) {\n transports.delete(id)\n sessionMap.delete(id)\n }\n }\n\n const mcpServer = createAgentMcpServer({\n coreRouter: deps.coreRouter,\n tokenStore: deps.tokenStore,\n sessionMap,\n getSessionId: () => transport.sessionId,\n lapBasePath,\n serverName,\n serverVersion,\n connectDescription,\n })\n\n await mcpServer.connect(transport)\n return transport.handleRequest(req)\n }\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { McpSessionMap } from './session-map.js';
3
+ import type { TokenStore } from '../token-store.js';
4
+ export type McpServerDeps = {
5
+ /** WHATWG router from the agent core — used to call LAP endpoints internally. */
6
+ coreRouter: (req: Request) => Promise<Response | null>;
7
+ tokenStore: TokenStore;
8
+ sessionMap: McpSessionMap;
9
+ /**
10
+ * Returns the MCP session ID for this server instance. Called lazily so
11
+ * the transport can assign the ID during `initialize` before any tool
12
+ * handler fires.
13
+ */
14
+ getSessionId: () => string | undefined;
15
+ lapBasePath: string;
16
+ serverName: string;
17
+ serverVersion: string;
18
+ connectDescription: string;
19
+ };
20
+ /**
21
+ * Build one `McpServer` instance for a single MCP session. Tool handlers
22
+ * call LAP endpoints via synthetic WHATWG Requests routed through
23
+ * `coreRouter` — no extra HTTP round-trip to localhost needed.
24
+ */
25
+ export declare function createAgentMcpServer(deps: McpServerDeps): McpServer;
26
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/server/mcp/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAQnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAGnD,MAAM,MAAM,aAAa,GAAG;IAC1B,iFAAiF;IACjF,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IACtD,UAAU,EAAE,UAAU,CAAA;IACtB,UAAU,EAAE,aAAa,CAAA;IACzB;;;;OAIG;IACH,YAAY,EAAE,MAAM,MAAM,GAAG,SAAS,CAAA;IACtC,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,kBAAkB,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,CAmEnE"}
@@ -0,0 +1,116 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { z } from 'zod';
3
+ import { FORWARDED_TOOL_DESCRIPTORS, DISCONNECT_SESSION_DESCRIPTOR, } from '../../mcp/tools.js';
4
+ import { verifyAndReadTid } from '../lap/describe.js';
5
+ /**
6
+ * Build one `McpServer` instance for a single MCP session. Tool handlers
7
+ * call LAP endpoints via synthetic WHATWG Requests routed through
8
+ * `coreRouter` — no extra HTTP round-trip to localhost needed.
9
+ */
10
+ export function createAgentMcpServer(deps) {
11
+ const server = new McpServer({ name: deps.serverName, version: deps.serverVersion }, { capabilities: { tools: {} } });
12
+ // ── connect_session ────────────────────────────────────────────────
13
+ server.registerTool('connect_session', {
14
+ description: deps.connectDescription,
15
+ inputSchema: z.object({
16
+ token: z.string().describe('Bearer token from the app connect panel'),
17
+ }).shape,
18
+ }, async ({ token }) => {
19
+ // Verify the token and extract the tid. We re-use the existing
20
+ // LAP auth helper by constructing a minimal synthetic Request.
21
+ const authReq = new Request('http://local/auth', {
22
+ headers: { authorization: `Bearer ${token}` },
23
+ });
24
+ const auth = await verifyAndReadTid(authReq, deps.tokenStore);
25
+ if (!auth.ok) {
26
+ return errorResult(auth.code === 'auth-failed'
27
+ ? 'Token is invalid or expired. Ask the user to copy a fresh token from the app.'
28
+ : `Auth failed: ${auth.code}`);
29
+ }
30
+ const sessionId = deps.getSessionId();
31
+ if (!sessionId)
32
+ return errorResult('MCP session not yet initialized — retry in a moment.');
33
+ deps.sessionMap.set(sessionId, { tid: auth.tid, token });
34
+ // Prefetch the initial observe bundle — same reason the bridge does
35
+ // this: Claude gets state + actions + description + context in one
36
+ // call, avoiding a follow-up round-trip.
37
+ const result = await lapCall(deps.coreRouter, token, deps.lapBasePath, '/observe', {});
38
+ if (!result.ok) {
39
+ deps.sessionMap.delete(sessionId);
40
+ return errorResult(`connect_session: observe failed — ${result.error}`);
41
+ }
42
+ return okResult({ status: 'connected', ...result.body });
43
+ });
44
+ // ── disconnect_session ─────────────────────────────────────────────
45
+ server.registerTool(DISCONNECT_SESSION_DESCRIPTOR.name, {
46
+ description: DISCONNECT_SESSION_DESCRIPTOR.description,
47
+ inputSchema: DISCONNECT_SESSION_DESCRIPTOR.schema.shape,
48
+ }, async () => {
49
+ const sessionId = deps.getSessionId();
50
+ if (sessionId)
51
+ deps.sessionMap.delete(sessionId);
52
+ return okResult({ status: 'disconnected' });
53
+ });
54
+ // ── forwarded tools ────────────────────────────────────────────────
55
+ for (const desc of FORWARDED_TOOL_DESCRIPTORS) {
56
+ registerForwardedTool(server, deps, desc);
57
+ }
58
+ return server;
59
+ }
60
+ function registerForwardedTool(server, deps, desc) {
61
+ server.registerTool(desc.name, { description: desc.description, inputSchema: desc.schema.shape }, async (args) => {
62
+ const sessionId = deps.getSessionId();
63
+ const session = sessionId ? deps.sessionMap.get(sessionId) : null;
64
+ if (!session) {
65
+ return errorResult('Not connected — ask the user to copy the token from the app connect panel ' +
66
+ 'and call connect_session with it.');
67
+ }
68
+ const result = await lapCall(deps.coreRouter, session.token, deps.lapBasePath, desc.lapPath, (args ?? {}));
69
+ if (!result.ok)
70
+ return errorResult(`${desc.name}: ${result.error}`);
71
+ return okResult(result.body);
72
+ });
73
+ }
74
+ /**
75
+ * Call a LAP endpoint internally by constructing a synthetic WHATWG
76
+ * Request and routing it through the agent core's router. No actual
77
+ * HTTP round-trip — the router handles it in-process.
78
+ */
79
+ async function lapCall(coreRouter, token, lapBasePath, lapPath, body) {
80
+ const req = new Request(`http://local${lapBasePath}${lapPath}`, {
81
+ method: 'POST',
82
+ headers: {
83
+ authorization: `Bearer ${token}`,
84
+ 'content-type': 'application/json',
85
+ },
86
+ body: JSON.stringify(body),
87
+ });
88
+ try {
89
+ const res = await coreRouter(req);
90
+ if (!res)
91
+ return { ok: false, error: `no handler for ${lapPath}` };
92
+ const payload = (await res.json());
93
+ if (!res.ok || payload.error) {
94
+ const code = payload.error?.code ?? res.status;
95
+ const detail = payload.error?.detail ? ` — ${payload.error.detail}` : '';
96
+ return { ok: false, error: `status=${res.status} code=${code}${detail}` };
97
+ }
98
+ return { ok: true, body: payload };
99
+ }
100
+ catch (e) {
101
+ return { ok: false, error: String(e) };
102
+ }
103
+ }
104
+ function okResult(body) {
105
+ return {
106
+ structuredContent: body,
107
+ content: [{ type: 'text', text: JSON.stringify(body) }],
108
+ };
109
+ }
110
+ function errorResult(msg) {
111
+ return {
112
+ content: [{ type: 'text', text: msg }],
113
+ isError: true,
114
+ };
115
+ }
116
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/server/mcp/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAEnE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EACL,0BAA0B,EAC1B,6BAA6B,GAE9B,MAAM,oBAAoB,CAAA;AAG3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAmBrD;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAmB;IACtD,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,EACtD,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAChC,CAAA;IAED,sEAAsE;IACtE,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,WAAW,EAAE,IAAI,CAAC,kBAAkB;QACpC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;SACtE,CAAC,CAAC,KAAK;KACT,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAClB,+DAA+D;QAC/D,+DAA+D;QAC/D,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,mBAAmB,EAAE;YAC/C,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;SAC9C,CAAC,CAAA;QACF,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;QAC7D,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACb,OAAO,WAAW,CAChB,IAAI,CAAC,IAAI,KAAK,aAAa;gBACzB,CAAC,CAAC,+EAA+E;gBACjF,CAAC,CAAC,gBAAgB,IAAI,CAAC,IAAI,EAAE,CAChC,CAAA;QACH,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,EAAE,CAAA;QACrC,IAAI,CAAC,SAAS;YAAE,OAAO,WAAW,CAAC,sDAAsD,CAAC,CAAA;QAE1F,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAA;QAExD,oEAAoE;QACpE,mEAAmE;QACnE,yCAAyC;QACzC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,EAAE,CAAC,CAAA;QACtF,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;YACjC,OAAO,WAAW,CAAC,qCAAqC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA;QACzE,CAAC;QACD,OAAO,QAAQ,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,GAAI,MAAM,CAAC,IAAe,EAAE,CAAC,CAAA;IACtE,CAAC,CACF,CAAA;IAED,sEAAsE;IACtE,MAAM,CAAC,YAAY,CACjB,6BAA6B,CAAC,IAAI,EAClC;QACE,WAAW,EAAE,6BAA6B,CAAC,WAAW;QACtD,WAAW,EAAE,6BAA6B,CAAC,MAAM,CAAC,KAAK;KACxD,EACD,KAAK,IAAI,EAAE;QACT,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,EAAE,CAAA;QACrC,IAAI,SAAS;YAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QAChD,OAAO,QAAQ,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAA;IAC7C,CAAC,CACF,CAAA;IAED,sEAAsE;IACtE,KAAK,MAAM,IAAI,IAAI,0BAA0B,EAAE,CAAC;QAC9C,qBAAqB,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;IAC3C,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,qBAAqB,CAC5B,MAAiB,EACjB,IAAmB,EACnB,IAAgC;IAEhC,MAAM,CAAC,YAAY,CACjB,IAAI,CAAC,IAAI,EACT,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,EACjE,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,EAAE,CAAA;QACrC,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QACjE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,WAAW,CAChB,4EAA4E;gBAC1E,mCAAmC,CACtC,CAAA;QACH,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,OAAO,CAC1B,IAAI,CAAC,UAAU,EACf,OAAO,CAAC,KAAK,EACb,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,OAAO,EACZ,CAAC,IAAI,IAAI,EAAE,CAA4B,CACxC,CAAA;QACD,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,OAAO,WAAW,CAAC,GAAG,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA;QACnE,OAAO,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC9B,CAAC,CACF,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,OAAO,CACpB,UAAsD,EACtD,KAAa,EACb,WAAmB,EACnB,OAAe,EACf,IAA6B;IAE7B,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,eAAe,WAAW,GAAG,OAAO,EAAE,EAAE;QAC9D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,KAAK,EAAE;YAChC,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAA;IACF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAA;QACjC,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,kBAAkB,OAAO,EAAE,EAAE,CAAA;QAClE,MAAM,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAkD,CAAA;QACnF,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,EAAE,IAAI,IAAI,GAAG,CAAC,MAAM,CAAA;YAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;YACxE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,GAAG,CAAC,MAAM,SAAS,IAAI,GAAG,MAAM,EAAE,EAAE,CAAA;QAC3E,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;IACpC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;IACxC,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,IAAa;IAC7B,OAAO;QACL,iBAAiB,EAAE,IAA+B;QAClD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;KACxD,CAAA;AACH,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;QACtC,OAAO,EAAE,IAAI;KACd,CAAA;AACH,CAAC","sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport { z } from 'zod'\nimport {\n FORWARDED_TOOL_DESCRIPTORS,\n DISCONNECT_SESSION_DESCRIPTOR,\n type McpForwardedToolDescriptor,\n} from '../../mcp/tools.js'\nimport type { McpSessionMap } from './session-map.js'\nimport type { TokenStore } from '../token-store.js'\nimport { verifyAndReadTid } from '../lap/describe.js'\n\nexport type McpServerDeps = {\n /** WHATWG router from the agent core — used to call LAP endpoints internally. */\n coreRouter: (req: Request) => Promise<Response | null>\n tokenStore: TokenStore\n sessionMap: McpSessionMap\n /**\n * Returns the MCP session ID for this server instance. Called lazily so\n * the transport can assign the ID during `initialize` before any tool\n * handler fires.\n */\n getSessionId: () => string | undefined\n lapBasePath: string\n serverName: string\n serverVersion: string\n connectDescription: string\n}\n\n/**\n * Build one `McpServer` instance for a single MCP session. Tool handlers\n * call LAP endpoints via synthetic WHATWG Requests routed through\n * `coreRouter` — no extra HTTP round-trip to localhost needed.\n */\nexport function createAgentMcpServer(deps: McpServerDeps): McpServer {\n const server = new McpServer(\n { name: deps.serverName, version: deps.serverVersion },\n { capabilities: { tools: {} } },\n )\n\n // ── connect_session ────────────────────────────────────────────────\n server.registerTool(\n 'connect_session',\n {\n description: deps.connectDescription,\n inputSchema: z.object({\n token: z.string().describe('Bearer token from the app connect panel'),\n }).shape,\n },\n async ({ token }) => {\n // Verify the token and extract the tid. We re-use the existing\n // LAP auth helper by constructing a minimal synthetic Request.\n const authReq = new Request('http://local/auth', {\n headers: { authorization: `Bearer ${token}` },\n })\n const auth = await verifyAndReadTid(authReq, deps.tokenStore)\n if (!auth.ok) {\n return errorResult(\n auth.code === 'auth-failed'\n ? 'Token is invalid or expired. Ask the user to copy a fresh token from the app.'\n : `Auth failed: ${auth.code}`,\n )\n }\n\n const sessionId = deps.getSessionId()\n if (!sessionId) return errorResult('MCP session not yet initialized — retry in a moment.')\n\n deps.sessionMap.set(sessionId, { tid: auth.tid, token })\n\n // Prefetch the initial observe bundle — same reason the bridge does\n // this: Claude gets state + actions + description + context in one\n // call, avoiding a follow-up round-trip.\n const result = await lapCall(deps.coreRouter, token, deps.lapBasePath, '/observe', {})\n if (!result.ok) {\n deps.sessionMap.delete(sessionId)\n return errorResult(`connect_session: observe failed — ${result.error}`)\n }\n return okResult({ status: 'connected', ...(result.body as object) })\n },\n )\n\n // ── disconnect_session ─────────────────────────────────────────────\n server.registerTool(\n DISCONNECT_SESSION_DESCRIPTOR.name,\n {\n description: DISCONNECT_SESSION_DESCRIPTOR.description,\n inputSchema: DISCONNECT_SESSION_DESCRIPTOR.schema.shape,\n },\n async () => {\n const sessionId = deps.getSessionId()\n if (sessionId) deps.sessionMap.delete(sessionId)\n return okResult({ status: 'disconnected' })\n },\n )\n\n // ── forwarded tools ────────────────────────────────────────────────\n for (const desc of FORWARDED_TOOL_DESCRIPTORS) {\n registerForwardedTool(server, deps, desc)\n }\n\n return server\n}\n\nfunction registerForwardedTool(\n server: McpServer,\n deps: McpServerDeps,\n desc: McpForwardedToolDescriptor,\n): void {\n server.registerTool(\n desc.name,\n { description: desc.description, inputSchema: desc.schema.shape },\n async (args) => {\n const sessionId = deps.getSessionId()\n const session = sessionId ? deps.sessionMap.get(sessionId) : null\n if (!session) {\n return errorResult(\n 'Not connected — ask the user to copy the token from the app connect panel ' +\n 'and call connect_session with it.',\n )\n }\n const result = await lapCall(\n deps.coreRouter,\n session.token,\n deps.lapBasePath,\n desc.lapPath,\n (args ?? {}) as Record<string, unknown>,\n )\n if (!result.ok) return errorResult(`${desc.name}: ${result.error}`)\n return okResult(result.body)\n },\n )\n}\n\n/**\n * Call a LAP endpoint internally by constructing a synthetic WHATWG\n * Request and routing it through the agent core's router. No actual\n * HTTP round-trip — the router handles it in-process.\n */\nasync function lapCall(\n coreRouter: (req: Request) => Promise<Response | null>,\n token: string,\n lapBasePath: string,\n lapPath: string,\n body: Record<string, unknown>,\n): Promise<{ ok: true; body: unknown } | { ok: false; error: string }> {\n const req = new Request(`http://local${lapBasePath}${lapPath}`, {\n method: 'POST',\n headers: {\n authorization: `Bearer ${token}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(body),\n })\n try {\n const res = await coreRouter(req)\n if (!res) return { ok: false, error: `no handler for ${lapPath}` }\n const payload = (await res.json()) as { error?: { code: string; detail?: string } }\n if (!res.ok || payload.error) {\n const code = payload.error?.code ?? res.status\n const detail = payload.error?.detail ? ` — ${payload.error.detail}` : ''\n return { ok: false, error: `status=${res.status} code=${code}${detail}` }\n }\n return { ok: true, body: payload }\n } catch (e) {\n return { ok: false, error: String(e) }\n }\n}\n\nfunction okResult(body: unknown): CallToolResult {\n return {\n structuredContent: body as Record<string, unknown>,\n content: [{ type: 'text', text: JSON.stringify(body) }],\n }\n}\n\nfunction errorResult(msg: string): CallToolResult {\n return {\n content: [{ type: 'text', text: msg }],\n isError: true,\n }\n}\n"]}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Per-MCP-session binding. Populated by `connect_session` and read by
3
+ * every forwarded tool handler. Keyed by the SDK-assigned MCP session ID
4
+ * (`mcp-session-id` response header / request header).
5
+ */
6
+ export type McpSession = {
7
+ /** Token record ID resolved at connect_session time. */
8
+ tid: string;
9
+ /** Bearer token — used to construct synthetic LAP requests. */
10
+ token: string;
11
+ };
12
+ export declare class McpSessionMap {
13
+ private map;
14
+ set(mcpSessionId: string, session: McpSession): void;
15
+ get(mcpSessionId: string): McpSession | null;
16
+ delete(mcpSessionId: string): void;
17
+ }
18
+ //# sourceMappingURL=session-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-map.d.ts","sourceRoot":"","sources":["../../../src/server/mcp/session-map.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAA;IACX,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,GAAG,CAAgC;IAE3C,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI;IAIpD,GAAG,CAAC,YAAY,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAI5C,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;CAGnC"}
@@ -0,0 +1,13 @@
1
+ export class McpSessionMap {
2
+ map = new Map();
3
+ set(mcpSessionId, session) {
4
+ this.map.set(mcpSessionId, session);
5
+ }
6
+ get(mcpSessionId) {
7
+ return this.map.get(mcpSessionId) ?? null;
8
+ }
9
+ delete(mcpSessionId) {
10
+ this.map.delete(mcpSessionId);
11
+ }
12
+ }
13
+ //# sourceMappingURL=session-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-map.js","sourceRoot":"","sources":["../../../src/server/mcp/session-map.ts"],"names":[],"mappings":"AAYA,MAAM,OAAO,aAAa;IAChB,GAAG,GAAG,IAAI,GAAG,EAAsB,CAAA;IAE3C,GAAG,CAAC,YAAoB,EAAE,OAAmB;QAC3C,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IACrC,CAAC;IAED,GAAG,CAAC,YAAoB;QACtB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,IAAI,CAAA;IAC3C,CAAC;IAED,MAAM,CAAC,YAAoB;QACzB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;IAC/B,CAAC;CACF","sourcesContent":["/**\n * Per-MCP-session binding. Populated by `connect_session` and read by\n * every forwarded tool handler. Keyed by the SDK-assigned MCP session ID\n * (`mcp-session-id` response header / request header).\n */\nexport type McpSession = {\n /** Token record ID resolved at connect_session time. */\n tid: string\n /** Bearer token — used to construct synthetic LAP requests. */\n token: string\n}\n\nexport class McpSessionMap {\n private map = new Map<string, McpSession>()\n\n set(mcpSessionId: string, session: McpSession): void {\n this.map.set(mcpSessionId, session)\n }\n\n get(mcpSessionId: string): McpSession | null {\n return this.map.get(mcpSessionId) ?? null\n }\n\n delete(mcpSessionId: string): void {\n this.map.delete(mcpSessionId)\n }\n}\n"]}
@@ -7,6 +7,8 @@ import type { RateLimiter } from './rate-limit.js';
7
7
  import type { PairingRegistry } from './ws/pairing-registry.js';
8
8
  import type { AcceptResult } from './core.js';
9
9
  import type { PairingConnection } from './ws/pairing-registry.js';
10
+ import type { McpRouterOptions } from './mcp/router.js';
11
+ export type { McpRouterOptions };
10
12
  /**
11
13
  * Options accepted by `createLluiAgentServer`. All values are
12
14
  * optional and fall back to in-memory defaults. See spec §10.1.
@@ -33,6 +35,17 @@ export type ServerOptions = {
33
35
  slidingTtlMs?: number;
34
36
  /** Allowed origins for the HTTP surface (CORS). Empty = any. */
35
37
  corsOrigins?: readonly string[];
38
+ /**
39
+ * Enable the server-side MCP endpoint at `/agent/mcp` (or a custom
40
+ * path). When set, Claude Desktop can connect directly to the app
41
+ * backend without installing the `llui-agent` bridge — the user pastes
42
+ * the token via `connect_session` in chat, same flow as the bridge but
43
+ * no separate process required.
44
+ *
45
+ * Pass `true` to use all defaults, or an `McpRouterOptions` object to
46
+ * customise the path, server name, and connect_session description.
47
+ */
48
+ mcp?: boolean | McpRouterOptions;
36
49
  };
37
50
  /**
38
51
  * Value returned by `createLluiAgentServer`. `router` matches any
@@ -1 +1 @@
1
- {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/server/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAEjE;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,wDAAwD;IACxD,UAAU,CAAC,EAAE,UAAU,CAAA;IAEvB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IAEnC,kDAAkD;IAClD,SAAS,CAAC,EAAE,SAAS,CAAA;IAErB,qEAAqE;IACrE,WAAW,CAAC,EAAE,WAAW,CAAA;IAEzB,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAA;IAEvB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAA;IAErB,gEAAgE;IAChE,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAChC,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IAClD;;;;;;OAMG;IACH,SAAS,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAChF,oEAAoE;IACpE,QAAQ,EAAE,eAAe,CAAA;IACzB,8BAA8B;IAC9B,UAAU,EAAE,UAAU,CAAA;IACtB,6BAA6B;IAC7B,SAAS,EAAE,SAAS,CAAA;IACpB;;;;;;OAMG;IACH,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAA;CACpF,CAAA"}
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/server/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAEvD,YAAY,EAAE,gBAAgB,EAAE,CAAA;AAEhC;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,wDAAwD;IACxD,UAAU,CAAC,EAAE,UAAU,CAAA;IAEvB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IAEnC,kDAAkD;IAClD,SAAS,CAAC,EAAE,SAAS,CAAA;IAErB,qEAAqE;IACrE,WAAW,CAAC,EAAE,WAAW,CAAA;IAEzB,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAA;IAEvB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAA;IAErB,gEAAgE;IAChE,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAE/B;;;;;;;;;OASG;IACH,GAAG,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAA;CACjC,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IAClD;;;;;;OAMG;IACH,SAAS,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAChF,oEAAoE;IACpE,QAAQ,EAAE,eAAe,CAAA;IACzB,8BAA8B;IAC9B,UAAU,EAAE,UAAU,CAAA;IACtB,6BAA6B;IAC7B,SAAS,EAAE,SAAS,CAAA;IACpB;;;;;;OAMG;IACH,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAA;CACpF,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/server/options.ts"],"names":[],"mappings":"","sourcesContent":["import type { IncomingMessage } from 'node:http'\nimport type { Duplex } from 'node:stream'\nimport type { TokenStore } from './token-store.js'\nimport type { IdentityResolver } from './identity.js'\nimport type { AuditSink } from './audit.js'\nimport type { RateLimiter } from './rate-limit.js'\nimport type { PairingRegistry } from './ws/pairing-registry.js'\nimport type { AcceptResult } from './core.js'\nimport type { PairingConnection } from './ws/pairing-registry.js'\n\n/**\n * Options accepted by `createLluiAgentServer`. All values are\n * optional and fall back to in-memory defaults. See spec §10.1.\n *\n * Pre-0.0.35 this required a `signingKey` for HMAC-signed JWT tokens.\n * The new opaque-token scheme (token.ts) doesn't sign anything — the\n * server stores the SHA-256 hash and looks tokens up. The option is\n * gone; existing config that passed `signingKey` should drop it.\n */\nexport type ServerOptions = {\n /** Token store. Defaults to an `InMemoryTokenStore`. */\n tokenStore?: TokenStore\n\n /** Identity resolver. Defaults to anonymous (always null). */\n identityResolver?: IdentityResolver\n\n /** Audit sink. Defaults to `consoleAuditSink`. */\n auditSink?: AuditSink\n\n /** Rate limiter. Defaults to `defaultRateLimiter` with 30/minute. */\n rateLimiter?: RateLimiter\n\n /** Base path prefix for LAP endpoints. Defaults to `/agent/lap/v1`. */\n lapBasePath?: string\n\n /** Pairing grace window after a tab closes, in ms. Default 15 min. */\n pairingGraceMs?: number\n\n /** Sliding TTL for active tokens, in ms. Default 1 h. */\n slidingTtlMs?: number\n\n /** Allowed origins for the HTTP surface (CORS). Empty = any. */\n corsOrigins?: readonly string[]\n}\n\n/**\n * Value returned by `createLluiAgentServer`. `router` matches any\n * `/agent/*` request and returns a Response (or null to fall through).\n * `wsUpgrade` handles Node HTTP upgrade events for `/agent/ws`.\n */\nexport type AgentServerHandle = {\n router: (req: Request) => Promise<Response | null>\n /**\n * Handles Node HTTP upgrade events for `/agent/ws`. Returns a Promise\n * because token verification uses WebCrypto (async). Node's\n * `server.on('upgrade', handler)` fires the handler without awaiting,\n * which is fine — the handler writes errors directly to the socket\n * and never throws back to the caller.\n */\n wsUpgrade: (req: IncomingMessage, socket: Duplex, head: Buffer) => Promise<void>\n /** The pairing registry. Runtime-neutral adapters may access it. */\n registry: PairingRegistry\n /** The active token store. */\n tokenStore: TokenStore\n /** The active audit sink. */\n auditSink: AuditSink\n /**\n * Runtime-neutral WebSocket acceptance primitive. Validates a token\n * and registers a `PairingConnection` with the registry. The Node\n * `wsUpgrade` above calls this internally; web-runtime adapters\n * (`@llui/agent/server/web`) use it after accepting a WebSocket via\n * their native API.\n */\n acceptConnection: (token: string, conn: PairingConnection) => Promise<AcceptResult>\n}\n"]}
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/server/options.ts"],"names":[],"mappings":"","sourcesContent":["import type { IncomingMessage } from 'node:http'\nimport type { Duplex } from 'node:stream'\nimport type { TokenStore } from './token-store.js'\nimport type { IdentityResolver } from './identity.js'\nimport type { AuditSink } from './audit.js'\nimport type { RateLimiter } from './rate-limit.js'\nimport type { PairingRegistry } from './ws/pairing-registry.js'\nimport type { AcceptResult } from './core.js'\nimport type { PairingConnection } from './ws/pairing-registry.js'\nimport type { McpRouterOptions } from './mcp/router.js'\n\nexport type { McpRouterOptions }\n\n/**\n * Options accepted by `createLluiAgentServer`. All values are\n * optional and fall back to in-memory defaults. See spec §10.1.\n *\n * Pre-0.0.35 this required a `signingKey` for HMAC-signed JWT tokens.\n * The new opaque-token scheme (token.ts) doesn't sign anything — the\n * server stores the SHA-256 hash and looks tokens up. The option is\n * gone; existing config that passed `signingKey` should drop it.\n */\nexport type ServerOptions = {\n /** Token store. Defaults to an `InMemoryTokenStore`. */\n tokenStore?: TokenStore\n\n /** Identity resolver. Defaults to anonymous (always null). */\n identityResolver?: IdentityResolver\n\n /** Audit sink. Defaults to `consoleAuditSink`. */\n auditSink?: AuditSink\n\n /** Rate limiter. Defaults to `defaultRateLimiter` with 30/minute. */\n rateLimiter?: RateLimiter\n\n /** Base path prefix for LAP endpoints. Defaults to `/agent/lap/v1`. */\n lapBasePath?: string\n\n /** Pairing grace window after a tab closes, in ms. Default 15 min. */\n pairingGraceMs?: number\n\n /** Sliding TTL for active tokens, in ms. Default 1 h. */\n slidingTtlMs?: number\n\n /** Allowed origins for the HTTP surface (CORS). Empty = any. */\n corsOrigins?: readonly string[]\n\n /**\n * Enable the server-side MCP endpoint at `/agent/mcp` (or a custom\n * path). When set, Claude Desktop can connect directly to the app\n * backend without installing the `llui-agent` bridge — the user pastes\n * the token via `connect_session` in chat, same flow as the bridge but\n * no separate process required.\n *\n * Pass `true` to use all defaults, or an `McpRouterOptions` object to\n * customise the path, server name, and connect_session description.\n */\n mcp?: boolean | McpRouterOptions\n}\n\n/**\n * Value returned by `createLluiAgentServer`. `router` matches any\n * `/agent/*` request and returns a Response (or null to fall through).\n * `wsUpgrade` handles Node HTTP upgrade events for `/agent/ws`.\n */\nexport type AgentServerHandle = {\n router: (req: Request) => Promise<Response | null>\n /**\n * Handles Node HTTP upgrade events for `/agent/ws`. Returns a Promise\n * because token verification uses WebCrypto (async). Node's\n * `server.on('upgrade', handler)` fires the handler without awaiting,\n * which is fine — the handler writes errors directly to the socket\n * and never throws back to the caller.\n */\n wsUpgrade: (req: IncomingMessage, socket: Duplex, head: Buffer) => Promise<void>\n /** The pairing registry. Runtime-neutral adapters may access it. */\n registry: PairingRegistry\n /** The active token store. */\n tokenStore: TokenStore\n /** The active audit sink. */\n auditSink: AuditSink\n /**\n * Runtime-neutral WebSocket acceptance primitive. Validates a token\n * and registers a `PairingConnection` with the registry. The Node\n * `wsUpgrade` above calls this internally; web-runtime adapters\n * (`@llui/agent/server/web`) use it after accepting a WebSocket via\n * their native API.\n */\n acceptConnection: (token: string, conn: PairingConnection) => Promise<AcceptResult>\n}\n"]}