Commit Graph
60 Commits
Author SHA1 Message Date
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
marcopan 5fe74612fb feat(config): load one validated secret bundle 2026-07-12 11:06:19 +02:00
marcopan 2302286ea1 fix(backend): reject compound provider credentials 2026-07-12 08:10:47 +02:00
marcopan f064daef09 fix(backend): harden provider credential isolation 2026-07-12 08:05:51 +02:00
marcopan e40a9d9a56 fix(backend): inject provider credentials from file 2026-07-12 07:53:22 +02:00
marcopan a3a266fd81 fix(deploy): close container final review 2026-07-12 00:44:39 +02:00
marcopan 767df63a33 feat(deploy): add portable external-service stack 2026-07-11 22:12:21 +02:00
marcopan 3ac0623247 fix(backend): make data root config authoritative 2026-07-11 21:35:54 +02:00
marcopan c6c00c336a feat(backend): support container runtime paths 2026-07-11 21:32:33 +02:00
marcopanandClaude Fable 5 2410f01b34 fix(bridge): forward Pi agent_end so the spinner stops at workflow completion
The FE derived 'working' purely as activeSession && !pendingWidget, so the
final workflow turn — the only one that ends without a follow-up gate —
left the spinner on forever (observed live: 21592s after F8 approve).

- SessionBridge maps Pi's agent_end -> SSE system_event {event: agent_end}
- PiProcessManager notifies the client (info error + synthetic agent_end)
  when the child dies unexpectedly; expected teardowns stay silent
- sessionStore tracks agentActive (on: user entry/text_delta/ui_request,
  off: agent_end); AppShell working now requires it; resume sets it
  optimistically

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:14:43 +02:00
marcopanandMarco Pancotti ad5a4d938c test(backend): pin schema-linking structured response passthrough 2026-07-06 23:25:00 +02:00
marcopanandClaude Opus 4.8 418187a4ad fix(backend): echo Pi's RPC id so reviewer gates unblock after answer
ctx.ui.input in `pi --mode rpc` correlates extension_ui_response on its own
top-level RPC id (crypto.randomUUID), not the descriptor id the gate carries
in `title`. SessionBridge replied with the descriptor id, so Pi silently
dropped the response and the model never resumed — every reviewer widget hung
after the human answered.

SessionBridge now stores Pi's top-level m.id (pendingPiId) and replies
extension_ui_response{ id: pendingPiId, value: <uiResponse> }; value still
carries the descriptor id so the gate's internal resp.id === descriptor.id
check still holds.

The fake-pi double had masked the bug by forcing m.id == descriptor.id; it now
mirrors real Pi (distinct randomUUID, correlate on it, drop unknown ids), with
a negative regression test. SKILL.md Phase 1 also now steers multi-answer
disambiguation to reviewer_decide (multiselect).

