Commit Graph

5 Commits

Author SHA1 Message Date
Vineeth Voruganti c2d8cf3a72
Scopes SDK Changes (#1030)
* feat: scopes SDK surface and session allowlist on session context

Exposes the Scopes v1 facade in both SDKs, which until now was reachable
only by hand-rolled HTTP, and closes the Phase 1 gap where the session
allowlist never landed on the context route.

SDKs (DEV-2001, folds in DEV-1996)

  New Scope class in both SDKs — addSessions / removeSession / sessions /
  status — plus honcho.scope() and honcho.scopes() entry points, a
  `scopes` option on session creation, and `scope` + `sessions` read
  options on chat, chatStream, representation, and session.context.
  `scope` on workspace search. Python covers sync and .aio equally.

  `sessions` is sugar, not a new wire field: on the recall endpoints it
  goes out as the constrained `filters: {session_id: [...]}` body, never
  as a key of its own. Kept separate from the `filters` parameter on the
  list/search methods on purpose — that one is the full filter DSL,
  whereas the recall endpoints accept a single key and 422 on anything
  else, so one name for two grammars would be a trap.

Server (DEV-2357)

  `GET /sessions/{id}/context` accepts a `sessions` allowlist confining
  the target's representation. Two deliberate choices worth review:

  - Sent as a repeated query parameter rather than the `filters` body the
    issue specced. The route is a GET and `session_id` is the only
    supported key, so a JSON blob in a query string buys nothing.
  - The peer card is omitted under an allowlist. Cards key on
    (workspace, observer, observed) with no session dimension, so they
    cannot be narrowed; returning one would leak exactly what the
    allowlist exists to exclude. Same reasoning as ALLOWLIST_SAFE_LEVELS.
    `scope` needs no carve-out — it swaps the observer to the scope peer,
    so the card read is the scope's own.

  `extract_session_allowlist` now delegates to a shared
  `normalize_session_allowlist`, so the cap, id charset, and must_include
  rule have one implementation across both entry points. Existing error
  messages are unchanged.

Also in here

  - ConclusionScope renamed to ConclusionsView in both SDKs. "Scope" now
    means a named set of sessions, which that class is not — it is a view
    over one observer/observed pair. ConclusionScope kept as a deprecated
    alias; the package-level import path only.
  - The TS HTTP client comma-joined array query params, so any list-valued
    parameter arrived as one malformed entry. Fixed at buildURL rather
    than the call site.

Not in this PR: the "How Scopes Work" docs guide and the Groudon
dashboard tab (both DEV-2001), and CHANGELOG entries for Phases 2a-2c,
which are still merged-but-unrecorded.

Verified: ruff, basedpyright, tsc --noEmit, biome all clean. New unit
tests cover the SDK option translation and the Scope client, but the
context route's own behavior — the 422s, the 401 membership gate, the
dropped peer card — has no test yet; the analogous chat/representation
cases in tests/test_session_allowlist.py are the place for it.

Refs DEV-2001, DEV-1996, DEV-2357

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
  Exposes the Scopes v1 facade in both SDKs, which until now was reachable
  only by hand-rolled HTTP, and closes the Phase 1 gap where the session
  allowlist never landed on the context route.

  SDKs (DEV-2001, folds in DEV-1996)

    New Scope class in both SDKs — addSessions / removeSession / sessions /
    status — plus honcho.scope() and honcho.scopes() entry points, a
    `scopes` option on session creation, and `scope` + `sessions` read
    options on chat, chatStream, representation, and session.context.
    `scope` on workspace search. Python covers sync and .aio equally.

    `sessions` is sugar, not a new wire field: on the recall endpoints it
    goes out as the constrained `filters: {session_id: [...]}` body, never
    as a key of its own. Kept separate from the `filters` parameter on the
    list/search methods on purpose — that one is the full filter DSL,
    whereas the recall endpoints accept a single key and 422 on anything
    else, so one name for two grammars would be a trap.

  Server (DEV-2357)

    `GET /sessions/{id}/context` accepts a `sessions` allowlist confining
    the target's representation. Two deliberate choices worth review:

    - Sent as a repeated query parameter rather than the `filters` body the
      issue specced. The route is a GET and `session_id` is the only
      supported key, so a JSON blob in a query string buys nothing.
    - The peer card is omitted under an allowlist. Cards key on
      (workspace, observer, observed) with no session dimension, so they
      cannot be narrowed; returning one would leak exactly what the
      allowlist exists to exclude. Same reasoning as ALLOWLIST_SAFE_LEVELS.
      `scope` needs no carve-out — it swaps the observer to the scope peer,
      so the card read is the scope's own.

    `extract_session_allowlist` now delegates to a shared
    `normalize_session_allowlist`, so the cap, id charset, and must_include
    rule have one implementation across both entry points. Existing error
    messages are unchanged.

  Also in here

    - ConclusionScope renamed to ConclusionsView in both SDKs. "Scope" now
      means a named set of sessions, which that class is not — it is a view
      over one observer/observed pair. ConclusionScope kept as a deprecated
      alias; the package-level import path only.
    - The TS HTTP client comma-joined array query params, so any list-valued
      parameter arrived as one malformed entry. Fixed at buildURL rather
      than the call site

* fix(scopes): close peer-card leak under limit_to_session, harden SDK inputs

Addresses review findings on the scopes work. All four were verified by
reproducing them, not by reading.

Peer card no longer leaks under any allowlist

  The card was dropped when `sessions` was set but returned when
  `limit_to_session=true` produced the identical allowlist, so a control
  meant to fail closed was defeated by swapping one query parameter. It is
  now gated on the effective allowlist, computed once and shared by the
  representation call and the card read — the duplicated inline
  conditional is what let the two drift apart.

  `POST /peers/{id}/chat` still injects an unscoped card under an
  allowlist (src/dialectic/chat.py fetches it on peer_card.use alone, with
  no reference to session_allowlist). Left alone deliberately: that is a
  behavior change to the shipped dialectic and
Scope validation messages survive the option union

  ScopeOptionSchema is a union, and Zod collapses a failing union into one
  `invalid_union` / "Invalid input" issue, burying the branch errors. Every
  invalid scope on chat/representation reported "Invalid input" and told
  the caller nothing — including the reserved-prefix case the check order
  exists to surface. The rules are now a plain function applied after the
  union resolves, so the specific message reaches the caller for bad
  charset, reserved prefix, empty and over-cap lists alike.

Empty scope no longer fails open

  `session.context({scope: ''})` and `honcho.search(q, {scope: ''})` used
  truthiness checks, so an invalid scope was dropped and the call returned
  *unscoped* results. Both now test against undefined so the value reaches
  the schema.

Session IDs validated before reaching a URL path

  `scope.removeSession('valid-session?typo')` addressed `valid-session`
  with a stray query string: the wrong session removed, and reconciliation
  run against it. Both SDKs now validate the charset first. Python's
  `add_sessions` was unvalidated too — harmless in a JSON body, but
  leaving one path checked and its sibling unchecked is how this recurs.

Also

  - Corrected the `limit_to_session` description: it claimed "only used if
    search_query is provided", but the allowlist reaches
    _query_documents_recent unconditionally.
  - Corrected the documented 1,000-session cap on `sessions`, which is
    unreachable via repeated query params — the request line exceeds h11's
    16 KB and nginx's 8 KB defaults at a few hundred entries, giving an
    opaque 414/431 instead of a 422.
  - Removed a dead route builder.

Tests: 7 new TypeScript cases and 2 new Python classes covering all four
findings. The TypeScript unit suite passes 146/146. The context route
itself is still unexercised — the card gate and the 401 membership check
remain verified by reading only.

Refs DEV-2001, DEV-1996, DEV-2357, DEV-2201

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(sdk): align the conclusions-view error message across both SDKs

The ConclusionScope -> ConclusionsView rename updated the identifier but
not the prose inside the thrown message, which the rename pattern
(\bConclusionScope\b) does not match. TypeScript ended up throwing
"managed by this conclusions view" while Python still threw "managed by
this conclusion scope" — the same error, different text per SDK.

Three server-backed conclusions.test.ts cases assert that message by
regex and failed under `pytest -k typescript`. The four equivalent Python
assertions were passing, because they matched Python's unchanged string —
so fixing only the TypeScript tests would have made the suite green with
the divergence still in place.

Brings Python's message, comments and docstring in line with TypeScript,
and updates the assertions in both suites. `grep -ri 'conclusion scope'`
is now empty.

Verified: 64 passed across tests/sdk_typescript/, tests/sdk/test_conclusions.py
and tests/sdk/test_scope_options.py — the last of which had never been run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 10:48:29 -04:00
ajspig 14538cfc90
Abigail/conclusions level filter (#851)
* feat(conclusions): expose reasoning level + allow filtering by level

The `level` of a conclusion (explicit / deductive / inductive /
contradiction) was filterable server-side but stripped from the
`Conclusion` response and not surfaced in either SDK. This adds it
end-to-end so callers can list explicit-only ("not dreamed on")
conclusions without dropping to raw HTTP.

- api: add `level` to the Conclusion response schema
- python sdk: `ConclusionLevel` type, `level` on Conclusion/response,
  `level=` kwarg on ConclusionScope.list() and the async variant
- ts sdk: `ConclusionLevel` type, `level` on Conclusion/response,
  `level` option on list()
- tests: assert level is exposed; add level-filter list test

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(conclusions): use generic filters= on list() instead of level= kwarg

Match the documented SDK convention (peers/sessions/messages all take a
generic `filters` dict passed through to the same dynamic server-side
filter logic) instead of a one-off `level=` kwarg. `level` filtering now
works as `list(filters={"level": "explicit"})` alongside any other
supported filter/operator.

The `level` field on the Conclusion response (added in the previous
commit) is kept — it's still not otherwise returned by the API.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(conclusions): allow filtering by level on query() in py + ts SDKs

The branch's level-filter work exposed `filters=` on `list()` but left
`query()` (semantic search) hardcoding `{observer, observed}`, so callers
could filter the list endpoint by reasoning level but not semantic search —
asymmetric in both SDKs.

- Python: add keyword-only `filters` to `ConclusionScope.query` and
  `ConclusionScopeAio.query`, merged over the scope's observer/observed.
- TypeScript: add optional `filters` arg to `ConclusionScope.query`,
  mirroring the existing `list()` change.

The server `/conclusions/query` endpoint already honors filters in the body
(verified against production), so this is purely SDK surface parity.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(filters): document filtering conclusions by reasoning level

The using-filters page covered workspaces/peers/sessions/messages but not
conclusions. Add a "Filtering Conclusions" section showing level-based
filtering on both list() and query(), including the common "explicit only"
(exclude dream-derived) case and the in[deductive,inductive] inverse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(conclusions): simplify filter merge to a single dict spread

Replace the merged_filters + if-block pattern in list()/query() (py sync,
aio, ts) with a single dict spread that layers the caller's filters over the
scope's observer/observed (and session). No behavior change — same merge
order (caller wins) — just less code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(conclusions): reject scope-managed keys in SDK conclusion filters

The generic filters= argument on ConclusionScope.list()/query() spread
user-supplied filters last, so a stray observer/observed/session key
silently overrode the scope and returned data from a different peer
pair. Add a fail-loud guard in both the Python and TypeScript SDKs that
rejects scope-managed filter keys with a clear error, directing callers
to peer.conclusions / conclusions_of(target) and the session= parameter.
session_id remains a valid filter on query() (which has no dedicated
session parameter).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2026-07-01 10:48:01 -04:00
doria d79a9f21d2
feat: make session_name nullable for documents and update related SDKs (#347)
* feat: make session_name nullable for documents and update related SDKs

- Introduced a migration to make the `session_name` column in the documents table nullable, allowing for sessionless dreams.
- Updated Python and TypeScript SDKs to reflect the optional nature of `session_id` in conclusion creation and related methods.
- Enhanced tests to cover scenarios for creating conclusions without a session ID, ensuring proper handling of sessionless conclusions.
- Adjusted documentation and type definitions to clarify the optional session context in various components.

* chore: add migration test

* fix: ensure orphaned sessions exist during downgrade for nullable session_name migration
2026-01-26 13:33:11 -05:00
doria dce96889bc
feat: honcho 3.0, sdks 2.0, excise stainless, update v3 docs, changelogs (#331)
* chore: 3.0 honcho and 2.0 sdks changelog

fix: use PeerContextResponse in peer.ts

* chore: move docs to /v3/, build SDKs

* chore: code review

* feat: [WIP] migrate away from stainless in typescript sdk

* chore: move api from /v2/ to /v3/

* feat: no-stainless typescript with real tests

* feat: migrate python sdk off of stainless

* feat: clean typescript sdk

* chore: add tests for ts http client

* fix: rewrite entire python sdk in new format, update typescript sdk to use `configuration` not `config` for consistency with API

* fix: clean up SDKs, synchronize

* chore: update sdk examples

* chore: update OpenAPI documentation and SDK examples to reflect changes

* fix: better test

* fix: install deps in test runner, improve robustness of streaming in sdk, coderabbit nits

* fix: standardize around camelCase in TS SDK

* refactor: update configuration handling in SDKs to use typed models for workspace, session, and peer configurations

* docs: clarify queue status usage and remove polling methods from SDKs

add claude skills for migrations

* chore: fix links in docs

* feat: add deriver flush mode to bypass batch token threshold

- Introduced `is_deriver_flush_enabled` function to check if flush mode is active.
- Updated `QueueManager` to conditionally apply batch token thresholds based on flush mode.
- Enhanced `UnifiedTestExecutor` to enable flush mode via Redis.
- Added `flush` parameter to test cases to facilitate testing of flush mode behavior.
- Updated various test cases to utilize the new flush functionality.

* feat: implement schedule_dream functionality in SDKs, use in unified test runner

- Added `schedule_dream` method to both Python and TypeScript SDKs for scheduling dream tasks.
- Updated HTTP routes to include endpoint for scheduling dreams.
- Enhanced test runner to utilize the new `schedule_dream` method for scheduling actions.
- Updated TypeScript client to support the new scheduling functionality with appropriate parameters.

* feat: update single deriver task to support multiple observers

- Changed the `observer` parameter to `observers` as a list in multiple functions across the deriver module.
- Updated the processing logic to handle multiple observers for representation tasks.
- Adjusted related payload and queue management functions to accommodate the new observers structure.
- Modified tests to reflect changes in the representation task handling and ensure proper functionality.

* refactor: update enqueue tests to support deduplication of queue items with multiple observers

- Modified tests in `test_enqueue.py` to reflect changes in the queue item structure, where each message now results in a single queue item containing a list of observers.
- Updated assertions to validate that the `observers` field correctly includes all relevant peers, ensuring proper functionality of the deduplication logic.
- Removed redundant payload matching logic to streamline test cases and improve clarity.

* fix: add backwards compatibility for representation work unit keys and payload observers

* feat: update dialectic configuration and introduce cost calculator

- Adjusted LLM and dialectic settings in `.env.template`, `config.toml.example`, and `src/config.py` to reduce maximum tool output characters and session history tokens for cost efficiency.
- Implemented a new `dialectic_cost_calculator.py` script to estimate costs based on reasoning levels and model pricing.
- Enhanced `DialecticAgent` to utilize minimal tools and adjusted output token settings based on reasoning level to optimize performance and reduce costs.

* feat: add reasoning level to chat input in unified test runner

- Enhanced the `UnifiedTestExecutor` to include a `reasoning_level` parameter in the chat method call.
- Updated the `QueryAction` model to support the new `reasoning_level` attribute, allowing for more nuanced chat interactions.

* feat: run deriver once for multiple observers (#335)

* feat: update single deriver task to support multiple observers

- Changed the `observer` parameter to `observers` as a list in multiple functions across the deriver module.
- Updated the processing logic to handle multiple observers for representation tasks.
- Adjusted related payload and queue management functions to accommodate the new observers structure.
- Modified tests to reflect changes in the representation task handling and ensure proper functionality.

* refactor: update enqueue tests to support deduplication of queue items with multiple observers

- Modified tests in `test_enqueue.py` to reflect changes in the queue item structure, where each message now results in a single queue item containing a list of observers.
- Updated assertions to validate that the `observers` field correctly includes all relevant peers, ensuring proper functionality of the deduplication logic.
- Removed redundant payload matching logic to streamline test cases and improve clarity.

* fix: add backwards compatibility for representation work unit keys and payload observers

* feat: refactor benchmark runners to share common functionality

- Introduced a new `runner_common.py` module containing shared utilities for benchmark test runners, including common argument parsing, client creation, and queue management.
- Updated `BEAMRunner`, `LoCoMoRunner`, and `LongMemEvalRunner` to inherit from `RunnerMixin`, leveraging shared functionality for metrics collection and logging.
- Added `reasoning_level` and `redis_url` parameters to runner constructors for enhanced configuration.
- Streamlined argument parsing by utilizing `add_common_arguments` for shared command-line options across all runners.

* fix: update last_user_message handling to use message content instead of ID

* fix: standardize config vs configuration

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2026-01-22 15:16:28 -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