Commit Graph

31 Commits

Author SHA1 Message Date
Vineeth Voruganti de428780a6 fix(scopes): close auth and observed-position gaps, paginate membership
Review response for #884.

Security:
- gate `SessionCreate.scopes` behind a workspace-level key; the session-create
  route is self-authorizing, so a peer- or session-scoped token could mint scope
  peers and join sessions to scopes it had no access to via `POST /scopes`
- refuse a reserved-but-nonexistent name in two observed positions that used the
  permissive guard: chat `target` and session-context `peer_target`. Both let a
  caller act on `scope.X` before it existed, then create the scope

Facade:
- exclude scope peers from `GET /sessions/{id}/peers` and refuse the membership
  -config read for a real scope, matching its write side
- replace `GET /scopes/{id}/sessions` with `POST /scopes/{id}/sessions/list`
  returning `Page[Session]`; the add route now returns 204. Membership was
  unbounded on both, while every other list surface paginates
- rename `crud.get_scope` to `get_scope_or_raise`

Tests:
- add a missing-name axis to the route-policy table (`Case.refuse_missing`), which
  is what surfaced the two guard gaps above
- delete 14 hand-written tests the table now enumerates; 52 -> 39 functions in
  test_scopes.py with more cases covered
- tighten the squatter assertion from `!= 422` to `< 400`, which was passing on 5xx
- assert the FastAPI-internals traversal still derives positions, so a framework
  upgrade can't silently empty the suite

Docs:
- drop internal ticket and RFC references from the published OpenAPI descriptions
  and surrounding comments; state the behavior instead
- move implementation reasoning out of the `PUT /peers/{id}` docstring, which
  FastAPI publishes, into a comment
