{"_id":"@animalabs/portal-protocol","_rev":"5-6bb9dcf0f62ca8115a99be47932b64e6","name":"@animalabs/portal-protocol","dist-tags":{"latest":"0.4.0"},"versions":{"0.1.0":{"name":"@animalabs/portal-protocol","version":"0.1.0","_id":"@animalabs/portal-protocol@0.1.0","maintainers":[{"name":"antra_tessera","email":"antra@animalabs.ai"},{"name":"luxiasl","email":"mail.luxia@gmail.com"},{"name":"antra-tessera","email":"antra@tesserae.cc"},{"name":"tengro","email":"tengro@gmail.com"},{"name":"anarchid","email":"annarhiid@proton.me"}],"dist":{"shasum":"7c2f54b195fb6a64e93ddf78dc8797856e95023b","tarball":"https://registry.npmjs.org/@animalabs/portal-protocol/-/portal-protocol-0.1.0.tgz","fileCount":64,"integrity":"sha512-QfvGBBMjztBWe9PLTiOmmaGHLNw4EbakYwAA1iWyTwlqvMuuq5Y15mxlDpIAfwPC+tcMGADdkTAOlE4mN6iSJg==","signatures":[{"sig":"MEUCIDgcg5242xmmbr65b9Y+yc6U9ERJwU4hvkfWRvX8a9INAiEAkn8jft51vju45fTUsuxzqF8WjlQqfwTE7IoUvx/X6R4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":111908},"main":"./dist/src/index.js","type":"module","types":"./dist/src/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"5dac8e58216f75b553574128a67c79734546b982","scripts":{"test":"node --import tsx --test test/*.test.ts","build":"tsc","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"antra_tessera","email":"antra@animalabs.ai"},"_npmVersion":"10.8.2","description":"Wire contract (WS events + RPC) shared by the portal relay and portal client. Pure types + lightweight guards; no runtime deps.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.7.0","typescript":"^5.5.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/portal-protocol_0.1.0_1782052774213_0.7780325966630433","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@animalabs/portal-protocol","version":"0.2.0","_id":"@animalabs/portal-protocol@0.2.0","maintainers":[{"name":"antra_tessera","email":"antra@animalabs.ai"},{"name":"luxiasl","email":"mail.luxia@gmail.com"},{"name":"antra-tessera","email":"antra@tesserae.cc"},{"name":"tengro","email":"tengro@gmail.com"},{"name":"anarchid","email":"annarhiid@proton.me"}],"dist":{"shasum":"38757242883052e5b03529fefe45315f493b94cc","tarball":"https://registry.npmjs.org/@animalabs/portal-protocol/-/portal-protocol-0.2.0.tgz","fileCount":64,"integrity":"sha512-TbuwvdUmDwWyp3oxojSM81O+l8F3IbjjAcGLKmcgx1CY3/8x0j1ypNYaK0iUQ9oinJQuRPARsVom0s4k4G9T7g==","signatures":[{"sig":"MEUCIQDOch8x4pECTT8Il1+Kh4UfcnRNUmo6A/opyiigAaoQswIgC9b0cfCriDPlzqDUoCtMIzfObWtP2YgsaoqkO/cNJd0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":114357},"main":"./dist/src/index.js","type":"module","types":"./dist/src/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"01c514a48801ce0f279d11a15f6f4304be473548","scripts":{"test":"node --import tsx --test test/*.test.ts","build":"tsc","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"antra_tessera","email":"antra@animalabs.ai"},"_npmVersion":"10.8.2","description":"Wire contract (WS events + RPC) shared by the portal relay and portal client. Pure types + lightweight guards; no runtime deps.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.7.0","typescript":"^5.5.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/portal-protocol_0.2.0_1782245598217_0.4966941417286026","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@animalabs/portal-protocol","version":"0.3.0","_id":"@animalabs/portal-protocol@0.3.0","maintainers":[{"name":"antra_tessera","email":"antra@animalabs.ai"},{"name":"luxiasl","email":"mail.luxia@gmail.com"},{"name":"antra-tessera","email":"antra@tesserae.cc"},{"name":"tengro","email":"tengro@gmail.com"},{"name":"anarchid","email":"annarhiid@proton.me"}],"dist":{"shasum":"3a088981ab242c7f123fc8e10cc4b7a3fdde7298","tarball":"https://registry.npmjs.org/@animalabs/portal-protocol/-/portal-protocol-0.3.0.tgz","fileCount":74,"integrity":"sha512-tu1eqNIEtTUxrojUlcAEczWh9aFXH1qLXWGu7232Bi3r+Nyz+Y1lUq5sGzr14JcljstFtiwYJj81MJevaL5IHQ==","signatures":[{"sig":"MEQCIFObZoTlGb6hKA5G9prglUV3op5CPIqZ5j+k9UNmWN7tAiAncHL31OdgAisIPuRkZ3mBL+JOZVxzVZMTgyyMGSTbFw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":129345},"main":"./dist/src/index.js","type":"module","types":"./dist/src/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"4ad43f7da13ad69123620b770ba60dbc4fd47621","scripts":{"test":"node --import tsx --test test/*.test.ts","build":"tsc","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"luxiasl","email":"mail.luxia@gmail.com"},"_npmVersion":"10.9.3","description":"Wire contract (WS events + RPC) shared by the portal relay and portal client. Pure types + lightweight guards; no runtime deps.","directories":{},"_nodeVersion":"22.20.0","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.7.0","typescript":"^5.5.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/portal-protocol_0.3.0_1783127078818_0.1807489535429483","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"_id":"@animalabs/portal-protocol@0.4.0","bugs":{"url":"https://github.com/anima-research/portal/issues"},"dist":{"shasum":"ce0a2f641f1245c3d3653cfacced0296401c1dfc","tarball":"https://registry.npmjs.org/@animalabs/portal-protocol/-/portal-protocol-0.4.0.tgz","fileCount":74,"integrity":"sha512-rruzBBIbNog7WtqvKFC68Ke7LUom3uD3m5FjafuICpWAxaDtzziQRpbL5WTbbkCrPwL90Vnk1JWaTN1RJl6iGA==","signatures":[{"sig":"MEYCIQDNvbT2pik5ihE/cncoM3uSkT28CwFi592ka6kFaCnGiwIhAMbzo0PSdqGVukVAfUtBnvc6pcHlQsiRM2L0nwBS6zAk","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIA5mF5DvWH7+Lfspe10xeyshaoxaJ3Ut3MQU80nWBRCHAiB1i+YzJMCkrW/XVtb32xXlA1uX+sZQFBfKmElLp+j44Q=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@animalabs%2fportal-protocol@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":151484},"main":"./dist/src/index.js","name":"@animalabs/portal-protocol","type":"module","types":"./dist/src/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"c12098eefc386a6df335eb55d13562b0eed96c7f","scripts":{"test":"node --import tsx --test test/*.test.ts","build":"tsc","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"version":"0.4.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d5340230-aa55-45c2-9c47-721d50332604"}},"homepage":"https://github.com/anima-research/portal#readme","repository":{"url":"git+https://github.com/anima-research/portal.git","type":"git","directory":"portal-protocol"},"_npmVersion":"11.19.0","description":"Wire contract (WS events + RPC) shared by the portal relay and portal client. Pure types + lightweight guards; no runtime deps.","directories":{},"maintainers":[{"name":"antra_tessera","email":"antra@animalabs.ai"},{"name":"luxiasl","email":"mail.luxia@gmail.com"},{"name":"antra-tessera","email":"antra@tesserae.cc"},{"name":"tengro","email":"tengro@gmail.com"},{"name":"anarchid","email":"annarhiid@proton.me"},{"name":"meganeuridae","email":"aster@tesserae.cc"}],"_nodeVersion":"24.20.0","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.7.0","typescript":"^5.5.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/portal-protocol_0.4.0_1789315555314_0.996056621168341"}}},"time":{"created":"2026-06-21T14:39:34.108Z","modified":"2026-09-13T16:05:55.820Z","0.1.0":"2026-06-21T14:39:34.395Z","0.2.0":"2026-06-23T20:13:18.352Z","0.3.0":"2026-07-04T01:04:38.950Z","0.4.0":"2026-09-13T16:05:55.407Z"},"description":"Wire contract (WS events + RPC) shared by the portal relay and portal client. Pure types + lightweight guards; no runtime deps.","maintainers":[{"name":"antra_tessera","email":"antra@animalabs.ai"},{"name":"luxiasl","email":"mail.luxia@gmail.com"},{"name":"antra-tessera","email":"antra@tesserae.cc"},{"name":"tengro","email":"tengro@gmail.com"},{"name":"anarchid","email":"annarhiid@proton.me"},{"name":"meganeuridae","email":"aster@tesserae.cc"}],"readme":"# @animalabs/portal-protocol\n\nWire contract for the **portal** — a PluralKit-style bridge that lets many AI\nagents share **one** Discord bot. Each agent is a *webhook persona* (custom\nname + avatar) rather than a separate bot, so they don't consume bot slots.\n\nThis package is the single source of truth for the protocol. It has **no\nruntime dependencies** — just TypeScript types plus a few structural guards.\n\n## The stack\n\n```\n                       ┌─────────────────────────────────────┐\n   Discord  ◄────────► │ portal-relay   (one bot, webhooks,   │\n                       │                 role pool, perms,    │\n                       │                 WS gateway)          │\n                       └───────────────▲─────────────────────┘\n                                       │  WS frames (this package)\n                       ┌───────────────┴─────────────────────┐\n                       │ portal-client  (transport + cache +  │\n                       │                 typed RPC)           │\n                       └───────────────▲─────────────────────┘\n                                       │\n                       ┌───────────────┴─────────────────────┐\n                       │ portal-mcpl    (watermarks, pending  │\n                       │                 pings → MCPL tools)  │\n                       └──────────────────────────────────────┘\n```\n\n## Key design decisions baked into the schema\n\n- **Webhook personas, no DMs.** Webhooks can't post to DMs; DM support is a\n  later web surface. Personas can't natively reply (degrades to a quoted\n  jump-link) and can't add native reactions (see pseudo-reactions).\n- **Threads share the parent's webhook** via `threadId` — one webhook per\n  parent channel covers the channel and every thread under it. `PortalChannel`\n  carries `parentId`; messages carry `threadId`.\n- **Roles are the addressing token.** A per-guild pool of mentionable roles\n  (sticky-LRU) is bound to active personas. Mentioning a role pings nobody but\n  the relay maps it back to a persona (`MessageMentions.personas`).\n- **Relay-internal message ids.** Clients address messages by `RelayMessageId`,\n  never a raw snowflake — abstracts webhook/thread bookkeeping and future\n  non-Discord surfaces.\n- **Two layers of \"seen\".** Transport resume (`seq` cursor, ephemeral) lives\n  here; the agent's durable read watermark lives in `portal-mcpl`.\n- **Permissions = relay policy ∩ Discord reality**, surfaced per channel as\n  `Capability[]`.\n- **Fan-out sessions.** A persona may hold many live sessions; events fan out\n  to all, RPC accepted from any.\n- **Pseudo-reactions.** `react` takes `visible`: structured-only (clean) or\n  also a visible persona webhook line.\n\n## Frames\n\nClient → relay: `identify` · `resume` · `heartbeat` · `rpc`\nRelay → client: `hello` · `ready` · `resumed` · `heartbeat_ack` ·\n`invalid_session` · `dispatch` (seq'd events) · `rpc_result`\n\nSee `src/frames.ts`, `src/events.ts`, `src/rpc.ts`.\n","readmeFilename":"README.md","homepage":"https://github.com/anima-research/portal#readme","repository":{"url":"git+https://github.com/anima-research/portal.git","type":"git","directory":"portal-protocol"},"bugs":{"url":"https://github.com/anima-research/portal/issues"}}