You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit d515d2d
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: README.md
+10-2Lines changed: 10 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -57,6 +57,14 @@ MCP authorization): 24 slices, each merged with a merge commit and tagged `slice
57
57
protected server's `401` with "authorize" on the `/mcp` page and keeps the token it obtains.
58
58
Trinity issues no production authority: the personal issuer refuses to start under an
59
59
external authority adapter, by construction (`docs/07-security-model.md`).
60
+
-**Reachable from elsewhere.** A gateway is a channel Trinity answers from: the adapter carries
61
+
text, and everything else (the session, the gate, the receipts) is the same machinery the
62
+
desktop uses. A sender Trinity does not know gets a pairing code shown on the `/gateways` page
63
+
and nothing else, no session and no model; once paired they get slash commands, the answer
64
+
streaming back as the turn runs, and any approval the turn raises rendered into the same
65
+
conversation. What a channel may approve is capped below what the desktop may: a `write` at
66
+
most by default, so an `exec` or a `destructive` request is decided at the machine, and the
67
+
refusal is receipted. The console adapter ships (`mix trinity.console`); the platforms are next.
60
68
-**Runs on a schedule.** Tasks on the `/tasks` page: a prompt, a persona, the skills to hint,
61
69
and when (a cron expression, a one-shot time, or a phrase like "every weekday at 9am" the
62
70
model turns into cron). Each run is a fresh conversation you can open, its result waits on
@@ -65,7 +73,7 @@ MCP authorization): 24 slices, each merged with a merge commit and tagged `slice
65
73
curator that marks old memories stale and archives the untouched ones (never deleting)
66
74
run on the same queues; `/oban` shows the jobs.
67
75
68
-
Not there yet: messaging gateways and subagents (M5b),
76
+
Not there yet: the messaging platforms themselves and subagents (M5b),
69
77
the native desktop shell and signed releases (M6), executable skills in a sandbox (M7). `ROADMAP.md` carries the live status of every
70
78
slice, and the [Milestones](#milestones) section below explains how to read it.
71
79
@@ -107,7 +115,7 @@ model registry is `config/llm.exs`; keys come from the environment (`.env` is gi
107
115
pages: `/` conversations, `/s/:id` a conversation and `/s/:id/receipts` its receipts, `/search`
108
116
full-text search, `/personas` and `/memory` the persona and its memory (with the semantic tab and
109
117
the embedding model's download), `/skills` the skills, the changes waiting for your decision and
110
-
the learn form, `/mcp` the MCP servers you connect to (their health and the tools they
118
+
the learn form, `/gateways` the channels you can reach Trinity from, `/mcp` the MCP servers you connect to (their health and the tools they
111
119
contribute), `/permissions` the rules and pending approvals (a server's question to you, when
112
120
one of its tools asks for input mid-call, is answered there too), `/tasks` the scheduled tasks and
113
121
their results, `/oban` the jobs (in development, or when configured), `/settings` the export. `mix trinity.export` and `mix trinity.import` do what `/settings` does from a terminal;
|`Trinity.Skills`| SKILL.md parsing, registry, loader, manager, scanner (as built at 040: parser, sources, registry, index, the three tools; at 041: staging, promotion, manager, scanner, diff, learn, `skill_manage` and `learn`) | Repo, Permissions, **Effects**, **Receipts**, Sandbox (as built at 041: Tools, Memory, Permissions, Receipts and LLM; Effects is not a dependency: the promotion is not a tool call, it writes its own effect receipt; Tools never depends on Skills) |
92
93
|`Trinity.Scheduler`| Oban workers for agent tasks, delivery (as built at 050: the `tasks` and `task_runs` rows, `next_run_at` computed from a cron expression or a one-shot datetime, `Workers.Tick` (the one Cron plugin entry, every minute, enqueuing `Workers.RunTask` for what is due), `RunTask` (one turn in a fresh `origin: "cron"` session, the summary, the delivery), `Delivery` with `Delivery.Desktop`, `Parse.human/2` (a phrase to cron through the model, checked by Oban's parser); Oban itself on the app's repo, Lite on SQLite and Basic on Postgres, queues `agent_tasks`, `memory`, `maintenance`; the observer as `Trinity.Memory.ObserverWorker` and the curator as `Trinity.Memory.Curator` live in Memory) | Sessions, LLM, **Repo** (Gateways at 070) |
93
94
| `Trinity.MCP` | Client manager, tool bridge, server (as built at 059: the boundary alone, holding the core's version, its JSON depth and its telemetry event names; at 060: `Servers` and `ServerConfig` (the rows), `Supervisor` and `Boot` (one `Client` per enabled row), `Client` with its `Wire` (the outbound request, the headers, the core's decoder and validator) and two transports (`Transport.Stdio`, a child on a Port; `Transport.HTTP`, one POST per request), `Bridge` (the one tool module every MCP tool runs through) and `Client.Auth` (062's seam, a static token at 060); at 061: `Server` (the module above the core, `@behaviour BeamMCP.Server`, handed to both transports through `:server`; `tools/call` answered through the membrane, everything else the core's), `Server.Catalog` (the exported tools, sorted), `Server.Exports` (the configured names, `:catalog` refused), `Server.Session` (the `origin: "mcp"` session with its own persona), `Server.Envelope` and `Server.Replay` (the sealed `requestState` and the nonce table), `Server.Auth.Local` (the static bearer, 062's seam on the server side), `Server.Plug` (the `POST /mcp` endpoint, mounted in `TrinityWeb.Endpoint` ahead of the parsers) and `Server.Stdio` with `mix trinity.mcp.stdio`; at 062: `Trinity.MCP.Auth`, a boundary of its own under `Trinity.MCP` with `deps: []` (the behaviour `authorize/2` answering a `Principal`, never the token; `Config`, `Token`, `JWKS`, `Discovery`, `Scopes`; the profiles `Local`, `External` and `Embedded` with `Embedded.Keys`; the client role `Client` with `Client.Store`), and `AuthHost` in `Trinity.MCP` (the configuration from the tree's paths and the authority selection, the personal profile's boot, the refusal receipts, the client role's flow for the page); `Server.Auth.Local` is gone, `Server.Plug` authorizes ahead of the transport and answers `401` with a challenge, `Server` checks scope before the gate) | Tools, **Effects**, **Permissions**, Memory (as built at 059: a top-level boundary, like `Trinity.Smoke`, with `deps: [Trinity, BeamMCP.JSON]`; the boundary compiler checks every call into the `beam_mcp` application and this boundary alone lists its modules; at 060 the deps are `[Trinity, BeamMCP.JSON, BeamMCP.Schema]`, Trinity's tools, permissions and receipts reached through `Trinity`'s exports, and `Trinity.Application` and `TrinityWeb` list `Trinity.MCP`; at 061 the core's `Server`, `Catalog`, `ToolSpec` and both transports are listed too, and `Trinity` exports `Tools.Registry` and `Effects.Runner` to it) |
|`Trinity.Gateways`| Adapter behaviour, router, allowlists, pairing (as built at 070: `Adapter` (the behaviour: `child_spec/1`, `capabilities/0`, `deliver/2`, `format/2`, `render_approval/2`, plus `name/1` deriving the row's adapter name from the module), `Console` (the in-process adapter the tests and `mix trinity.console` talk to), `Format` (chunking to the channel's limit, re-opening a split code fence, markdown reduced to text where a channel renders none, the default approval rendering), `Identity` and `Identities` (the `gateway_identities` rows, the pairing code and the allowlist), `Router` (one process: admission, then the token bucket, then commands, then the session; it subscribes to `session:<id>` and `approvals:<id>` per conversation and coalesces deltas into an edited message), `Commands` (the slash registry, including `/attach`, `/approve` and `/deny`), `Cap` (the channel trust cap) and `Receipts` (the capped-decision receipt)) | Sessions, **Permissions**, PubSub |
95
96
|`Trinity.Subagents`| Delegation, result collection | Sessions, Tools |
Copy file name to clipboardExpand all lines: docs/05-data-model.md
+21-1Lines changed: 21 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -173,7 +173,7 @@ One row per request, the audit trail this slice owns; slice 024 reads it for dec
173
173
| prompt | text | the user message of the run's turn |
174
174
| persona_id | fk personas, nullable | the default persona when unset |
175
175
| skill_names | {array, string} | hinted to the model in the prompt |
176
-
| deliver_to | map |`%{"kind" => "desktop"}` at 050; gateways name their kind at 070|
176
+
| deliver_to | map |`%{"kind" => "desktop"}` at 050; at 070 `%{"kind" => "gateway", "adapter" => "console", "conversation" => "c-1"}` names a channel|
177
177
| enabled | boolean | the tick enqueues enabled tasks only; a `once` task is disabled once enqueued |
178
178
| timeout_ms | integer | one run's bound (600 000 by default) |
179
179
| last_run_at, next_run_at | timestamp |`next_run_at` is computed by `Trinity.Scheduler` from the schedule, not mirrored from Oban: the tick (one Cron plugin entry a minute) enqueues what is due and advances it |
@@ -194,6 +194,26 @@ Oban's own `oban_jobs` (and `oban_peers`) carry the jobs; the pruner keeps a wee
194
194
The curator (slice 050) adds `stale_at` and `archived_at` to `memories` (docs above): stale is still
195
195
recalled and shown as such; archived leaves recall and stays in the row.
196
196
197
+
### gateway_identities (Slice 070, as built)
198
+
Who outside this machine may talk to Trinity. The key is the pair, because an external id means
199
+
nothing outside the platform that issued it.
200
+
201
+
| column | type | notes |
202
+
|---|---|---|
203
+
| adapter | string | the adapter's own name (`Trinity.Gateways.Adapter.name/1`: "console"), half the key |
204
+
| external_user_id | string | the platform's id for the person; unique with `adapter`|
205
+
| display_name | string, nullable | whatever the platform offered, for the page |
206
+
| state | string | "pending" (a code was shown) \| "paired" \| "revoked"; a revoked row is kept, so turning someone away is recorded rather than forgotten |
207
+
| code | string, nullable | the six-character pairing code, shown on `/gateways` and nowhere else; cleared once spent |
208
+
| code_expires_at | timestamp, nullable | ten minutes; a pending sender who comes back later is shown a new code rather than locked out |
209
+
| paired_at, revoked_at | timestamp, nullable ||
210
+
| last_conversation | string, nullable | where the pairing was asked from, so the answer goes back there |
211
+
212
+
A gateway session is an ordinary session with `origin` = the adapter's name and `origin_ref`
213
+
`%{"adapter", "conversation", "external_user_id"}` (010's columns; this slice adds no column to
214
+
`sessions`). A scheduled task delivers to one with
0 commit comments