chore(docs): Add Documentation for Scopes (#1086)

* chore(docs): Add Documentation for Scopes

* chore(docs): add scopes to API reference, architecture, and design patterns

- Add the seven /scopes routes and their schemas to openapi.json, plus the
  scope/kind fields on chat, representation, session-create, and peer-list
  schemas; generate the endpoint pages and register a scopes nav group
- Add a Scopes subsection and diagram node to the architecture data model
- Add scope guidance to design patterns: quick-reference rows, an isolation
  boundary comparison (workspace / scope / session allowlist), and common
  mistakes (scope-per-reader, scopes-as-access-control)
- Replace the "Underneath the Facade" section in scopes.mdx with behavioral
  guardrails and pointers to the implementation source

* chore(docs): tighten scopes doc to decision-level detail

- Drop the recall-resolution diagram (restated the Two Arms table)
- Replace the enumerated Rules table with prose; caps and error shapes
  now live in the API reference schema descriptions
- Trim backfill/removal internals to observable behavior and note that a
  backfilled scope deepens through subsequent dreams

* chore(docs): reserve "scope" for the scopes feature

Using it as a verb for session design, recall filters, and CLI targeting
collides with the named-session-set feature.

* chore(docs): clarify the scopes page and document create/status responses

The page now leads with projection rather than partition and points at the
scopes API; OpenAPI declares the 201/409/404 those routes actually return.

* chore(docs): fix broken anchor and core-concepts link

The rebase reintroduced a link to a renamed anchor in scopes.mdx, and
unified-memory-setup pointed at /core-concepts/, which has no index page.

* chore(docs): correct scope arms, listing, and read-surface pointers

The Accepts row mixed named-scope with the allowlist arm, kind=scope on
the peers list does not return facade ids, and chat/context/search never
mentioned scope=.

* chore(docs): drop the 1k-token session batching narrative

Reasoning no longer waits on a per-session token threshold, so product
docs should not tell people to size sessions around that gate.

* chore: minor fix
This commit is contained in:
Vineeth Voruganti 2026-08-27 15:21:10 -04:00 committed by GitHub
parent e1537216cf
commit 4acd78d45f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 992 additions and 68 deletions

View File

@ -10,6 +10,10 @@
{
"source": "/v3/guides/integrations/claudecode",
"destination": "/v3/guides/integrations/claude-code"
},
{
"source": "/v3/documentation/features/advanced/representation-scopes",
"destination": "/v3/documentation/features/advanced/directional-representations"
}
],
"colors": {
@ -62,7 +66,8 @@
"v3/documentation/features/advanced/reasoning-configuration",
"v3/documentation/features/advanced/summarizer",
"v3/documentation/features/advanced/peer-card",
"v3/documentation/features/advanced/representation-scopes",
"v3/documentation/features/advanced/directional-representations",
"v3/documentation/features/advanced/scopes",
"v3/documentation/features/advanced/dreaming",
"v3/documentation/features/advanced/queue-status",
"v3/documentation/features/advanced/webhooks",
@ -208,6 +213,18 @@
"v3/api-reference/endpoint/sessions/search-session"
]
},
{
"group": "scopes",
"pages": [
"v3/api-reference/endpoint/scopes/get-or-create-scope",
"v3/api-reference/endpoint/scopes/get-scopes",
"v3/api-reference/endpoint/scopes/get-scope",
"v3/api-reference/endpoint/scopes/add-sessions-to-scope",
"v3/api-reference/endpoint/scopes/get-scope-sessions",
"v3/api-reference/endpoint/scopes/remove-session-from-scope",
"v3/api-reference/endpoint/scopes/get-scope-status"
]
},
{
"group": "messages",
"pages": [

View File

@ -0,0 +1,3 @@
---
openapi: post /v3/workspaces/{workspace_id}/scopes/{scope_id}/sessions
---

View File

@ -0,0 +1,3 @@
---
openapi: post /v3/workspaces/{workspace_id}/scopes
---

View File

@ -0,0 +1,3 @@
---
openapi: post /v3/workspaces/{workspace_id}/scopes/{scope_id}/sessions/list
---

View File

@ -0,0 +1,3 @@
---
openapi: get /v3/workspaces/{workspace_id}/scopes/{scope_id}/status
---

View File

@ -0,0 +1,3 @@
---
openapi: get /v3/workspaces/{workspace_id}/scopes/{scope_id}
---

View File

@ -0,0 +1,3 @@
---
openapi: post /v3/workspaces/{workspace_id}/scopes/list
---

View File

@ -0,0 +1,3 @@
---
openapi: delete /v3/workspaces/{workspace_id}/scopes/{scope_id}/sessions/{session_id}
---

View File

@ -109,7 +109,6 @@ Messages are stored but no observations, summaries, or representations are being
```bash
DERIVER_WORKERS=4
```
5. **Representation Batching** — By default the deriver buffers representation work until a work unit has accumulated enough tokens, set via `DERIVER_REPRESENTATION_BATCH_WORK_UNIT_TARGET_TOKENS` (`0` disables the accumulation gate). A separate setting, `DERIVER_REPRESENTATION_BATCH_TARGET_INPUT_TOKENS`, caps the conversation window fed to each deriver LLM call when draining a claimed work unit. Sub-threshold tails become eligible after `DERIVER_REPRESENTATION_BATCH_MAX_AGE_SECONDS` (default 1800 seconds), so quiet sessions eventually flush without disabling batching globally. Set the age to `0` for legacy behavior where sub-threshold tails wait indefinitely. See [token batching](/v3/documentation/core-concepts/reasoning#token-batching) for more details
## Alternative Provider Issues

View File

@ -34,7 +34,7 @@ Honcho has a hierarchical data model centered around the entities below.
Workspaces are the top-level containers in Honcho. They provide complete isolation between different applications or environments, essentially serving as a namespace to keep different workloads separate. You might use separate workspaces for development, staging, and production environments, or to isolate different product lines. They also enable multi-tenant SaaS applications where each customer gets their own isolated workspace with complete data separation.
Authentication is scoped to the workspace level, and configuration settings can be applied workspace-wide to control behavior across all peers and sessions within that workspace.
Authentication is issued at the workspace level, and configuration settings can be applied workspace-wide to control behavior across all peers and sessions within that workspace.
---
@ -50,12 +50,14 @@ You can use peers for any entity that persists over time--individual users in ch
### <Icon icon="message" /> Sessions
Sessions represent interaction threads or contexts between peers. A session can involve multiple peers and provides temporal boundaries for when a set of interactions starts and ends. This lets you scope context and memory to specific interactions while still maintaining longer-term peer representations that span sessions.
Sessions represent interaction threads or contexts between peers. A session can involve multiple peers and provides temporal boundaries for when a set of interactions starts and ends. This lets you confine context and memory to specific interactions while still maintaining longer-term peer representations that span sessions.
Use sessions to scope things like support tickets, meeting transcripts, learning sessions, or conversations. You can also use single-peer sessions as a way to import external data--create a session with just one peer and structure emails, documents, or files as messages to enrich that peer's representation.
Use sessions for things like support tickets, meeting transcripts, learning sessions, or conversations. You can also use single-peer sessions as a way to import external data--create a session with just one peer and structure emails, documents, or files as messages to enrich that peer's representation.
Session-level configuration gives you fine-grained control over perspective-taking behavior. You can configure whether a peer should form representations of other peers in the session, and whether other peers should form representations of them.
Sessions are also the unit of visibility: when one peer's history spans contexts that shouldn't inform each other, you can group sessions into named [scopes](/v3/documentation/features/advanced/scopes) that bound recall to just those sessions.
---
### <Icon icon="envelope" /> Messages
@ -84,7 +86,7 @@ Honcho runs as two cooperating processes: an **API server** that handles request
**Write path (synchronous).** A message is stored and a reasoning task is enqueued in the same request; the API returns immediately. Nothing about the reasoning that follows blocks the caller.
**Deriver + Summarizer (async, per-message).** The worker picks up queued tasks in small batches. The Deriver reads new messages and extracts conclusions about the peer--explicit statements and direct deductions. In parallel, the Summarizer periodically rolls up recent messages into short- and long-form session summaries. Both run per-message (well, per-batch) rather than on a schedule.
**Deriver + Summarizer (async, per-message).** The worker picks up queued tasks. The Deriver reads new messages and extracts conclusions about the peer--explicit statements and direct deductions. In parallel, the Summarizer periodically rolls up recent messages into short- and long-form session summaries. Both run per-message rather than on a schedule.
**Dreamer (periodic).** On a schedule (or triggered on demand), the Dreamer revisits existing conclusions to consolidate and deepen them: removing redundant or stale ones, drawing inductive conclusions across patterns that span multiple messages, and updating peer cards--compact biographical summaries of a peer. This is where memory gets richer over time, not just larger.

View File

@ -12,22 +12,26 @@ Ready to add Honcho to your codebase? The **`/honcho-integration` skill** applie
## Quick Reference
**Workspaces isolate, peers persist, and sessions scope the active context.**
**Workspaces isolate, peers persist, and sessions bound the active context.**
| Decision | Recommendation |
|----------|---------------|
| How many workspaces? | One workspace per application, tool, tenant, or collaboration boundary. Split workspaces only when you need hard isolation between products, customers, environments, or agents. |
| When should agents share a workspace? | When agents collaborate over the same product, project, team, user, customer, or game state. Separate them when they should not see or influence each other's memory. |
| Who should be a peer? | Any persistent participant whose messages should be attributed or reasoned about: users, agents, assistants, NPCs, students, or customers. Use one peer for the same entity across sessions and platforms. |
| How should I scope sessions? | Scope sessions to the active interaction: per-conversation, per-channel, per-task run, per-project, per-import, or other bounded context. Reuse a session when local context should keep accumulating. |
| How should I divide sessions? | Match each session to the active interaction: per-conversation, per-channel, per-task run, per-project, per-import, or other bounded context. Reuse a session when local context should keep accumulating. |
| How does cross-session reasoning work? | Session memory stays local to one session. Peer representations accumulate across every session where the peer is included, and `session.context()` becomes cross-session when you include a peer target. |
| Should I set `observe_me: false`? | Yes, for deterministic peers Honcho does not need to model, like bots or tool agents. Still save their messages so other peers have session context. Keep it enabled for users and evolving agents. |
| Do I need `observe_others`? | Only when a peer needs its own perspective on another participant, such as in games, multi-agent systems, or parent/subagent workflows. |
| When do I need a scope? | When one peer's history spans contexts that must not leak into each other's recall — but you still want one workspace and one unified peer. Group the confidential sessions into a [scope](/v3/documentation/features/advanced/scopes) and pass it at query time. |
| Perspectives or scopes? | `observe_others` gives a *participant* its own view of another peer. A scope bounds recall to *where things were said*, for a reader that isn't a participant. If the reader is in the session, use perspectives; if you're fencing off a set of sessions, use a scope. |
## Workspace Design
A workspace is a hard isolation boundary. **Default to one workspace per application,** and split only at a real privacy, compliance, or product boundary (e.g. per-tenant SaaS, or a tool that needs intentionally isolated memory). Agents that collaborate over the same product, user, or game state belong in the *same* workspace so each can retrieve what the others produced.
If what you actually need is "this part of a peer's history shouldn't inform that assistant," don't split the workspace — that severs the peer's identity too. Use a [scope](/v3/documentation/features/advanced/scopes) instead: the peer stays whole, and recall through the scope sees only its member sessions.
Honcho plugins default to one workspace *per host* (`hermes`, `claude_code`, `cursor`, `opencode`). To unify memory across them, point each at the same workspace — see [Unified Memory Setup](/v3/guides/recipes/unified-memory-setup).
<Info>
@ -48,11 +52,11 @@ For unified context across Honcho plugins, set the same user peer ID (`peerName`
## Session Design
Sessions define the temporal boundaries of an interaction. How you scope them affects how summaries are generated, how context is retrieved, and when reasoning fires.
Sessions define the temporal boundaries of an interaction. Where you draw those boundaries affects how summaries are generated and how context is retrieved.
**Common session patterns**
| Pattern | Session scoped to | Example |
| Pattern | Session covers | Example |
|---------|-------------------|---------|
| Per-conversation | Each new chat thread | ChatGPT or Claude Code style UI where each thread is a session |
| Per-channel | A persistent channel or room | Discord channel, Slack thread |
@ -62,10 +66,6 @@ Sessions define the temporal boundaries of an interaction. How you scope them af
Create a **new** session when context resets (new conversation, new day, new topic); **reuse** one when context should keep accumulating (ongoing channel, persistent thread).
<Warning>
**Don't scope sessions too thin.** Honcho batches reasoning until a peer accumulates ~1,000 tokens *within a single session*, with a default age-based flush for quiet tails ([token batching](/v3/documentation/core-concepts/reasoning#token-batching)). Low-volume or trickle inputs should still append to one ongoing session rather than fragment across many, so reasoning runs with useful context instead of many small delayed batches.
</Warning>
**How cross-session reasoning works**
- **Session memory** is local to an interaction — summaries and recent-message context describe only what happened there.
@ -75,14 +75,33 @@ So you can start a session fresh or pull in a peer's long-term memory. [`session
---
## Choosing an Isolation Boundary
Honcho gives you three boundaries at different strengths. Pick the weakest one that solves your problem:
| Boundary | Strength | Use when |
|----------|----------|----------|
| **Workspace** | Hard isolation — nothing crosses, including the peer itself | Different products, tenants, or environments |
| **[Scope](/v3/documentation/features/advanced/scopes)** | Recall boundary — one peer, but queries through the scope see only its sessions | One peer's contexts must not leak into each other (clinical vs. billing, per-reseller support) |
| **Session allowlist** (`sessions=[...]`) | Ad-hoc recall restriction, decided per request | The session set varies per query, or you need a quick boundary without provisioning anything |
Two things scopes are **not**:
- **Not authorization.** A workspace key reads any session, scoped or not. A scope constrains queries that name it; it doesn't protect data from queries that don't.
- **Not topic filtering.** Scopes bound recall by *where something was said*, not what it's about. A therapy detail mentioned in a billing session lands in the billing scope. If you might ever need a scope boundary, align your session boundaries with your confidentiality boundaries from the start — the session is the unit scopes can enforce.
---
## Common Mistakes
- **Splitting one identity across peer IDs** -- If the same user is `alice`, `alice-discord`, and `alice-cursor`, Honcho builds separate representations. Use one stable peer ID when you want unified memory.
- **Too many tiny sessions** -- Summaries and recent messages are session-scoped, and reasoning only fires past ~1,000 tokens per session. Splitting a continuous conversation across many sessions fragments local context and can stall reasoning. Reuse a session when context should flow continuously.
- **Too many tiny sessions** -- Summaries and recent messages are local to one session. Splitting a continuous conversation across many sessions fragments that local context. Reuse a session when context should flow continuously.
- **Separating agents that should collaborate** -- If agents need shared product, customer, or team context, put them in the same workspace. Separate workspaces are hard isolation boundaries.
- **Leaving `observe_me` on for assistants** -- Wastes reasoning compute on a peer you control. Deterministic behavior doesn't need to be modeled.
- **Turning on `observe_others` everywhere** -- Directional representations are powerful, but they add complexity. Use them when peers need distinct perspectives, not just because a session has multiple peers.
- **Forgetting `peer_target` on session context** -- `session.context()` defaults to the active session's summary and recent messages, which are session-scoped. It becomes cross-session only through adding a peer_target which includes the peer representation.
- **A scope per reader** -- Scopes should map to real confidentiality boundaries, not to consumers. If every assistant gets its own scope, you've rebuilt workspace fragmentation inside one workspace, and each projection reasons over a thin slice. Fewer, boundary-shaped scopes; many readers can share one.
- **Treating scopes as access control** -- A scope bounds *recall*, not *access*. Enforce who may query what in your application layer; use scopes to keep the answers themselves from drawing on out-of-bounds sessions.
- **Forgetting `peer_target` on session context** -- `session.context()` defaults to the active session's summary and recent messages, which are local to that session. It becomes cross-session only through adding a peer_target which includes the peer representation.
- **Blocking on processing** -- Messages are processed asynchronously in the background. Don't poll or wait for reasoning to complete before continuing your application flow.
## Next Steps
@ -94,6 +113,9 @@ So you can start a session fresh or pull in a peer's long-term memory. [`session
<Card title="Get Context" icon="messages" href="/v3/documentation/features/get-context">
Retrieve formatted context from sessions for your LLM
</Card>
<Card title="Scopes" icon="shield-halved" href="/v3/documentation/features/advanced/scopes">
Bound recall to named sets of sessions
</Card>
<Card title="Chat Endpoint" icon="comments" href="/v3/documentation/features/chat">
Query Honcho about your peers with natural language
</Card>

View File

@ -66,21 +66,11 @@ The reasoning outputs--conclusions, summaries, peer cards--are stored as part of
The diagram above shows how agents write messages to Honcho, which triggers reasoning that updates peer representations. Agents can then query representations to get additional context for their next response.
### Token Batching
Rather than running inference on every individual message, Honcho accumulates messages in the queue and processes them as a batch once the total token count of pending messages for a given peer representation crosses a threshold--roughly **1,000 tokens** at the current batch size. This keeps ingestion costs down, since Honcho charges based on reasoning passes, and ensures each pass has a meaningful amount of context to work with. At ~1,000 tokens the batch comfortably fits in the context window of any modern LLM, so no content is lost.
If a user sends several short messages in a row (e.g., "yes", "ok", "sounds good"), those messages sit in the queue until enough content has accumulated. Once the threshold is met, the full batch is processed together in a single reasoning call.
<Note>
This batching only applies to **representation** tasks (conclusion extraction). Summary and dream tasks have their own scheduling logic and are not subject to the token threshold.
</Note>
## Balances & Design Choices
Off-the-shelf LLMs can perform formal logical reasoning, but they aren't optimized for it. Honcho uses custom models trained specifically for logical rigor (following formal reasoning rules rather than plausible-sounding text), structured output (consistent JSON schema with premises and conclusions), and efficiency (smaller, faster models tuned for this specific task). This allows Honcho to reason more reliably and at lower cost than general-purpose frontier LLMs.
The approach balances quality with practical constraints. Custom models are smaller and cheaper to run, scaffolded conclusions are more token-efficient than raw conversation history, and we batch where appropriate to optimize update frequency.
The approach balances quality with practical constraints. Custom models are smaller and cheaper to run, and scaffolded conclusions are more token-efficient than raw conversation history.
Honcho's reasoning capabilities are actively being improved. Current areas of development include enhanced inductive and abductive reasoning, multi-hop and temporal reasoning, and expanded file types and modalities. The system is designed to be extensible--new reasoning capabilities can be added without breaking existing functionality.

View File

@ -1,6 +1,6 @@
---
title: 'Representation Scopes'
description: 'Advanced configuration and querying for representations'
title: 'Directional Representations'
description: 'How peers build and query representations of other peers'
icon: 'circle'
---
@ -214,7 +214,7 @@ Most applications don't need directional representations. Start with the default
Under the hood, Honcho stores representations as (observer, observed) pairs in internal collections:
- **Collection**: A unique (observer, observed, workspace) tuple containing documents
- **Documents**: Individual conclusions and artifacts (deductive, inductive, abductive conclusions, summaries, peer cards) with session scoping
- **Documents**: Individual conclusions and artifacts (deductive, inductive, abductive conclusions, summaries, peer cards) with per-session filtering
When you retrieve with `target`, Honcho fetches documents from the specific (observer, observed) collection. When you retrieve without `target`, it fetches from the (peer, peer) collection—the peer's self-representation.
@ -225,7 +225,7 @@ This architecture enables:
## Semantic Search Parameters
Both `representation()` and `chat()` support semantic filtering to retrieve a subset of relevant conclusions. You can optionally filter by session — pass `session` to scope to a single session, or use the REST-only [session allowlist](/v3/documentation/features/advanced/using-filters#scoping-recall-to-sessions) to scope to a set of sessions:
Both `representation()` and `chat()` support semantic filtering to retrieve a subset of relevant conclusions. You can optionally filter by session — pass `session` to restrict to a single session, or use the REST-only [session allowlist](/v3/documentation/features/advanced/using-filters#scoping-recall-to-sessions) to restrict to a set of sessions:
| Parameter | Type | Description |
|-----------|------|-------------|
@ -265,7 +265,7 @@ Directional representations update automatically through the reasoning pipeline
2. The message sender has `observe_me=true` (or session-level equivalent)
3. Other peers in the session have `observe_others=true`
The pipeline respects scoping—Honcho's representations reason over messages across all sessions, while directional representations only reason over messages from sessions where the observer was an active participant.
The pipeline respects these boundaries—Honcho's representations reason over messages across all sessions, while directional representations only reason over messages from sessions where the observer was an active participant.
### Peer Join Order Matters

View File

@ -12,7 +12,8 @@ Advanced features give you fine-grained control over Honcho's behavior and imple
- [Configuration](/v3/documentation/features/advanced/reasoning-configuration) - Configure reasoning models and behavior
- [Summarizer](/v3/documentation/features/advanced/summarizer) - Automatic session summarization
- [Peer Card](/v3/documentation/features/advanced/peer-card) - Quick-reference profile of stable biographical facts about a peer
- [Representation Scopes](/v3/documentation/features/advanced/representation-scopes) - Directional representations for multi-peer scenarios
- [Directional Representations](/v3/documentation/features/advanced/directional-representations) - How peers build separate representations of each other
- [Scopes](/v3/documentation/features/advanced/scopes) - Named sets of sessions that act as visibility boundaries for recall
- [Dreaming](/v3/documentation/features/advanced/dreaming) - Autonomous memory consolidation and self-improvement
- [Queue Status](/v3/documentation/features/advanced/queue-status) - Monitor background processing and reasoning tasks

View File

@ -79,7 +79,7 @@ console.log(card);
## Directional Peer Cards
Peer cards follow the same observer-observed model as [representations](/v3/documentation/features/advanced/representation-scopes). When `observe_others` is enabled, a peer can have a **different** card for each peer it observes.
Peer cards follow the same observer-observed model as [representations](/v3/documentation/features/advanced/directional-representations). When `observe_others` is enabled, a peer can have a **different** card for each peer it observes.
For example, if Alice and Bob are in a session together and Alice has `observe_others: true`, Alice will build her own peer card for Bob--separate from Honcho's peer card for Bob. You can read and write these directional cards using the `target` parameter.

View File

@ -8,9 +8,9 @@ Whenever messages are stored in Honcho, background processes kick off to [reason
Reasoning is an asynchronous process and will not immediately
generate insights for the latest message you've sent. This is
by design: we want to reason efficiently over batches of messages
rather than assessing each message in a vacuum. Honcho provides
several utilities to check the status of the queue.
by design: Honcho reasons in the background rather than on the
write path. Honcho provides several utilities to check the status
of the queue.
<CodeGroup>
```python Python
@ -95,7 +95,7 @@ not the total number of items ever processed.
</Note>
The `queue_status` method can take additional
parameters to scope the status to a specific work unit:
parameters to filter the status by a matching observer, sender, or session:
<CodeGroup>
```python Python

View File

@ -157,7 +157,7 @@ You may therefore disable observation of a peer by setting the `observe_me` flag
If the peer has a session-level configuration, it will override this configuration. If the flag is not set, or is set to `true`, the peer will be observed.
<Info>
For session-level observation controls and local representations (where peers build separate models of each other), see [Representation Scopes](/v3/documentation/features/advanced/representation-scopes).
For session-level observation controls and local representations (where peers build separate models of each other), see [Directional Representations](/v3/documentation/features/advanced/directional-representations).
</Info>
<CodeGroup>

View File

@ -0,0 +1,355 @@
---
title: 'Scopes'
description: 'Named sets of sessions that act as visibility boundaries for recall'
icon: 'shield-halved'
---
A **scope** is a named set of sessions that acts as a visibility boundary. Recall
performed through a scope sees only what happened in that scope's sessions,
while the peer keeps its single unified representation of everything it has ever
participated in.
Use scopes when one peer's history spans contexts that must not leak into each
other — a therapy app where the clinical sessions must not inform the billing
assistant, a support product where a reseller's agent may only answer from its
own tickets, a multi-tenant deployment where one human works across tenants.
## Projection, Not Partition
The peer keeps one representation. A scope is a **projection** of it: a view
built only from evidence in the member sessions.
```mermaid
graph TB
P[Peer: user-123<br/>one unified representation]
P --> S1[session: therapy-1]
P --> S2[session: therapy-2]
P --> S3[session: billing-1]
P --> S4[session: onboarding-1]
SC1[scope: therapy] -.->|projects| S1
SC1 -.->|projects| S2
SC2[scope: billing] -.->|projects| S3
style P fill:#B6DBFF,stroke:#333,color:#000
style S1 fill:#B6DBFF,stroke:#333,color:#000
style S2 fill:#B6DBFF,stroke:#333,color:#000
style S3 fill:#B6DBFF,stroke:#333,color:#000
style S4 fill:#B6DBFF,stroke:#333,color:#000
style SC1 fill:#FFE0B2,stroke:#333,color:#000
style SC2 fill:#FFE0B2,stroke:#333,color:#000
```
- **Sessions can belong to more than one scope.** Membership is many-to-many.
- **Sessions can belong to no scope.** `onboarding-1` above is reachable
by an unscoped request and by nothing else.
- **An unscoped request still sees everything.** A scope constrains the requests
that name it; it does not hide the sessions from requests that don't.
<Warning>
Scopes are a recall boundary, not an authorization boundary. Who may call the
API is still governed by workspace, session, and peer keys.
</Warning>
## The Two Arms
There are two ways to confine recall, and they behave differently. Picking the
wrong one is the most common mistake with this feature.
| | `scope="therapy"` (named scope) | `sessions=[...]` / `scope=["a","b"]` (allowlist) |
|---|---|---|
| **Mechanism** | Reads the scope's own representation of the peer | Restricts the peer's own representation to a set of sessions |
| **Conclusions** | All levels — `explicit`, plus `deductive` / `inductive` reasoned **within** the scope | `explicit` only |
| **Reasoning chains** | Available | Unavailable |
| **Setup required** | Yes — create the scope, add sessions, wait for backfill | None — pass session IDs ad hoc |
| **Accepts** | One scope name | A list of up to 100 scope names, or up to 1,000 session IDs |
### Named scope: depth
Passing a **single** scope name swaps the observer. Recall runs against the
scope's own view of the target peer, which the deriver and dreamer have been
building from the scope's member sessions all along. That view contains
higher-order inferences — but only ones reasoned from evidence inside the scope.
```python
answer = user.chat("What is stressing them out?", scope="therapy")
```
This is the arm you want for a durable, meaningful boundary.
### Allowlist: breadth
Passing a **list** of scopes, or a bare list of session IDs, keeps the peer as
the observer and restricts recall to the union of those sessions. Because a
dream-derived conclusion is synthesized across sessions, it cannot be attributed
to any one of them — so this arm recalls `explicit` conclusions only, and answers
from directly-stated facts rather than inference.
```python
answer = user.chat("What did they say about billing?", sessions=[s1, s2])
answer = user.chat("What did they say?", scope=["therapy", "intake"])
```
Reach for this when the set of sessions is decided per-request, or when you want
a quick boundary without provisioning a scope. See
[Scoping Recall to Sessions](/v3/documentation/features/advanced/using-filters#scoping-recall-to-sessions)
for the full allowlist rules.
<Info>
A list of scopes is the allowlist arm, not "several named scopes at once". It
gives you the union of their *sessions*, at explicit-only depth — it does not
give you the union of their reasoned views. If you need depth, query one scope.
</Info>
## Creating a Scope and Managing Membership
<CodeGroup>
```python Python
from honcho import Honcho
honcho = Honcho(workspace_id="my-app")
# Get or create — idempotent; passing metadata updates the existing scope
therapy = honcho.scope("therapy")
# Add existing sessions (max 100 per call)
therapy.add_sessions(["therapy-session-1", "therapy-session-2"])
# Or attach at session creation — the scope is created if it doesn't exist
session = honcho.session("therapy-session-3", scopes=["therapy"])
# Inspect
for s in therapy.sessions():
print(s.id)
therapy.remove_session("therapy-session-1")
for scope in honcho.scopes():
print(scope.id, scope.metadata)
```
```typescript TypeScript
import { Honcho } from "@honcho-ai/sdk";
const honcho = new Honcho({ workspaceId: "my-app" });
// Get or create — idempotent; passing metadata updates the existing scope
const therapy = await honcho.scope("therapy");
// Add existing sessions (max 100 per call)
await therapy.addSessions(["therapy-session-1", "therapy-session-2"]);
// Or attach at session creation — the scope is created if it doesn't exist
const session = await honcho.session("therapy-session-3", {
scopes: ["therapy"],
});
// Inspect
for await (const s of await therapy.sessions()) {
console.log(s.id);
}
await therapy.removeSession("therapy-session-1");
for await (const scope of await honcho.scopes()) {
console.log(scope.id, scope.metadata);
}
```
```bash REST
# Get or create (201 created / 200 existing)
curl -X POST "$HONCHO_URL/v3/workspaces/my-app/scopes" \
-H "Authorization: Bearer $HONCHO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"id": "therapy"}'
# Add sessions
curl -X POST "$HONCHO_URL/v3/workspaces/my-app/scopes/therapy/sessions" \
-H "Authorization: Bearer $HONCHO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"session_ids": ["therapy-session-1", "therapy-session-2"]}'
# List membership
curl -X POST "$HONCHO_URL/v3/workspaces/my-app/scopes/therapy/sessions/list" \
-H "Authorization: Bearer $HONCHO_API_KEY"
# Remove one session
curl -X DELETE "$HONCHO_URL/v3/workspaces/my-app/scopes/therapy/sessions/therapy-session-1" \
-H "Authorization: Bearer $HONCHO_API_KEY"
```
</CodeGroup>
Scope IDs are unprefixed, must match `^[a-zA-Z0-9_-]+$`, and are at most 506
characters. Get-or-create is idempotent: if the scope already exists, the same
call returns it, and any `metadata` you pass is written onto it.
<Note>
Every scopes route — and every read that passes `scope` — requires a
**workspace-level or admin key**. A scope's membership can exceed any single
peer's own session membership, so peer- and session-scoped keys are rejected
with `401`.
</Note>
## Membership Changes Copy, They Don't Re-Derive
A session added to a scope while empty needs nothing special: messages sent
after the change flow into the scope through the normal deriver fan-out.
A session that **already has messages** is handled retroactively by a background
job rather than by re-running the LLM over its history: adding it copies the
session's existing `explicit` conclusions into the scope, and removing it
retracts that session's contributions — including conclusions derived from them.
Copying rather than re-deriving is why membership changes are cheap and
deterministic — and why they are also **asynchronous**. It also means a freshly
backfilled scope starts at explicit depth and accrues deeper reasoning through
subsequent dreams.
Poll `status()` to tell "the scope hasn't caught up yet" apart from "the scope
has caught up and there is genuinely nothing to recall":
<CodeGroup>
```python Python
therapy.add_sessions(["old-session-with-history"])
status = therapy.status()
# {"old-session-with-history": {"state": "pending", "updated_at": "..."}}
# → later: {"state": "completed", "docs_copied": 42, "updated_at": "..."}
```
```typescript TypeScript
await therapy.addSessions(["old-session-with-history"]);
const status = await therapy.status();
// { "old-session-with-history": { state: "pending", updatedAt: "..." } }
```
```bash REST
curl "$HONCHO_URL/v3/workspaces/my-app/scopes/therapy/status" \
-H "Authorization: Bearer $HONCHO_API_KEY"
```
</CodeGroup>
`state` is `pending`, `completed`, or `failed`; `docs_copied` appears once a
backfill completes. Only sessions that have had a backfill enqueued appear, so an
empty result means none have — not that the scope is empty.
## Reading Through a Scope
`scope` is accepted on these surfaces:
| Surface | Accepts | Notes |
|---------|---------|-------|
| [`peer.chat()`](/v3/documentation/features/chat) | one scope or a list | Confines both conclusion recall and the messages the agent reads |
| `peer.representation()` | one scope or a list | Confines conclusion recall |
| [`session.context()`](/v3/documentation/features/get-context) | one scope only | Perspective source for `peer_target`'s representation and card. Requires `peer_target`; mutually exclusive with `peer_perspective` |
| `honcho.search()` | one scope only | Restricts message search to the scope's member sessions |
| `honcho.chat()` | one scope or a list | Always the allowlist arm — even a single name. There is no observer to swap |
<CodeGroup>
```python Python
# Chat — answered only from the therapy sessions
answer = user.chat("What is stressing them out?", scope="therapy")
# Representation
rep = user.representation(scope="therapy")
# Session context, using the scope as the perspective source
ctx = session.context(peer_target="user-123", scope="therapy")
# Message search, restricted to the scope's sessions
messages = honcho.search("insomnia", scope="therapy")
```
```typescript TypeScript
// Chat — answered only from the therapy sessions
const answer = await user.chat("What is stressing them out?", {
scope: "therapy",
});
// Representation
const rep = await user.representation({ scope: "therapy" });
// Session context, using the scope as the perspective source
const ctx = await session.context({
peerTarget: "user-123",
scope: "therapy",
});
// Message search, restricted to the scope's sessions
const messages = await honcho.search("insomnia", { scope: "therapy" });
```
```bash REST
curl -X POST "$HONCHO_URL/v3/workspaces/my-app/peers/user-123/chat" \
-H "Authorization: Bearer $HONCHO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "What is stressing them out?", "scope": "therapy"}'
```
</CodeGroup>
### Rules
`scope` is mutually exclusive with `filters`, `sessions`, and `session` /
`session_id` — and on session context, with `peer_perspective` (where it also
requires `peer_target`). Like the session allowlist, it **fails closed**: a
contradiction is rejected with a `422` rather than silently widened, a scope
with no member sessions recalls nothing, and an empty list (`scope=[]`) is
rejected rather than treated as "no boundary". Per-surface caps and error
shapes are in the [API reference](/v3/api-reference/endpoint/scopes/get-or-create-scope).
## Provenance, Not Topic
A scope is defined by **where a fact was said**, not what it is about.
If a user mentions a therapy detail in a billing session, that conclusion is
formed from the billing session and lands in the `billing` scope. Querying
`scope="therapy"` will not find it, and querying `scope="billing"` will.
<Warning>
Scopes give you provenance-based privacy, not topic-based privacy. If you need
"no clinical content in the billing assistant's answers" regardless of where it
was said, that is content classification and has to be enforced above Honcho —
by controlling what reaches which session in the first place, or by filtering
the answer.
</Warning>
Design accordingly: keep the session boundary aligned with the confidentiality
boundary you actually care about, since that session boundary is the one scopes
can enforce.
## Guardrails
A few behaviors follow from how scopes are built:
- **The `scope.` prefix is reserved.** Creating a peer, or adding a peer to a
session, with a `scope.`-prefixed name is rejected.
- **List scopes through the scopes surface.** `honcho.scopes()` /
`POST /scopes/list` returns unprefixed ids. Peer listings hide scopes by
default; `kind="scope"` on `POST /peers/list` returns the backing peers named
`scope.<id>`, and `kind="all"` includes both regular peers and those backing
peers.
- **A scope can't be observed.** No representation is formed *of* a scope, so a
scope is rejected in any `target` / observed position, including as a dream
target.
- **Membership is managed only through the scopes surface.** The session
add-peers, set-peers, and remove-peers routes reject scope names and point you
at `/scopes/{scope_id}/sessions` or the `scopes` field on session create.
If you want the exact mechanics for scopes, read: [`src/routers/scopes.py`](https://github.com/plastic-labs/honcho/blob/main/src/routers/scopes.py),
[`src/crud/scope.py`](https://github.com/plastic-labs/honcho/blob/main/src/crud/scope.py),
and [`src/deriver/scope_backfill.py`](https://github.com/plastic-labs/honcho/blob/main/src/deriver/scope_backfill.py).
## Limits
| Limit | Value |
|-------|-------|
| Scope ID length | 506 characters |
| Scope ID charset | `^[a-zA-Z0-9_-]+$` |
| Sessions per membership call | 100 |
| Scopes in one `scope` read option | 100 |
| Scopes on session create | 100 |
| Sessions in a resolved allowlist | 1,000 |
Full request and response shapes are in the
[API reference](/v3/api-reference/endpoint/scopes/get-or-create-scope).

View File

@ -49,6 +49,20 @@ import { Honcho } from "@honcho-ai/sdk";
```
</CodeGroup>
Pass `scope` on workspace search to restrict matches to that
[scope](/v3/documentation/features/advanced/scopes)'s member sessions. A scope
with no members returns nothing.
<CodeGroup>
```python Python
results = honcho.search("budget planning", scope="therapy")
```
```typescript TypeScript
const results = await honcho.search("budget planning", { scope: "therapy" });
```
</CodeGroup>
### Session Search
Search within a specific session's conversation history:

View File

@ -727,7 +727,7 @@ messages = session.messages(filters={
### Filtering Conclusions
Conclusions are scoped to an observer/observed peer pair (accessed via
Conclusions belong to an observer/observed peer pair (accessed via
`peer.conclusions` for self-conclusions or `peer.conclusions_of(target)` for
conclusions about another peer). The observer and observed are filled in
automatically by the scope, so the `filters` you pass add to them.
@ -843,7 +843,7 @@ a **session allowlist**, restricting what the request can recall to the sessions
you name — conclusions on both endpoints, and on chat the messages the agent
reads as well.
This is how you scope recall to more than one session. The `session_id`
This is how you restrict recall to more than one session. The `session_id`
parameter pins a request to exactly one session; an allowlist accepts a set.
Only the `session_id` key is supported here, in three shapes:
@ -875,10 +875,34 @@ curl -X POST "$HONCHO_URL/v3/workspaces/my-app/peers/user-123/representation" \
```
</CodeGroup>
Both SDKs expose this as a `sessions` option, which goes on the wire as the
`filters` body above:
<CodeGroup>
```python Python
answer = user.chat("What did the user ask about billing?",
sessions=["support-chat-1", "support-chat-2"])
rep = user.representation(sessions=["support-chat-1", "support-chat-2"])
```
```typescript TypeScript
const answer = await user.chat("What did the user ask about billing?", {
sessions: ["support-chat-1", "support-chat-2"],
});
const rep = await user.representation({
sessions: ["support-chat-1", "support-chat-2"],
});
```
</CodeGroup>
<Note>
The session allowlist is REST-only today. The SDKs cover the single-session case
with `session`, but do not yet expose the allowlist — call the endpoint directly
when you need a set of sessions.
If the same set of sessions is a boundary you reuse, name it: a
[scope](/v3/documentation/features/advanced/scopes) is a persistent version of
this allowlist, and querying a single scope recalls at full depth rather than
`explicit`-only. `sessions` is the right tool when the set is decided
per-request.
</Note>
### Rules
@ -907,7 +931,7 @@ can only narrow.
### What Changes Under an Allowlist
Scoping recall by session narrows what the reasoning agent can draw on:
Restricting recall by session narrows what the reasoning agent can draw on:
- **Only `explicit` conclusions are recalled.** Dream-derived conclusions
(`deductive`, `inductive`) are synthesized across sessions, so they can't be

View File

@ -110,11 +110,17 @@ const answer = await peer.chat("What did the user ask about?", { session: sessio
```
</CodeGroup>
To scope a request to a *set* of sessions, use the session allowlist — a
To restrict a request to a *set* of sessions, use the session allowlist — a
constrained `filters` body on the endpoint. See
[Scoping Recall to Sessions](/v3/documentation/features/advanced/using-filters#scoping-recall-to-sessions)
for the accepted shapes and for what an allowlist changes about the answer.
Pass `scope="therapy"` to answer from that [scope](/v3/documentation/features/advanced/scopes)'s
own representation of the peer. A list (`scope=["therapy", "intake"]`) is an
allowlist of those scopes' sessions, not named-scope depth.
`honcho.chat(scope=)` is always the allowlist arm, even with one name. Details
are on the [scopes page](/v3/documentation/features/advanced/scopes#the-two-arms).
## Structured Outputs
When your application needs a machine-readable answer instead of prose, pass a schema as `response_format` and the answer is guaranteed to conform to it:

View File

@ -99,7 +99,7 @@ context = session.context(summary=False, tokens=2000)
### Peer Representation in Context
You can include a peer's [representation](/v3/documentation/core-concepts/representation) and peer card in the context by specifying `peer_target`. This is useful for providing the LLM with knowledge about a specific peer.
You can include a peer's [representation](/v3/documentation/core-concepts/representation) and peer card in the context by specifying `peer_target`. This is useful for providing the LLM with knowledge about a specific peer. Pass `scope` with `peer_target` to use a [named scope](/v3/documentation/features/advanced/scopes) as the perspective source (`scope` is mutually exclusive with `peer_perspective` and requires a workspace-level or admin-level key).
<CodeGroup>
```python Python
@ -119,6 +119,14 @@ context = session.context(
peer_target="user-123",
peer_perspective="assistant" # From assistant's viewpoint
)
# Or use a named scope as the perspective source (requires peer_target;
# mutually exclusive with peer_perspective)
context = session.context(
tokens=2000,
peer_target="user-123",
scope="therapy",
)
```
```typescript TypeScript
@ -139,6 +147,14 @@ context = session.context(
peerTarget: "user-123",
peerPerspective: "assistant" // From assistant's viewpoint
});
// Or use a named scope as the perspective source (requires peerTarget;
// mutually exclusive with peerPerspective)
const scopedContext = await session.context({
tokens: 2000,
peerTarget: "user-123",
scope: "therapy",
});
})();
```
</CodeGroup>
@ -211,6 +227,7 @@ context = session.context(
| `tokens` | `int` | Maximum tokens to include |
| `peer_target` | `str` | Peer ID to include representation for |
| `peer_perspective` | `str` | Peer ID for perspective (requires peer_target) |
| `scope` | `str` | Named scope as the perspective source for `peer_target`'s representation and card. Requires `peer_target` and a workspace-level or admin-level key; mutually exclusive with `peer_perspective`. See [Scopes](/v3/documentation/features/advanced/scopes) |
| `search_query` | `str` | Query for semantic search (requires peer_target) |
| `limit_to_session` | `bool` | Limit to session conclusions only |
| `search_top_k` | `int` | Semantic search results to include (1-100) |

View File

@ -89,14 +89,14 @@ and are stored under `oauth` without deleting a shared `apiKey`.
}
```
<Info>
Per-command scoping (workspace / peer / session) is handled via `-w` / `-p` / `-s`
Per-command targeting (workspace / peer / session) is handled via `-w` / `-p` / `-s`
flags or `HONCHO_*` env vars. **Not** persisted as CLI defaults. This is
deliberate: every invocation is explicit about what it operates on.
</Info>
### Runtime overrides
Workspace, peer, and session scoping are **per-command only** — pass flags or
Workspace, peer, and session targeting are **per-command only** — pass flags or
`HONCHO_*` env vars on every invocation.
```bash

View File

@ -23,7 +23,7 @@ The Honcho plugin is a community integration. See the [plugin README](https://gi
## How It Works
The extension hooks into pi's extension system. It automatically syncs user and assistant messages to Honcho after each agent response, injects cached user profile and project context into the system prompt with zero network latency, and exposes LLM tools (`honcho_search`, `honcho_chat`, `honcho_remember`) for active memory operations. Session scoping is configurable — memory can be shared per repo, per git branch, or per directory. If Honcho is unavailable, pi continues working normally.
The extension hooks into pi's extension system. It automatically syncs user and assistant messages to Honcho after each agent response, injects cached user profile and project context into the system prompt with zero network latency, and exposes LLM tools (`honcho_search`, `honcho_chat`, `honcho_remember`) for active memory operations. Session mapping is configurable — memory can be shared per repo, per git branch, or per directory. If Honcho is unavailable, pi continues working normally.
## Next Steps

View File

@ -61,11 +61,11 @@ In practice, that means agent peers can both be observed by Honcho and form repr
## How It Works
### Identity And Scope
### Identity And Mapping
The integration breaks down into four parts:
- **Identity and scope** - each Paperclip company maps to a Honcho workspace, agents and human actors map to peers, and issues map to sessions.
- **Identity and mapping** - each Paperclip company maps to a Honcho workspace, agents and human actors map to peers, and issues map to sessions.
- **What gets copied into Honcho** - issue comments and document revisions sync into Honcho, with document content sectioned and normalized message content capped before ingestion.
- **What operators get** - operators get a plugin settings page, migration preview/status data, including a per-issue migration mapping preview, repair tools, and an issue-level `Memory` tab.
- **What agents get** - agents get Honcho retrieval and peer-chat tools inside Paperclip.
@ -130,7 +130,7 @@ The plugin registers the following Honcho tools for Paperclip agents:
Review how workspaces, peers, and sessions fit together.
</Card>
<Card title="Representation Scopes" icon="messages" href="../../documentation/features/advanced/representation-scopes">
<Card title="Directional Representations" icon="messages" href="../../documentation/features/advanced/directional-representations">
Review how `observe_me` and `observe_others` change what peers can model.
</Card>
</CardGroup>

View File

@ -109,8 +109,9 @@ and `aiPeer` there. See the [Hermes guide](/v3/guides/integrations/hermes) for t
A scheduled job feeds external data (emails, meeting notes, CRM records) into Honcho.
Attribute the messages to the peer the data is *about* — not to an agent — and group
them into a session. **How you scope that session is the main decision here**, because
it controls when Honcho reasons over the data (more on that below).
them into a session. Match the session to how you want that import's local context
to accumulate: a per-run session like `email-import-{date}`, or one ongoing
per-source session like `email-import-gmail`.
```python
from datetime import datetime, timezone
@ -131,18 +132,6 @@ for i in range(0, len(messages), 100):
session.add_messages(messages[i:i + 100])
```
Honcho batches reasoning until a peer accumulates ~1,000 tokens *within a single session*,
with a default age-based flush for quiet tails
([token batching](/v3/documentation/core-concepts/reasoning#token-batching)). Scope the
session to the volume you ingest:
- **High-volume runs** (a day of emails, a CRM export) clear the threshold easily — a
per-run session like `email-import-{date}` is fine.
- **Low-volume or trickle imports** (a few short records at a time) should append to
one **ongoing per-source session** (e.g. `email-import-gmail`), so content
accumulates across runs instead of fragmenting into thin sessions that each flush
later with little context.
The [Gmail](/v3/guides/gmail) and [Granola](/v3/guides/granola) guides are related
import examples.

View File

@ -1574,7 +1574,7 @@
"get": {
"tags": ["sessions"],
"summary": "Get Peer Config",
"description": "Get the configuration for a Peer in a Session.\n\nMember-read lets a peer-scoped key reach this route, but a peer may only\nread its own per-session config not a co-member's. Workspace/admin and\nsession-scoped tokens (which already span the whole session) are unaffected.",
"description": "Get the configuration for a Peer in a Session.\n\nMember-read lets a peer-scoped key reach this route, but a peer may only\nread its own per-session config \u2014 not a co-member's. Workspace/admin and\nsession-scoped tokens (which already span the whole session) are unaffected.",
"operationId": "get_peer_config_v3_workspaces__workspace_id__sessions__session_id__peers__peer_id__config_get",
"security": [{ "HTTPBearer": [] }],
"parameters": [
@ -2234,6 +2234,343 @@
}
}
},
"/v3/workspaces/{workspace_id}/scopes": {
"post": {
"tags": ["scopes"],
"summary": "Get Or Create Scope",
"description": "Get a Scope by ID or create a new Scope with the given ID.\n\nReturns 201 when the scope is created and 200 when it already exists.\nA pre-existing peer occupying the scope's reserved internal name is never\nadopted; that conflict returns 409.",
"operationId": "get_or_create_scope_v3_workspaces__workspace_id__scopes_post",
"security": [{ "HTTPBearer": [] }],
"parameters": [
{
"name": "workspace_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "Workspace Id" }
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ScopeCreate",
"description": "Scope creation parameters"
}
}
}
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Scope" }
}
}
},
"201": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Scope" }
}
}
},
"409": {
"description": "Conflict",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
}
}
}
},
"/v3/workspaces/{workspace_id}/scopes/list": {
"post": {
"tags": ["scopes"],
"summary": "Get Scopes",
"description": "Get all Scopes for a Workspace. Results are paginated.",
"operationId": "get_scopes_v3_workspaces__workspace_id__scopes_list_post",
"security": [{ "HTTPBearer": [] }],
"parameters": [
{
"name": "workspace_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "Workspace Id" }
},
{
"name": "reverse",
"in": "query",
"required": false,
"schema": {
"type": "boolean",
"description": "Whether to reverse the order of results",
"default": false,
"title": "Reverse"
},
"description": "Whether to reverse the order of results"
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Page_Scope_" }
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
}
}
}
},
"/v3/workspaces/{workspace_id}/scopes/{scope_id}": {
"get": {
"tags": ["scopes"],
"summary": "Get Scope",
"description": "Get a single Scope by ID.",
"operationId": "get_scope_v3_workspaces__workspace_id__scopes__scope_id__get",
"security": [{ "HTTPBearer": [] }],
"parameters": [
{
"name": "workspace_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "Workspace Id" }
},
{
"name": "scope_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "Scope Id" }
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Scope" }
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
}
}
}
},
"/v3/workspaces/{workspace_id}/scopes/{scope_id}/sessions": {
"post": {
"tags": ["scopes"],
"summary": "Add Sessions To Scope",
"description": "Add Sessions to a Scope.\n\nAll named sessions must already exist (404 otherwise). Adding a session that\nis already a member is a no-op. List the resulting membership with\n`POST /scopes/{scope_id}/sessions/list`.\n\nNote: any added session that already has messages triggers an asynchronous\nbackfill-by-copy of its existing documents into the scope; track progress\nvia ``GET /scopes/{scope_id}/status``.",
"operationId": "add_sessions_to_scope_v3_workspaces__workspace_id__scopes__scope_id__sessions_post",
"security": [{ "HTTPBearer": [] }],
"parameters": [
{
"name": "workspace_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "Workspace Id" }
},
{
"name": "scope_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "Scope Id" }
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ScopeSessionsAdd",
"description": "IDs of the sessions to add to the scope"
}
}
}
},
"responses": {
"204": { "description": "Successful Response" },
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
}
}
}
},
"/v3/workspaces/{workspace_id}/scopes/{scope_id}/sessions/{session_id}": {
"delete": {
"tags": ["scopes"],
"summary": "Remove Session From Scope",
"description": "Remove a Session from a Scope.\n\nNote: documents copied/derived while the session was a member are\nreconciled asynchronously \u2014 the session's explicit copies are soft-deleted\nfrom the scope, dependent derived documents follow (fail-closed), and the\nscope's card is rebuilt from the remaining evidence.",
"operationId": "remove_session_from_scope_v3_workspaces__workspace_id__scopes__scope_id__sessions__session_id__delete",
"security": [{ "HTTPBearer": [] }],
"parameters": [
{
"name": "workspace_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "Workspace Id" }
},
{
"name": "scope_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "Scope Id" }
},
{
"name": "session_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "Session Id" }
}
],
"responses": {
"204": { "description": "Successful Response" },
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
}
}
}
},
"/v3/workspaces/{workspace_id}/scopes/{scope_id}/sessions/list": {
"post": {
"tags": ["scopes"],
"summary": "Get Scope Sessions",
"description": "Get the Sessions that are members of a Scope, paginated.\n\nOrdered by how long each session has been a member: longest-standing member\nfirst, or most recently added first when `reverse` is true.",
"operationId": "get_scope_sessions_v3_workspaces__workspace_id__scopes__scope_id__sessions_list_post",
"security": [{ "HTTPBearer": [] }],
"parameters": [
{
"name": "workspace_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "Workspace Id" }
},
{
"name": "scope_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "Scope Id" }
},
{
"name": "reverse",
"in": "query",
"required": false,
"schema": {
"type": "boolean",
"description": "Whether to reverse the order of results",
"default": false,
"title": "Reverse"
},
"description": "Whether to reverse the order of results"
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Page_Session_" }
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
}
}
}
},
"/v3/workspaces/{workspace_id}/scopes/{scope_id}/status": {
"get": {
"tags": ["scopes"],
"summary": "Get Scope Status",
"description": "Get the backfill/reconciliation job status for a Scope.\n\nReturns a per-session map of the backfill job state (pending / completed /\nfailed) with the number of documents copied once complete. Empty when no\nbackfill has ever been enqueued for the scope.",
"operationId": "get_scope_status_v3_workspaces__workspace_id__scopes__scope_id__status_get",
"security": [{ "HTTPBearer": [] }],
"parameters": [
{
"name": "workspace_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "Workspace Id" }
},
{
"name": "scope_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "Scope Id" }
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/ScopeStatus" }
}
}
},
"404": {
"description": "Not Found",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
}
}
}
},
"/v3/workspaces/{workspace_id}/conclusions": {
"post": {
"tags": ["conclusions"],
@ -2917,6 +3254,20 @@
"title": "Filters",
"description": "Optional filters to scope recall. This endpoint supports only the 'session_id' key: a session id, a list of session ids, or {\"in\": [...]}. Recall (conclusions and messages) is restricted to the allowlist; unsupported keys are rejected. When session_id is also set, it must be included in the allowlist."
},
"scope": {
"anyOf": [
{ "type": "string" },
{
"items": { "type": "string" },
"type": "array",
"maxItems": 100,
"minItems": 1
},
{ "type": "null" }
],
"title": "Scope",
"description": "Optional (unprefixed) scope name(s) to confine recall. A single scope answers from the scope's own representation of the target peer: conclusion recall is confined to what the scope observed and message recall to the scope's member sessions. A list of scopes restricts recall to the union of the scopes' member sessions (explicit allowlist, fail-closed: an empty union recalls nothing). Mutually exclusive with `filters` and `session_id`. Requires a workspace- or admin-level key."
},
"target": {
"anyOf": [{ "type": "string" }, { "type": "null" }],
"title": "Target",
@ -3174,6 +3525,22 @@
"required": ["items", "total", "page", "size", "pages"],
"title": "Page[Peer]"
},
"Page_Scope_": {
"properties": {
"items": {
"items": { "$ref": "#/components/schemas/Scope" },
"type": "array",
"title": "Items"
},
"total": { "type": "integer", "minimum": 0.0, "title": "Total" },
"page": { "type": "integer", "minimum": 1.0, "title": "Page" },
"size": { "type": "integer", "minimum": 1.0, "title": "Size" },
"pages": { "type": "integer", "minimum": 0.0, "title": "Pages" }
},
"type": "object",
"required": ["items", "total", "page", "size", "pages"],
"title": "Page[Scope]"
},
"Page_Session_": {
"properties": {
"items": {
@ -3356,6 +3723,14 @@
{ "type": "null" }
],
"title": "Filters"
},
"kind": {
"anyOf": [
{ "type": "string", "enum": ["scope", "all"] },
{ "type": "null" }
],
"title": "Kind",
"description": "Which kinds of peers to list. Omitted (default): regular peers only (scope peers are excluded). 'scope': scope peers only. 'all': every peer."
}
},
"type": "object",
@ -3376,6 +3751,20 @@
"title": "Filters",
"description": "Optional filters to scope the representation. This endpoint supports only the 'session_id' key: a session id, a list of session ids, or {\"in\": [...]}. When session_id is also set, it must be included in the allowlist."
},
"scope": {
"anyOf": [
{ "type": "string" },
{
"items": { "type": "string" },
"type": "array",
"maxItems": 100,
"minItems": 1
},
{ "type": "null" }
],
"title": "Scope",
"description": "Optional (unprefixed) scope name(s) to confine the representation. A single scope reads the scope's own representation of the target peer, formed only from the scope's member sessions. A list of scopes restricts the representation to conclusions from the union of the scopes' member sessions (explicit allowlist, fail-closed: an empty union yields an empty representation). Mutually exclusive with `filters` and `session_id`. Requires a workspace- or admin-level key."
},
"target": {
"anyOf": [{ "type": "string" }, { "type": "null" }],
"title": "Target",
@ -3542,6 +3931,72 @@
"required": ["observer", "dream_type"],
"title": "ScheduleDreamRequest"
},
"Scope": {
"properties": {
"id": { "type": "string", "title": "Id" },
"metadata": {
"additionalProperties": true,
"type": "object",
"title": "Metadata"
},
"created_at": {
"type": "string",
"format": "date-time",
"title": "Created At"
}
},
"type": "object",
"required": ["id", "created_at"],
"title": "Scope",
"description": "Scope response \u2014 external view of the peer backing a scope.\n\nThe ``id`` is the unprefixed scope name; the reserved peer-name prefix is\nan internal implementation detail and never surfaces here."
},
"ScopeCreate": {
"properties": {
"id": { "type": "string", "minLength": 1, "title": "Id" },
"metadata": {
"anyOf": [
{ "additionalProperties": true, "type": "object" },
{ "type": "null" }
],
"title": "Metadata"
}
},
"type": "object",
"required": ["id"],
"title": "ScopeCreate",
"description": "Schema for creating (or getting) a scope by its unprefixed name."
},
"ScopeSessionsAdd": {
"properties": {
"session_ids": {
"items": { "type": "string" },
"type": "array",
"maxItems": 100,
"minItems": 1,
"title": "Session Ids",
"description": "IDs of existing sessions to add to the scope"
}
},
"type": "object",
"required": ["session_ids"],
"title": "ScopeSessionsAdd",
"description": "Schema for adding sessions to a scope."
},
"ScopeStatus": {
"properties": {
"backfill_status": {
"additionalProperties": {
"additionalProperties": true,
"type": "object"
},
"type": "object",
"title": "Backfill Status"
}
},
"type": "object",
"title": "ScopeStatus",
"description": "Per-session backfill/reconciliation job status for a scope.\n\n``backfill_status`` maps each session that has had a backfill enqueued to\nits current job state: ``{state, updated_at[, docs_copied]}`` where\n``state`` is ``pending``/``completed``/``failed`` and ``docs_copied`` is\npresent once a backfill completes."
},
"Session": {
"properties": {
"id": { "type": "string", "title": "Id" },
@ -3669,6 +4124,18 @@
{ "$ref": "#/components/schemas/SessionConfiguration" },
{ "type": "null" }
]
},
"scopes": {
"anyOf": [
{
"items": { "type": "string" },
"type": "array",
"maxItems": 100
},
{ "type": "null" }
],
"title": "Scopes",
"description": "Optional list of (unprefixed) scope names to add this session to. Each scope is created if it does not exist yet. If the session already has messages, its existing documents are backfilled into the scope asynchronously."
}
},
"type": "object",