Commit Graph
91 Commits
Author SHA1 Message Date
marcopan 450d7ab07f fix: gate workspace diagnostic migration 2026-08-03 23:30:15 +02:00
marcopan c5685f4962 feat: define workspace diagnostic contracts 2026-08-03 23:16:23 +02:00
marcopan 319d1add2e fix: harden workspace diagnostics probes 2026-08-03 22:59:06 +02:00
marcopan ff795d1c91 feat: diagnose workspace connector bindings 2026-08-03 22:52:29 +02:00
marcopan e2d1117614 fix: make workspace registry lock process-bound 2026-08-03 22:42:57 +02:00
marcopan 553bb41138 fix: harden workspace registry refresh and snapshots 2026-08-03 22:33:39 +02:00
marcopan 2087fbb0c9 feat: manage workspace Git checkout and snapshots 2026-08-03 22:21:10 +02:00
marcopan 5d7ebc5b01 fix: harden workspace runtime snapshots 2026-08-03 22:10:09 +02:00
marcopan 049f8675c6 feat: resolve workspace bindings into runtime configs 2026-08-03 21:49:57 +02:00
marcopan 5a654939a5 fix: harden workspace schema contracts 2026-08-03 21:40:58 +02:00
marcopan 92cb0545be feat: add canonical workspace schema 2026-08-03 21:31:07 +02:00
marcopan 6434c9c4e1 fix: reject reserved Git HEAD branch 2026-08-03 21:22:08 +02:00
marcopan cc951d8073 fix: harden workspace registry config validation 2026-08-03 21:19:29 +02:00
marcopan 6e1321f93c feat: configure Git workspace registry 2026-08-03 21:13:23 +02:00
marcopan 00761ae2ca fix: enforce one Pi runtime per user 2026-07-21 16:13:17 +02:00
marcopan a040c083cc fix: reap finalized runtimes before session start 2026-07-21 16:04:08 +02:00
marcopan 019f6b282e fix: release finalized Pi runtimes 2026-07-21 15:39:34 +02:00
marcopan 801f847ec4 fix: use Pi user auth and handle startup failures 2026-07-21 14:01:47 +02:00
marcopan 2ff63d371f feat: harden workflow gates and expose token usage 2026-07-21 12:14:26 +02:00
marcopan 0cf09777f2 Fix session resume and PSD container configuration 2026-07-20 20:22:47 +02:00
marcopanandClaude Fable 5 f772ef9dca fix(backend): robustness pass — spawn leak, timeouts, workspace fail-loud, 409 order, respond guard
Audit findings 4.1-4.6.

- spawnFor: a rejected configure/start no longer leaks a registered runtime
  with a live Pi child (identity-checked teardown + rethrow); every later
  start used to hit "session runtime already active".
- ThtRunner.run: default 60s timeout on every tht child (SIGKILL backstop),
  120s for DWH-touching calls (sql preview/export, search pack); a dropped
  VPN mid-call no longer wedges the HTTP request forever.
- configArg: a NAMED workspace whose yaml is missing now throws instead of
  silently falling back to the default config (operations were silently
  targeting the wrong workspace).
- resume: the finalized/archived 409 is evaluated BEFORE the alreadyActive
  fast-path — the manifest is the truth even with a lingering runtime.
- ollamaEnsure: exit-0 with non-JSON stdout is a failed check, not ok:true.
- SessionBridge.respond: only the response matching the pending descriptor
  is forwarded to Pi; stale/duplicate submissions return 409 instead of
  being sent with the current gate's RPC id.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 01:37:20 +02:00
marcopanandClaude Fable 5 2958b32fd5 fix(backend): generation-aware SSE event ids — stale cursors can no longer eat events
Audit finding 3.1 (high, 3/3 reviewer consensus). Event ids restart at 1
when the backend restarts; a browser auto-reconnect carrying the old
numeric Last-Event-ID was honored whenever the new process had already
emitted that many events, silently suppressing fresh events (same ids,
different content). The previous guard only caught cursor > lastId.

Wire ids are now "<generation>:<seq>" (generation = per-hub instance
token; seq = the existing per-session monotonic counter). The hub parses
raw header/query candidates itself: other-generation and legacy bare-
number cursors are stale → replay from the beginning; same-generation
cursors keep the newest-valid-wins behavior. EventSource treats ids as
opaque, so no frontend change.

Finding 3.2 (eviction) resolved by NOT evicting: close keeps the seq
counter on purpose (sessions reopen; monotonicity is what makes old
cursors detectable) — documented at the call site; buffers are emptied by
clear() and ring-bounded at 200.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 01:32:47 +02:00
marcopanandClaude Opus 4.8 3453f3ae23 feat: pre-check DWH reachability before creating a session (local dev only)
New session now refuses to spawn a Pi runtime that would only die in bootstrap
retrieval when the DWH/vector host is unreachable (e.g. a dropped VPN). Before
`session new`, POST /sessions probes the DWH via `tht db ping`; if it is down it
returns 503 {code:"dwh_unreachable"} with a clear message and creates nothing.

- Gated behind the THT_DWH_PRECHECK flag (default off), enabled only by the local
  dev launcher (run-stack.sh) — containers/CI never pay the probe, and existing
  tests that don't set it are unaffected.
