Changelog
Reference

Addressing

Entity URLs, the gateway short form, stream paths, workspace keys, the segment grammar, and the addressing helpers in @teaspill/schema.

Every running agent is an entity with one URL-shaped id, and every stream and workspace it uses derives from that id. This page is the grammar and the derivation rules; for the mental model, read Entities & addressing.

All the derivation functions live in @teaspill/schema (addressing.ts). They are pure and validate their inputs — a malformed identifier throws AddressingError rather than reaching the catalog or a stream.

At a glance

ThingCanonical formExample (default tenant)
Entity URL (entityId)/t/<tenant>/a/<type>/<id>/t/default/a/researcher/01j9z8k3q…
Entity URL, gateway short form/a/<type>/<id>/a/researcher/01j9z8k3q…
Timeline stream (server key)/t/<tenant>/agents/<type>/<id>/timeline/t/default/agents/researcher/01j9…/timeline
Deltas stream/t/<tenant>/agents/<type>/<id>/deltas/t/default/agents/researcher/01j9…/deltas
Workspace stdout stream/t/<tenant>/workspaces/<name>/stdout/t/default/workspaces/a-researcher-01j9…/stdout
Gateway stream URL/streams + <streamPath>/streams/t/default/agents/…/timeline

The entity URL is the same string everywhere — the catalog row's primary key, the entityId in every event, and the value browsers hold to read a stream. There is no separate opaque id.

Segment grammar

Every segment — <tenant>, <type>, <id>, <name> — matches the same charset:

^[a-z0-9][a-z0-9_-]*$

Lowercase alphanumerics, -, and _; must start with an alphanumeric. No ., /, %, uppercase, or unicode inside a segment; / is the path separator only.

SegmentMax length
tenant32
type48
id64
workspace name64

Lowercase-only avoids "same entity, two URLs" bugs on case-insensitive filesystems, where the history store writes one file per stream. The length caps keep the full timeline stream path legible on disk.

The <tenant> segment is a deployment-level constant (TEASPILL_TENANT, default default). One deployment is one tenant, so in practice every identifier reads /t/default/…. The gateway accepts and emits the tenant-relative short form /a/<type>/<id> for ergonomics and expands it to the canonical form before touching anything durable.

Instance ids

  • Default: a lowercase ULID (26 chars, time-sortable). Generated by newInstanceId(). Sortable means ORDER BY id roughly matches creation order — convenient for listings and catalog queries.
  • Caller-supplied ids enable deterministic, idempotent spawn: key a child's id off (parent, role) and a retried spawn reattaches to the same instance instead of creating a duplicate. A supplied id must match the segment grammar (validated by assertInstanceId); the gateway rejects a non-conforming id with a 400.
Identity is the full URL (tenant, type, id). A second spawn with the same id lands on the same object and reattaches — it does not create a duplicate. See Multi-agent patterns for the deterministic-worker pattern.

Stream paths

Each stream is an independent append-only log. The paths below are the server keys; a client reaches them through the gateway at /streams + <path> (see the Gateway HTTP API).

StreamServer keyHelper
Timeline (authoritative history)/t/<tenant>/agents/<type>/<id>/timelinetimelineStreamPath(url)
Deltas (live token fragments)/t/<tenant>/agents/<type>/<id>/deltasdeltasStreamPath(url)
Workspace stdout (coarse)/t/<tenant>/workspaces/<name>/stdoutworkspaceStdoutStreamPath(key)
Workspace exec stdout (per-exec)/t/<tenant>/workspaces/<name>/exec/<runId>/stdoutworkspaceExecStdoutStreamPath(key, runId)

Note the collection segment is agents (plural) in a stream path versus the a marker in an entity URL — both derive from the same entity URL. Prefix any server key with /streams for the client-facing URL: gatewayStreamUrl(streamPath).

Workspace keys

A workspace key is <tenant>/<name>. The <name> comes from one of two places:

  • Private (default, one per entity): a-<type>-<id>, derived from the owning entity URL by privateWorkspaceKey(url). Unique by construction, traceable back to its agent.
  • Shared / named: a caller-supplied <name> passed at spawn (same segment grammar). Several entities may point at the same shared key; serialization still holds because it is keyed on the workspace, not the entity.

Helpers

Exported from @teaspill/schema.

FunctionReturns
entityUrl(tenant, type, id)Canonical entity URL.
parseEntityUrl(url){ tenant, type, id } (revalidates).
isEntityUrl(url)Boolean guard.
toHttpForm(url) / fromHttpForm(path, tenant?)Canonical ↔ gateway short form.
newInstanceId()A fresh lowercase ULID.
assertInstanceId(id)Throws unless the id matches the grammar.
timelineStreamPath(url) / deltasStreamPath(url)Server stream keys.
gatewayStreamUrl(streamPath)Prefix a server key with /streams.
workspaceKey(tenant, name) / parseWorkspaceKey(key)Build / split a workspace key.
privateWorkspaceKey(url)The default per-entity workspace key.
workspaceStdoutStreamPath(key) / workspaceExecStdoutStreamPath(key, runId)Workspace output stream keys.

The regexes (TENANT_RE, TYPE_RE, ID_RE, WORKSPACE_NAME_RE) and constants (DEFAULT_TENANT, GATEWAY_STREAMS_PREFIX) are exported too.