Skip to content

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 06 Sep 11:02
· 157 commits to main since this release

Review remediation: release pipeline, server sessions, transport hardening & docs truth (spec 021-2 / 021-3)

Includes 021-3 remediation work orders W001–W041 resolving server session concurrency and turn locking, NAT64 metadata defense-in-depth, transport lifecycle safety, and docs truth.

Breaking changes:

  • SDK API rename — askSeepient / runSeepientServer: The one-shot entry points generateText/streamText are replaced by a single unified askSeepient(prompt, options); the server factory createServer/startServer is renamed runSeepientServer. Migration (pre-1.0, no shims):

    Old (≤ v0.6.x) New (v0.7.0)
    generateText(prompt, options?) askSeepient(prompt, options?)
    streamText(prompt, options?) askSeepient(prompt, { ...options, stream: true })
    createServer(options?) / startServer(options?) runSeepientServer(options?) (use listen: false for an unattached http.Server)
    GenerateTextOptions / StreamTextOptions AskSeepientOptions
    GenerateTextResult AskSeepientResult
    StreamTextResult AskSeepientStreamResult
    ServerOptions RunSeepientServerOptions (now exported only from seepient/server, next to runSeepientServer)

    Additional surface changes in the rename: toResponse() now accepts optional { headers }; phantom structured-output fields were removed (AskSeepientOptions.output, AskSeepientResult.data/error — structured output remains deferred); Seepient.chatStream() options no longer accept stream/signal (it is always streaming and owns its abort handle via the returned result); RunSeepientServerOptions.settingsManager is typed by the SettingsManagerLike contract instead of any. Update all imports and call sites accordingly — no compatibility aliases are provided in the pre-1.0 phase.

  • SDK session ownership binding (tenant isolation): createSeepient now stamps the effective principalId (default "sdk-user") on every persisted session (SessionData.principalId) and fails closed with SESSION_OWNERSHIP_MISMATCH when resuming under a different principal — previously a second tenant supplying the same sessionId and its own stores could restore and continue the first tenant's conversation. Sessions persisted before this change carry no owner stamp and will not resume (delete the stored session or start a new sessionId). Custom PersistenceBackend implementations must round-trip the new principalId field; the deprecated messages-only SessionStore adapter cannot carry ownership and now fails closed when resuming an existing session. Expanded API key hash digests from 16 to 64 hex characters (full SHA-256). Pre-0.7.0 session files created under 16-hex key ownership will not resume and fail closed with a descriptive error. Server sessions are ephemeral runtime state.

Release pipeline & packaging safety (US1):

  • Static publish hook safety: Restored prepublishOnly to pnpm run build (HEAD semantics); added pnpm run pack:verify static hook assertion preventing clean/delete scripts in publish hooks and verifying native exact-commit helper staging.
  • Release workflow gate: Added pack:verify gate to .github/workflows/release.yml between staging and npm publish.
  • Docker CI health & documentation: Added CI container image build & health check job to release pipeline; updated Dockerfile and standalone usage documentation comments; removed deprecated SEEPIENT_SHELL_APPROVE.
  • Artifact hygiene: Removed uncommitted temporary visual check artifacts and stale architecture dumps.

Server sessions & WebSocket integrity (US2):

  • Stateless one-shot chat & session adoption (D1): POST /v1/chat and WebSocket chat requests sent without a sessionId operate statelessly as one-shot turns — no session files are created on disk, no session capacity limits are consumed, and no sessionId is returned. Requests providing an explicit sessionId adopt or create the session and persist full turn history.
  • Id-preserving session lifecycle: createSession adopts client-supplied IDs across memory and persistence backends with strict charset validation and collision refusal. addMessage fails loudly on missing sessions.
  • GET /v1/sessions endpoint: Added authenticated collection listing returning SessionSummary metadata for the caller's key only (no message bodies).
  • REST session resumption: POST /v1/chat accepts optional sessionId, resumes conversation history, persists turn messages, and echoes session identity.
  • WebSocket concurrency guard & lifecycle: Single in-flight chat per connection with REQUEST_IN_FLIGHT error frame; abort signals reach active streaming controllers; pipeline identities use UUIDs.
  • SDK turn mutex durability: chatStream finally block guarantees mutex release() executes even if session persistence fails.