2026-08-12 11:36:54 -04:00
Vineeth Voruganti 331f6ff416 fix(scopes): exclude scope memberships from the session observer limit
Scope memberships carry observe_others=true, so every scope counted against
SESSION_OBSERVERS_LIMIT (default 10) — capping scopes-per-session at the limit
minus the session's real observers, and reporting the failure as
`400 Cannot create session <name> with 11 observers. ... Observers are peers
with 'observe_others' set to true.` on a membership call. Wrong on three counts:
the ceiling is undocumented and contradicts RFC §5.1 ("sessions belong to any
number of scopes"), the message describes session creation, and it leaks the
word "observer" through a facade whose entire job is hiding observers (RFC
goal 5). The limit exists to bound per-observer deriver fan-out for real peers;
a scope costs document rows, not LLM calls (RFC §5.2), so it does not belong in
that budget.

Excluded from both halves of the check in `_get_or_add_peers_to_session`: the
incoming names via a flag-based lookup, existing memberships via a correlated
NOT EXISTS on `scope_peer_clause()` — the same pattern the replacement and
removal paths already use, so the exclusion holds regardless of concurrent
scope creation. The early `count_observers_in_config(session.peer_names)` check
in `get_or_create_session` is left alone: `peer_names` cannot contain a scope,
and `scopes` is a separate field.

`reject_scope_peers` is split into a `scope_peer_names()` query helper plus a
two-line raiser so the observer count reuses the authoritative name-AND-flag
predicate instead of growing a third copy of it. Still costs nothing on the
common path — no reserved-prefix name in the input means no query at all.

Also caps `SessionCreate.scopes` at 100, matching `ScopeSessionsAdd.session_ids`.
This belongs in the same commit: the observer limit was the only thing bounding
that list, so removing it turns an unbounded `scopes` array into a peer row and
a membership row per element, committed — the single-request path to the
cardinality anti-pattern RFC §8 warns about. Partly answers OQ6: no per-session
cap, 100 per request.

Tests: a session joins SESSION_OBSERVERS_LIMIT + 2 scopes through both the
facade and session creation; real observers over the limit still 400, so the
carve-out cannot quietly disable the limit; 101 scopes is a 422.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 22:52:31 -04:00
Vineeth Voruganti 5e989794b6 fix(scopes): refuse future scopes in observed positions, preserve scope membership
Addresses a fifth review pass against 14136e5b. All six findings reproduced
locally before fixing.

1. High — generic peer replacement removed scope memberships.
   `set_peers_for_session` soft-deleted every active SessionPeer row, and the
   request-level guard only inspected names *present* in the replacement map. A
   caller detached a scope by simply omitting it, never naming it — so no
   request-level guard could ever see it. Reproduced: scope sessions went
   ['<id>'] -> [] on a 200. The exclusion now lives in the UPDATE itself
   (correlated NOT EXISTS against scope_peer_clause), so replacement means
   "replace ordinary peers" regardless of request contents or concurrent creation.

2. High — peer cards could be pre-seeded for future scopes.
   `set_peer_card` resolves only the observer and writes a JSONB key derived from
   an unchecked observed name, and the route guard rejected only *existing*
   flagged scopes. Reproduced: PUT card with target=scope.<missing> returned 200,
   creating that scope then returned 201, and the card described the real scope.

3. High — dreams could be queued for future scopes.
   The route checked `observed` in a read-only session that closed before
   `enqueue_dream`, and a missing reserved name passes any is-it-a-scope check.
   Reproduced: 204 with observed=scope.<missing>.

   2 and 3 share a root cause, so they share a fix: a new `reject_scope_observed`
   that is stricter than `reject_scope_peers` in exactly one case — a *missing*
   reserved name is refused, because nothing on these paths creates the peer, so
   nothing else would ever catch it. Existing unflagged squatters still pass.
   Both guards moved to the mutation point: card validation into
   `crud.set_peer_card` (same transaction as the JSONB write, so Dreamer and
   agent-tool callers are covered), dream validation into `enqueue_dream` (same
   transaction as the queue insert). The redundant route-level checks are dropped
   rather than left as weaker duplicates.

4. Medium — prefixed NUL names still reached PostgreSQL.
   `reject_scope_peers` filtered for the reserved prefix and sent matches to a
   text comparison, so "scope.future\0name" raised psycopg.DataError — a 500.
   Both guards now share `_reserved_name_candidates`, which materializes the input
   once and rejects impossible values before any SQL. Materializing matters
   independently: the message-author path passes a generator, and validation
   iterates separately from the prefix filter, so a generator would be
   half-consumed. `_reject_impossible_peer_names` now takes a Collection so the
   type checker enforces that.

5. Medium — representation kept a check-to-use race.
   The previous commit claimed both representation roles were rechecked after
   resolution; that was wrong — only the dialectic preflight got that check, and
   the representation route never goes through it. It now opens one short
   read-only session *after* the embedding call, checks both positions, and passes
   that same session to `get_working_representation`, so no connection is held
   across external work and a scope committed later cannot have conclusions in the
   collection being read.

6. Low — policy coverage was not exhaustive. `sender_id` reaches CRUD as
   `observed` but was missing from the detected parameter set. ALLOW cases could
   also not carry builders, so the suite never proved the other half of the
   contract — that legitimate scope *observers* keep working, which a guard
   rejecting scopes everywhere would satisfy. Both fixed; observer positions on
   conclusions, dreams, cards, session context and queue status are now asserted
   behaviorally.

Deliberately not implemented: the scope-creation backstop scanning for
pre-existing card keys and queue items naming a future backing peer. Reasoning is
recorded in `get_or_create_scopes` — no new such state can be created now, any
pre-existing row is coincidental since `scope.` was never a meaningful namespace,
the consequence is inert, and detecting card keys means a full table scan per
scope creation.

Mutation-tested each new guard: removing the replacement exclusion fails both
membership-preservation tests; weakening either observed guard to existing-only
fails the pre-seeding tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 16:07:52 -04:00
Vineeth Voruganti 14136e5bc4 fix(scopes): refuse a scope in every observed position; enumerate per position
Addresses a fourth review pass against 62681e4d. The headline finding is that the
previous commit's enumeration test had the wrong *model*, not a missing entry.

1. Manual conclusions could create knowledge about a scope. `POST /conclusions`
   validated only that observer_id and observed_id exist, so a scope as
   `observed_id` persisted a conclusion about a peer carrying observe_me=false and
   created an (observer, scope) collection for it. Confirmed: 201, and it read
   back. `POST /schedule_dream` had the same hole via `observed`.

   The fix is positional, because the invariant is:

       A scope may be an OBSERVER. A scope may never be OBSERVED.

   A scope as `observer_id` is how scoped conclusions are stored and must keep
   working (verified still 201); as `observed_id` it is now refused. Same split
   applied to schedule_dream `observed`, the peer-card `target` (which also covers
   a scope's self-card, since target-omitted collapses observed to peer_id), and
   session-context `peer_target`.

2. Chat target and both representation roles kept check-to-use races. Only the
   chat path-level observer was re-checked on its resolved row; the target was
   checked by name and then resolved without inspecting scope identity. Both are
   now checked at the dialectic preflight, where observer and observed are already
   resolved — an absent name has already failed by then, and an existing squatter
   cannot retroactively become a scope.

3. Generic membership removal was still racy. The adjacent SELECT narrowed the
   window but could not close it under READ COMMITTED. The UPDATE now carries its
   own correlated NOT EXISTS against scope_peer_clause(), so Postgres evaluates
   the exclusion as part of the statement and a scope committed after the advisory
   check still cannot be detached.

4. New-name validation ran after the name reached Postgres. A NUL byte passed the
   request schemas and PeerSpec, then raised psycopg.DataError inside the lookup —
   a 500. Values that cannot correspond to a stored row by construction (NUL
   bytes, over-length names) are now refused before the query.
   (Over-length names already returned 422; only the wasted query was real there.)

The enumeration test is rekeyed from (method, path) to (method, path, position).
A binary per-route verdict cannot express finding 1 at all: `POST /conclusions` is
one route with two positions and opposite verdicts. Detection widens to observer /
observed / target / peer_target / peer_perspective, which surfaced four routes the
previous version never saw — conclusions, schedule_dream, queue/status, and
session context.

Also registers `src.routers.workspaces.tracked_db` in the conftest patch list; the
new guard there would otherwise have run against the real configured database
instead of the per-test one.

Mutation-tested: disabling the conclusions observed-guard fails the positional
test naming that position; adding an unclassified `observed_id` param fails
enumeration.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 14:57:09 -04:00
Vineeth Voruganti 0d7270ac08 fix(scopes): guard scope membership config, move checks to the mutation point
Addresses a second review pass against 10655792.

1. Scope membership configuration was directly user-mutable.
   `PUT /sessions/{id}/peers/{peer_id}/config` had no scope guard at all, and
   `crud.set_peer_config` resolved the peer only to discard the row. Confirmed:
   posting `{"observe_others": false, "observe_me": true}` for a scope returned
   204 and persisted, which silently stops all fan-out into the scope and makes
   Honcho form a representation *of* a scope — neither of which is reachable
   through the facade. Deterministic, no race required. Now checked on the row
   `get_peer` already returns, so it costs nothing and cannot race.

2. Empty and over-long names were still 500s. Removing the charset pattern from
   `PeerSpec` fixed one trap but left its length bounds, and request-bound peer
   names carry no length limits of their own — so `peer_id: ""` or a 513-char
   name reached `PeerSpec(...)` and raised a raw pydantic ValidationError that
   the catch-all turned into a 500. `PeerSpec` now carries no constraints at all
   (matching its documented purpose) and every rule for a new name lives in
   `_validate_new_peer_names` on the insert path.

3. Resolved-row protection generalized. The previous pass applied it only to
   membership upserts, leaving check-then-use windows elsewhere: peer update
   could have a concurrently-created scope's configuration replaced wholesale
   (create-path validation does not fire for a peer that now exists), the chat
   observer get-or-create could resolve a fresh scope as its observer, and the
   generic session-peer removal could silently detach a scope from its sessions.
   Each now inspects the resolved peer immediately before acting; the redundant
   name-level guard on the update route is dropped in favor of the race-free one.

`remove_peers_from_session` grows an internal `_allow_scope_peers` flag because
the scopes facade ends membership through that same path and must not be blocked
by its own guard.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 13:52:20 -04:00
Vineeth Voruganti 10655792bd fix(scopes): validate peer names on create, close namespace squatting and upsert race
Addresses three review findings against 48047a6a.

1. `PeerSpec` let API callers create invalid and reserved-prefix peers.
   Widening `get_or_create_peers` to accept a pattern-free schema fixed the
   lookup 500s but also removed validation from the *insert* path, and
   request-controlled names reach it via message authors, session peer maps, and
   the chat observer path — none of which carry a charset pattern of their own.
   Confirmed: `POST /sessions/{id}/messages` with `peer_id: "scope.x"` returned
   201 and minted an unflagged squatter, after which `POST /scopes {id: x}` was
   permanently 409-blocked — namespace denial of service by any caller able to
   post a message. `peer_id: "not a valid name!@#"` was likewise created.

   Fixed by validating only names about to be INSERTed
   (`_validate_new_peer_names`), so already-existing names — legacy dotted
   names, scope peers — still resolve without a spurious 422. That keeps the
   Dreamer fix intact, since it reads through `get_peer`.

2. Existing reserved-prefix squatters could not be updated. The name-based guard
   on `PUT /peers/{peer_id}` refused every `scope.` name, contradicting the
   invariant that an unflagged squatter stays a normal peer. Now flag-based, so
   behavior is three-way: a real scope is refused, an existing unflagged peer
   updates, and a missing reserved-prefix name is refused by (1) rather than
   minted.

3. Scope checks raced with get-or-create and the membership upsert. The
   route-level guards run before peers are resolved, so a scope created
   concurrently in that window would be attached by the generic path with a
   default `SessionPeerConfig()`, clobbering its observer membership config.
   Adds `_reject_resolved_scope_peers`, which runs on the resolved rows in the
   same transaction as the upsert — no window, no extra query. The early checks
   stay for better error messages.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 13:03:27 -04:00
Vineeth Voruganti 48047a6a99 fix(scopes): make scope identity unforgeable, unblock non-pattern peer names
Three coupled changes to the scopes facade.

1. `PeerCreate` no longer gates internal lookups. It exists to validate a new,
   user-supplied peer id at the API boundary, but crud used it as a DTO for
   names that already exist, so any name outside RESOURCE_NAME_PATTERN raised a
   raw pydantic ValidationError — which is not a HonchoException, so it fell
   through to the catch-all handler as an HTTP 500. Adds `PeerSpec` (same
   fields, no charset pattern) as `PeerCreate`'s base, widens
   `get_or_create_peers` to accept it, and changes `get_peer` to take a plain
   str. All 13 construction sites converted; the create route keeps full
   validation.

   This unbreaks the Dreamer: DreamScheduler passes `collection.observer`
   straight into the specialist preflight, and scope peers have
   `observe_others=true`, so every `(scope.x, peer)` dream died there — the
   feature scopes exist to enable. It also fixes a pre-existing bug unrelated
   to scopes: a peer named `alice.smith` (legal before d429de0e5338, which
   validated names by length alone) 500s on message create, session peer add,
   and peer update.

2. The `kind` flag moves from `configuration` to `internal_metadata`.
   `configuration` is user-writable — `PeerCreate`/`PeerUpdate` accept a
   free-form dict and `update_peer` replaces it wholesale — so a legitimate
   `{"observe_me": true}` update silently dropped the flag, and a forged
   `{"kind": "scope"}` injected an ordinary peer into `POST /scopes/list`.
   `internal_metadata` appears in no API schema. `observe_me: false` stays in
   `configuration`, where it belongs.

3. Scope identity requires prefix AND flag, via `is_scope_peer()` and
   `scope_peer_clause()`. Neither half is forgeable: the prefix sits outside
   RESOURCE_NAME_PATTERN, `internal_metadata` is unreachable. Usage-site guards
   become flag-based so a legacy peer merely occupying the namespace keeps
   working rather than 422-ing on its own traffic; peer create and update stay
   name-based, since those must stop new names entering the namespace.
   `update_peer` now returns 422 instead of 500.

Also swaps the reserved prefix from `scope__` to `scope.`: `_` is inside
RESOURCE_NAME_PATTERN, so any tenant could already own a `scope__x` peer.

No DB migration — `internal_metadata` already exists on `peers`, and no scope
peers exist in any deployment yet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 12:07:17 -04:00
Vineeth Voruganti 79342b57e5 Merge branch 'main' into vineeth/dev-1997 2026-07-24 11:30:32 -04:00
Rajat Ahuja 5ad22840d8
feat: add support for redis cluster (#905) 2026-07-13 15:51:47 -04:00
Vineeth Voruganti 5a781f95fc feat: add scopes CRUD routes and session-create scopes wiring
New /v3/workspaces/{workspace_id}/scopes facade (workspace-level auth;
peer- and session-scoped keys are rejected):

  POST   ""                                   create-or-get (201/200)
  POST   /list                                 paginated scope list
  GET    /{scope_id}                           single scope
  POST   /{scope_id}/sessions                  add memberships
  DELETE /{scope_id}/sessions/{session_id}     remove membership
  GET    /{scope_id}/sessions                  list member session ids

- crud/scope.py: get_or_create_scopes stamps the backing peer with
  {"kind": "scope", "observe_me": false} and refuses to adopt a
  legacy peer occupying the reserved name without the flag (409)
- memberships are session_peers rows with observe_others=true /
  observe_me=false — identical to a hand-built observer peer
- SessionCreate.scopes: create-or-get each scope peer and add the
  membership at session creation (the no-backfill common path)
- crud/session.py: public upsert_session_peers wrapper so the facade
  bypasses the route-level guardrails without reaching into privates

Backfill of pre-existing documents and reconciliation on removal land in
DEV-1999; membership only affects messages ingested after the change.

Part of DEV-1997 (Scopes RFC DEV-1970).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 15:41:17 -04:00
Rajat Ahuja 326a757cdb
Fix scoped JWTs (#679)
* Peer- and session-scoped JWTs were effectively workspace-scoped: auth() walked the route's declared scope and fell through to a workspace match, so a {w: ws-a, p: alice} token could act on any peer in ws-a.

* feat: peer keys can read sessions they belong to; require workspace on scoped keys

* fix: authorize JWTs by narrowest scope and gate member reads

Follow-up hardening on the narrowest-claim auth fix:

- Scope get_peer_config member-read to the caller's own peer; a session
  member could previously read a co-member's per-session config.
- Enforce session membership on POST /peers/{id}/chat: the session_id
  arrives in the body (invisible to require_auth), so a peer key could
  read any session's injected message history. Check is_peer_in_session
  in the handler before the dialectic runs.
- Consolidate the workspace-match check in auth() to a single hoisted
  guard so no branch can silently re-open cross-workspace access.
- Normalize empty-string scope claims to None in verify_jwt so a blank
  workspace can't satisfy the peer/session token-shape invariant.
- Extract scope_requires_workspace(), shared by verify_jwt and the keys
  API so the creation-time guard and verification invariant can't drift.
  route requires auth) and CLAUDE.md auth-scoping guidance.
- docs: describe narrow-scope key semantics in the platform reference.

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2026-06-22 17:30:00 -04:00
adavyas 0cf63c10da
feat(api): restore reverse pagination (#685)
* feat(api): restore v3 reverse pagination

* docs: add reverse pagination docstrings

* docs: document session reverse parameter

* fix: add fallback column for ties

* refactor: tighten reverse query typing

* chore: pre-commit styling

* chore(tests): Add additional validation tests and update changelogs

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2026-05-21 13:40:47 -04:00
Vineeth Voruganti 5b6bd59030
Tighten Transaction Scopes (#525)
* fix: further remove extraneous transactions

* fix: (search) use 2 phase function to reduce un-needed transaction

* fix: refactor agent search to perform external operations before making a transaction

* fix: reduce scope of queue manager transaction

* fix: (bench) add concurrency to test bench

* fix: address review findings for search dedup, webhook idempotency, and bench throttling

* Fix Leakage in non-session-scoped chat call (#526)

* fix: (search) reduce scope for peer based searches

* fix: tests

* fix: (test) address coderabbit comment

* fix: drop db param from deliver_webhook

---------

Co-authored-by: Rajat Ahuja <rahuja445@gmail.com>
2026-04-08 11:14:50 -04:00
Vineeth Voruganti 5a42f9b3cd
Use savepoints to prevent race condition in get_or_create chain (#399)
* fix: Use savepoints to prevent race condition in get_or_create chain

Replace commit()/rollback() with begin_nested() savepoints in
get_or_create_workspace, get_or_create_peers, and get_or_create_session
so that an IntegrityError rollback in a nested call doesn't undo flushed
work from the caller. Moves transaction commit responsibility to the
outermost caller and defers cache operations to post-commit callbacks
on GetOrCreateResult.

Closes DEV-1321

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Add missing commit and post_commit in chat endpoint

The chat() endpoint in routers/peers.py called get_or_create_peers()
but discarded the result without committing or invoking post_commit().
This meant new peers created lazily via SDK chat calls were never
persisted, and cache invalidation was skipped.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 23:28:04 -05:00
Vineeth Voruganti 780bfe1c30
Refactor cache to store data rather than orm objects (#395)
* fix: refactor cache to store data rather than orm objects

* fix: Add Cache Version Keys
2026-02-23 16:03:29 -05:00
Vineeth Voruganti 78df86dc66
fix: Remove noisy sentry error on llm failure and upgrade deps (#396) 2026-02-23 15:53:29 -05:00
Rajat Ahuja 33ef0f8eab
perf: add redis caching and reduce embedding API calls (#372)
* feat: add redis to get collection + get session peer config

* fix: reduce embedding calls

* fix: delete session caches when soft-deleting session

* fix: batch embeddings in create observatiosn tool

* fix: batch embeddings call in extract_preferences

* fix: double embed in fallback for _handle_search_memory

* fix: claude comments

* fix: return ObservationResult

* fix: make cache delete/set retryable. remove session peer config cache

* chore: claude nitpicks

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2026-02-13 00:53:46 -05:00
Vineeth Voruganti 6b3ecef601
Telemetry Overhaul (#333)
* chore: Refactor a top-level telemetry folder

* fix: Add OTEL push based metrics

* chore: cleanup otel to match prometheus implementation

* fix: Remove prometheus

* feat: Scaffold CloudEvent Emitter

* chore: remove dead code

* feat: PoC Cloud Events

* chore: cleanup test

* fix: Event naming conventions and OTEL Settings

* fix: Revamped Dream Event Structure

* fix: Instrument Event Code

* fix: Add Tests for CloudEvent Telemetry

* fix: Code Rabbit Nits

* fix: Dedupe Event IDs
2026-01-21 16:49:14 -05:00
Rajat Ahuja 833a89e70a
Turbopuffer and LanceDB Integration (#287)
* feat: init turbopuffer and lanceDB

* fix: remove destructive embedding migration

* fix: bug fixes

* fix: LanceDB

* fix: turbopuffer

* fix: search and add create_observations

* fix: use Async clients

* fix: search; protect agaainst failed vector create/delete

* fix: coderabbit comments

* fix: set up compose vector store and reconciliation loop

* feat: sync docs without embeddings

* fix: reduce batch size; comments; types; add indexes for reconciliation

* fix: add message embedding resilience

* fix: clean-up and migration test

* fix: cleanup 2

* fix: centralize retry logic; bump reconciliation batch; use tracked db; fix soft-delete race condition

* fix: skip double query when pgvector is primary

* fix: down migration

* fix: remove hard-delete from critical path and make PgVectorStore deletions a no-op

* fix: use soft-delete pattern for duplicate detection

* fix: steps toward deprecating MessageEmbedding table

* fix: remove composite and pgvector store -> make more specific

* fix: migration order

* fix: shorten reconciliation cycle + fix 'IN' equality check

* fix: coderabbit comments

* fix: add test for migration 7c0d9a4e3b1f

* feat: refactor to use ReconcilerScheduler

* fix: CR / opus comments

* fix: work unit key and reserve system workspace

* fix: make workspace_name nullable

* fix: clean up sync vectors

* fix: delete syntax

* fix: hash namespace

* External Vector Store Nits (#332)

* fix: Migration naming and long held connection

* chore: Comment for potential debt

* chore: update typescript core package

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2026-01-16 17:04:01 -05:00
Benjamin McCormick 5b7ae0d82c feat: API renaming and cleanup
- Rename API routes for consistency
- Add backwards-compatible conclusion and queue endpoints
- SDK cleanup and representation improvements
- Add reasoning_level param validation
- Fix thinking budget validation for Anthropic

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 16:50:40 -05:00
Vineeth Voruganti 72eb0827ec
Add session cloning to Ergonomic SDKs (#285)
* feat: Add session cloning to Ergonomic SDKs

* chore: docs nit
2025-12-04 14:51:39 -05:00
doria e3d345b961
API/SDK updates: configurability, more parameters. Unified test harness (#283)
* feat: add better params to working representation fetch in SDKs, return messages when added

* fix: working representation routes now accepting all parameters properly, with tests

* feat: add metadata/config fields to SDK objects where viable

* fix: tests

* feat: refactor SDKs to use representation config; [TEMP STAINLESS BUILD] update API

* feat: add representation object to sdks

* fix: use stainless sdk on branch

* fix: update TypeScript SDK tsconfig to use node16 module resolution

* fix: add isolatedModules = true to tsconfig

* fix: lol

* chore: coderabbit review

* feat: make delete session real

* feat: add observations routes with delete endpoints for documents. make session deletion real.

* chore: type cleanup

* fix: tests

* chore: coderabbit review

* fix: namespace by workspace

* feat: add ability to customize messages_per_summary at both workspace and session level

* chore: tests for summary config

* chore: coderabbit cleanup

* feat: make session and workspace config totally customizeable

* feat: add search by peer knowledge (#250)

* feat: search by peer perspective

* fix: enforce workspace in filters, make messages distinct in join

* fix: batch and merge migration steps

* fix: add refresh, add config to workspace, add refresh function, make fields readonly

* fix: search distinct

* fix: merge migrations

* fix: merge migrations

* fix: batch deletions, improve comments, limit consolidate dream to 100 docs at a time, auth on observations routes

* chore: review

* chore: coderabbit

* chore: review

* chore: broken comment

* feat: add set peer card route to API

* feat: create advanced configuration parameters with message>session>workspace hierarchy

* [wip] build unified testing harness

* chore: lint

* fix: cache invalidation, naming things, etc

* feat: longmem tests

* chore: peer config refactor

* feat: consolidate dream working, refactor representation

* fix: Various CR Comment Fixes

* feat: Allow configurable Redis port for harness instances and update cleanup methods to be asynchronous.

* fix: version bump, api/sdk updates

* fix: observation endpoints, deletion queue, sdk observation implementation

* chore: Fix migration order

* fix: Use published stainless sdks

* chore: (docs) update api-reference

* fix: (docs) update based on api and sdk changes

* fix: Code Rabbit Comments

* fix: Code Rabbit Final Nits

* fix: dream scheduler

* fix: SDK model type consistency

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2025-12-03 16:49:30 -05:00
Rajat Ahuja b3e715d1d6
fix conditional cache invalidation (#280)
* fix: cleanup cache logs

* fix: address cache thrashing

* fix: warm cache for new session
2025-12-03 12:22:35 -05:00
Rajat Ahuja 8e3f24d4f8
feat: add caching (redis) (#265)
* feat: init redis cache

* feat: initialize redis client

* fix: add fakeredis client

* fix: update docker compose

* fix: various coderabbit comments

* fix: update docker-compose.yml.example

* fix: CR comments 2

* fix: clean up client

* fix: cache key class; workspace LRU; constants -> utils

* fix: CodeRabbit comments

* feat: add redis to harness

* fix: use cashews

* fix: add locking for cache stampedes

* fix: clean up PR

* f: unify cache key templates and stop caching None

* fix: alter prefixes + prime cache on get_or_create()

* fix: re-add fakeredis

* chore: Code Rabbit nits

* CR comments; make client more resilient

* fix: BPR

* fix: set redis once but clear cache in between every test

* fix: attempt fix contextvar error

* fix: make TestClient rely on redis patch

* fix: potential working fix

* fix: namespace inheritance; address comments

* fix: namespace inheritance; address comments

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2025-11-07 13:04:28 -05:00
Rajat Ahuja 77a965e97f
feat: fix race condition in message sequence batching (#235)
* feat: fix race condition in message sequence batching

* fix: CodeRabbit comments; commit early to release the advisory lock before generating embeddings

* fix: use index + rm unused method

* fix: PR comments

* fix: bug in lock timeout

* fix: patch tracked_db for peers route within conftest.py
2025-10-16 11:46:55 -04:00
doria 68acf38134
Misc: bug fixes, multi-db test harness, DELETE workspace (#230)
* feat: add optional JWT and webhook secrets to honcho instance creation

* chore: ignore spurious warnings

* feat: add response format if using gpt-5 model family

* feat: add response models to all apis except anthropic

* fix: raise NotImplementedError for response models in AsyncAnthropic client

* chore: address review

* [WIP] representation structure + deriver cleanup

* chore: add tests, cleanup

* feat: [WIP: semi-working] representation object

* fix: alignment

* fix: make observations hashable for dedup

* fix: datetime formatting, observation counting

* fix: switch to int for message id, clean up representation

* feat: remove need for metadata working rep

* chore: cleanup

* fix: use tenacity instead of custom fns

* feat: add representation and card to context if desired

* feat: add semantically relevant observations

* fix: pass all params to streaming, nonblocking streaming

* feat: consolidate document saving, make working representation fetching much smarter

* chore: add 100% test coverage of representation util

* feat: basic dream infra

* feat: dream queue item first pass

* chore: fixes & cleanup from coderabbit

* fix: dreams scheduled when new document count reaches a certain threshold

* feat: wip: timed dreams (not working)

* fix: test

* fix: remove useless pyright ignore

* fix: executing dreams

* feat: dreaming

* feat: [WIP] longmemeval bench

* feat: add USE_PEER_CARD setting, fix longmem test driver

* feat: get full working rep for dialectic in one swoop -- fix representation_from_documents to use the proper timestamp!

* fix: timestamps for real, handle assistant qs in longmem

* fix: remove old client, add batching to longmem

* perf: remove duplicate detection, will move to background task

* feat: track perf metrics on evals

* feat: adjust deriver prompt to use peer_id, add question date to question, clean up deriver

* fix: label metrics by task for better perf trace

* chore: code review

* feat: add efficiency score to longmem bench

* chore: tuning and cleaning up eval

* chore: bring in the big prompts

* feat: add support for vllm client

* feat: perf: bundle db calls in deriver and dialectic, increase max conns in docker db

* feat: add merge-sessions flag to longmemeval, add SUMMARY_ENABLED flag

* fix: COLLECT_METRICS default false

* chore: display start/end message ids, don't include in metrics

* fix: break large messages apart for eval

* fix: only get/create collection when needed

* feat: properly attribute documents with message id ranges and add session name column to documents

* fix: revert move of get_or_create_collection (need for fkey)

* fix: always get collection with peer name even if it's none

* chore: coderabbit

* fix: give peer card its own config, expand document schema, refactor get_context to be parallel, various cleanup chores and bugfixes

* chore: refactor: reify observer/observed system across entire codebase, including db migration

* refactor: cleanup code organization, make singletons where desired

* refactor: replace embeddings store with representation manager

* chore: coderabbit cleanup

* feat: multi-db longmem harness

* Merge branch 'main' into ben/multi-db-harness

* [WIP] feat: add delete workspace endpoint, use in bench

* chore: move excess logging to debug

feat: improve metrics block logs to include more data

fix: make longmem db deletion configurable

* fix: [CRITICAL] use async genai client

* chore: update core sdk, fix tests to use aio as well

* fix: rollback prompt changes

* chore: update version

* fix: cleanup, coderabbit, wrap delete op in try/except

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2025-10-09 16:53:41 -04:00
Vineeth Voruganti 0838a281c1
Session Observer Limit (#186)
* fix: change session peers limit to enforce restrictions based on observers

* fix: Add new Exception type

* chore: fix type
2025-08-12 11:46:52 -04:00
doria 23557ced02
fix: resolve get_effective_observe me race condition, default peer config (#176)
* fix: resolve get_effective_observe me race condition, default peer config

* fix: preserve custom config even after leaving

* chore: test cases, enqueue types
2025-08-05 13:44:54 -04:00
Rajat Ahuja 6338337c2d
fix: make get_or_create routes more resilient (#175)
* fix: make get_or_create routes more resilien

* fix: standardize retry
2025-07-31 16:49:57 -04:00
doria bc5474c170
feat: 2.1.1 release (#167) 2025-07-23 23:06:09 -04:00
doria a14899521c
Honcho 2.1.0 "ROTE" deriver (#160)
* feat: update SDKs to core 1.2.0

* feat: 2.1.0 introduce ROTE deriver/dialectic
chore: refactor repo

* fix: get typescript sdk tests working again, bump version numbers

* chore: cleanup

* chore: update unit test provider config

* fix: remove "backup" query gen

* fix: remove old utils from conftest
2025-07-16 18:02:43 -04:00