Tests: backend 67/67, tsc clean, fake-pi contract 2/2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 10:43:49 +02:00
marcopanandClaude Opus 4.8 37d40d6680 fix(backend): drop pi --approve flag (removed in pi 0.73 @mariozechner rebrand)
pi 0.73 rpc mode is headless and runs tools without an approval gate; the
removed --approve flag made pi exit with 'Unknown option: --approve', breaking
every session spawn. Spawn args are now just --mode rpc. +regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 21:03:48 +02:00
marcopan a791919925 fix(backend): check read-only 409 before the Ollama preflight on resume 2026-06-29 20:06:03 +02:00
marcopanandClaude Sonnet 4.6 90a26dafce feat(backend): Ollama embeddings preflight on session create/resume (503 hard-fail)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 19:37:39 +02:00
marcopan a0af089d00 feat(backend): ThtRunner.ollamaEnsure (parses tht ollama ensure --json) 2026-06-29 19:31:49 +02:00
marcopan 790ac71284 feat(backend): spawnFor new/resume prompt mode; resume sends /riprendi-sessione 2026-06-29 12:38:26 +02:00
marcopan 387ae56583 test(backend): make mgr injectable; assert teardown-before-delete ordering 2026-06-29 12:36:29 +02:00
marcopanandClaude Opus 4.8 bd29ac517c feat(backend): rename/group/archive/unarchive/delete/documents routes + resume read-only guard
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 12:32:52 +02:00
marcopan 4a63db51e8 feat(backend): ThtRunner session mutation + documents methods 2026-06-29 12:29:14 +02:00
marcopanandClaude Opus 4.8 f59d6d1478 fix(backend): allow PUT in CORS methods so browser can save settings
Found via live browser test: PUT /settings preflight was rejected because
@fastify/cors default methods omit PUT. GET/POST were unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:54:30 +02:00
marcopanandClaude Opus 4.8 ec36ee421a feat(backend): POST /sessions applies global settings, body is question-only
workspace/provider/model/thinking now come from getSettings() injected into
sessionRoutes; the request body supplies only question+name. Also teaches
fake_pi_rpc to respond to set_model and set_thinking_level RPC commands so
tests that pass real model settings don't hang.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:16:08 +02:00
marcopanandClaude Opus 4.8 098915515f fix(backend): deterministic /models fallback test + merge duplicate import
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:12:18 +02:00
marcopanandClaude Opus 4.8 bfbb017413 feat(backend): /settings GET+PUT, /models PiModel shape, app wiring
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:09:04 +02:00
marcopanandClaude Opus 4.8 c4b599ec00 feat(backend): ephemeral Pi model lister (get_available_models) with TTL cache
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:04:33 +02:00
marcopanandClaude Opus 4.8 6b2883a467 feat(backend): persistent settings store + settingsFile config
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:01:22 +02:00
marcopanandClaude Opus 4.8 091a055bf0 fix(backend): bridge maps real Pi message_update->text_delta for FE streaming
Live end-to-end against real pi --mode rpc revealed Pi streams assistant text as
top-level message_update events whose nested assistantMessageEvent carries the
incremental delta — not the top-level text_delta the fake-pi-rpc emits. The bridge
now maps message_update(assistantMessageEvent.text_delta).delta -> FE text_delta,
so the chat shows the model's output during a real session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 15:03:13 +02:00
marcopanandClaude Opus 4.8 1ee692cdb1 fix(backend): tht --config is per-command, append after subcommand (not global -c)
Live end-to-end surfaced a 500 on every tht call: ThtRunner prepended
'-c <config>' before the subcommand, but tht has no global -c option
('No such option: -c'). --config/-c is a per-command option, so it must be
appended AFTER the subcommand. Extracted buildArgv() and fixed the unit test
that had codified the wrong (prepended) order.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 14:52:39 +02:00
marcopanandClaude Sonnet 4.6 c63b2bd126 fix(backend): idempotent spawnFor + identity-checked exit + unified SSE re-emit shape
- spawnFor now tears down any existing runtime for the same session id before
  the cap check, so resume/respawn neither leaks the old child nor falsely hits
  maxPiProcesses
- exit handler is identity-checked (captures rt) so a stale child's late exit
  cannot evict a newer runtime
- SSE pending re-emit now sends the full ClientEvent shape
  { type: "ui_request", ui_request } to match hub.publish live events
- tests: same-id respawn replaces runtime (count 1); old child exit does not
  evict new runtime; sse-hub re-emit asserts unified shape

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 21:53:12 +02:00
marcopanandClaude Sonnet 4.6 b16c94e30b feat(backend): resume + venv PATH + end-to-end F1 smoke (fake-pi-rpc)
- Add PiProcessManager.resume(sessionId, tht): reads provider/model/thinking
  from tht.sessionShow() and calls spawnFor with those values
- Add POST /sessions/:id/resume route: calls mgr.resume then re-wires
  bridge.onClientEvent → hub.publish
- Confirm venv PATH already present in real spawn (no change needed)
- Add e2e test: POST /sessions → SSE receives ui_request via pendingWidget
  re-emit → POST /sessions/s1/response → 204 (all over real HTTP against
  fake-pi-rpc)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 21:37:24 +02:00
marcopanandClaude Sonnet 4.6 d630cac3ab feat(task-10): SQL routes + /workspaces + /models + fix sql preview path bug
Harness:
- preview_cmd FILE positional arg made optional; when omitted with --session,
  path is derived via _session_sql_file (mirrors export_cmd) — fixes the
  deferred Task-5 bug where the backend passed sessions/<id>/sql_final.sql
  relative to harnessDir, which broke for workspace-dependent paths.
- New pytest: test_preview_session_no_file_resolves_sql_final

Backend:
- ThtRunner.sqlPreview: drop positional file arg; use --session only
- New routes/sql.ts: POST /sessions/:id/sql/preview + /export
- New routes/meta.ts: GET /workspaces (yaml scan) + GET /models (injectable
  seam + graceful fallback to {models:[]})
- app.ts: register sqlRoutes + metaRoutes; add listModels to BuildAppDeps
- tht-runner.test.ts: add sqlPreview argv assertion (no file path)
- test/routes-sql-meta.test.ts: 9 tests (sql preview/export + meta routes)

Tests: harness 233 passed; backend 29 passed; build clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 21:31:35 +02:00
marcopanandClaude Sonnet 4.6 061002f85c fix(backend): forward workspace to tht config selection (MVP backend-side)
POST /sessions no longer silently drops `workspace`. ThtRunner.run/json
take an optional workspace; configArg() selects workspaces/<ws>.yaml when
it exists under harnessDir, else falls back to default configPath.
sessionNew threads workspace through. Route forwards b.workspace with an
MVP note (gate/Pi side still single-workspace via symlinked config/tht.yaml).

19/19 tests pass, tsc clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 21:26:09 +02:00