SDK parity & type truth (US3):

  • One-shot option parity: Added purpose and tier options to GenerateTextOptions and StreamTextOptions, threaded to agent loop resolution.
  • Declaration type narrowing: Narrowed TrustedHostToolEffectDeclaration to supported effect kinds (network-egress, secret-use, model-egress), injecting boundary defaults and eliminating untyped casts.
  • Unified local store predicate: Added isLocalAuditStore helper unifying local filesystem store detection across SDK, HTTP server, and lifecycle factory.
  • Runtime contract extension: Extended ProviderRuntimeContract with optional inspection and listener cleanup methods, eliminating (runtime as any) probes.

021-4 remediation work order (W100–W164):

  • API rename landed (D1 adopt): askSeepient / runSeepientServer are the public API; abort reaches media operations again; onError parity in non-streaming mode; hooks.onFinish fires in streaming mode; phantom structured-output fields removed; chatStream options narrowed; SettingsManagerLike contract replaces settingsManager?: any.
  • Embedding safety (W130/W131): runSeepientServer registers signal handlers only when listening and returns a dispose() handle; the WebSocket layer is per-instance (independent WSS + registries), so multiple servers can live in one process.
  • Security parity (W140–W146): WS handlers enforce the REST scope model (agent:run/agent:read); the effect broker and the transport validator now share byte-level IP classification (hex-mapped, v4-compatible, NAT64, and reserved IPv4 spellings denied); gateway OpenAPI import and REST calls route through the SSRF-validated pinned fetch (gateway_import_openapi re-classified communications); cross-host redirect credential stripping is case-insensitive incl. X-Api-Key; safeSsrfFetch gained a cross-hop deadline; default CORS no longer reflects arbitrary origins; WS messages consume the shared rate limiter, per-key connection caps enforced (SEEPIENT_WS_MAX_CONNECTIONS_PER_KEY), and non-/ws upgrades are 404'd.
  • Session integrity (W150–W154, D3 option a): send-time history normalization collapses dangling failed-turn user messages on the model-input copy (stored crash-recovery data intact); resolved-error turns persist no empty assistant row and answer 502; the absolute session TTL defers to in-flight turns and deleteSession refuses while a turn is live; denied cross-key probes no longer cache the victim's session; sessionId charset/length aligned (128-char cap).
  • Transport polish (W160–W164): unified capped body reader (413 on settings/gateway/provider routes); server.rateLimitRpm re-read per request; shutdown closes keep-alive sockets and flushes the audit outbox before force-exit; sanitized error envelopes with raw detail only in logs; scripts/ now typechecked in the test gate with a docs-sync test denying deleted API symbols; strict IPv6 hex validation; media and webhook fetches routed through the validated fetch.

Transport hardening & documentation truth (US4):

  • Default CORS no longer reflects arbitrary origins (021-4 W145): with no server.corsOrigins setting and no SEEPIENT_CORS_ORIGINS env var, responses carry no Access-Control-Allow-Origin header — browser cross-origin access is now opt-in. Set the setting or env var to an origin allowlist, or * to reflect any origin. Combined with the default 0.0.0.0 bind, the previous default made the allowlist feature moot out of the box.
  • SSRF socket IP pinning: Reused socket lookup override primitive via pinnedFetch to connect strictly to pre-validated IP addresses, eliminating DNS rebinding TOCTOU windows. Added 5-hop redirect limit and extended private/reserved CIDR blocks.
  • Transport DoS caps: Enforced 10 MB default body limit on REST requests (413 PAYLOAD_TOO_LARGE), 1 MiB WebSocket frame size (maxPayload), 300 rpm per-key token bucket rate limiter (429 RATE_LIMITED), and configurable CORS allowlist (SEEPIENT_CORS_ORIGINS).
  • Structured request logging: Added JSON-line logger with requestId correlation at transport seams; sanitized 500 error responses to prevent internal detail leaks.
  • Documentation reorganization: Removed duplicate docs/embedding/workers.md in favor of canonical docs/sdk/stateless-workers.md, repointed VitePress sidebar and documentation references, and configured .gitignore for docs/.vitepress/dist.
  • Documentation truth: Corrected docs license to BUSL-1.1; removed phantom seepient/react guide; corrected /sdk/session-persistence links and server persist option.