- ThtRunner.dbPing() runs `tht db ping` with a 10s timeout (run() gains an optional
  timeout that SIGKILLs a hung child).
- Frontend: apiFetch throws a typed ApiError (status + parsed payload); the new-
  session composer shows the specific alert on `dwh_unreachable` instead of the
  generic retry hint, keeping the question for retry.

Verified live on an isolated backend (precheck on + broken DWH host → 503
dwh_unreachable, no session created) and via unit tests (backend 228, frontend 308).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 12:08:47 +02:00
marcopanandClaude Opus 4.8 f979ada5e7 feat(frontend): open a live session straight to its pending gate
Opening an in-progress session that has a live Pi runtime now reconnects to
its pending gate instead of the empty landing screen that read as "stopped".
Cold/completed sessions keep the read-only documents panel with its explicit
Resume, so a mere click never spawns a runtime. Backend GET /sessions now
reports a per-session `active` flag (live runtime bound) to drive this.

Also:
- "New session" now closes any open session detail panel (left box).
- The model-activity separator can be dragged to a full 50/50 split
  (was capped at 576px); central-min still guards narrow viewports.

Test fixes uncovered along the way:
- Node 25 ships an experimental global localStorage that shadows jsdom's and
  lacks clear(), failing every jsdom test at setup; install a spec-compliant
  in-memory Storage (feature-detected, inert on CI/LTS).
- Fix 4 pre-existing session-mgmt tests that used an ambiguous getByText for a
  session shown in both nav and header; target the nav item by test id.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 22:56:12 +02:00
marcopanandClaude Opus 4.8 c5fd03ed84 feat(backend): let pi self-authenticate providers from its own auth store
The backend injects a single managed model key (THT_MODEL_API_KEY[_FILE]) as
the selected provider's env var, but that key belongs to one provider — so
selecting a second cloud provider (e.g. DeepSeek while the managed key is zai's)
forced the wrong key onto it and failed auth. This is why the model could not be
switched to DeepSeek.

When the selected provider is present in pi's own auth store
(~/.pi/agent/auth.json), skip injection and let pi resolve that provider's key
itself. Deployments without an auth store (containers) yield an empty set, so the
managed-key injection stays authoritative and fail-fast there. authProviders is
injectable into PiProcessManager for deterministic tests.

Verified live: GLM 5.2, DeepSeek V4 Flash, and aritmolab Qwen3.6 all operate through the ThothII model selector.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 18:26:18 +02:00
User 5cacf70a0d fix: bootstrap user preferences without invalidating DWH cache 2026-07-16 20:32:20 +02:00
User ccb3cf4aa9 test: cover user-owned session security boundaries 2026-07-16 19:16:58 +02:00
User cadc4c6947 docs(deploy): document user-owned session cutover 2026-07-16 19:02:58 +02:00
User b454fb478b fix(backend): harden principal child isolation 2026-07-16 18:38:40 +02:00
User 458eb13c89 feat(backend): enforce user-owned sessions 2026-07-16 18:32:52 +02:00
User 0bcec1591b fix: close delete resume and SSE replay races 2026-07-15 02:08:11 +02:00
User 6747f6f8a0 fix: serialize session lifecycle transitions 2026-07-15 01:37:42 +02:00
User 08b1f4909e fix: make session resume atomic across restarts 2026-07-15 00:42:35 +02:00
User 2b4797f133 fix: harden resume and SSE replay 2026-07-15 00:06:00 +02:00
User d2d8029ff2 fix(security): sanitize session bootstrap failures 2026-07-14 23:13:21 +02:00
User e0a97a01f3 feat(backend): expose sanitized tool activity 2026-07-14 22:46:22 +02:00
User df1e7dea9c fix(resume): recover cleanly after Pi exits 2026-07-14 21:35:50 +02:00
User b1d1284cc8 fix(backend): restart idle Pi sessions on resume 2026-07-14 20:44:59 +02:00
User aba8666f16 fix(backend): track Pi turn lifecycle 2026-07-14 20:39:45 +02:00
User 0cf86e3540 fix(backend): allow configured local Qwen provider 2026-07-14 18:44:12 +02:00
User 3d23d0543c fix(backend): validate configured model provider pair 2026-07-14 18:39:37 +02:00
User c463de8da2 fix(backend): scope Pi model listing to enabled models 2026-07-14 18:33:15 +02:00
User 1b78f72b64 feat(backend): read Pi enabled model scope 2026-07-14 18:27:55 +02:00
User 333874a755 fix: make join approval atomic 2026-07-14 15:24:06 +02:00
User c7474f3852 fix: restore model activity reasoning stream 2026-07-14 15:03:07 +02:00
User 6dbf93fff9 feat: harden runtime readiness and session workflow 2026-07-14 10:27:25 +02:00
marcopan 7628eaa579 fix(docker): run real questions through trusted Pi gate 2026-07-12 19:20:10 +02:00
marcopan 8518a73685 fix(security): scrub raw deployment secret values 2026-07-12 11:34:32 +02:00
marcopan d500563963 fix(security): scrub deployment secrets from Pi child 2026-07-12 11:33:13 +02:00
marcopan 3807c65a41 test(config): cover secret bundle inode races 2026-07-12 11:09:09 +02:00