Commit Graph
202 Commits
Author SHA1 Message Date
Alex 1475ac2bf9 fix: fk and collision issue 2026-06-18 16:21:42 +01:00
Alex b7a8eaeb9f Merge pull request #2548 from arc53/team
feat: teams
2026-06-17 10:21:42 +01:00
Alex 2923f85ce3 fix: better s3 bucket var names 2026-06-16 18:08:04 +01:00
Alex 1b7f84d981 Merge remote-tracking branch 'origin/main' into team 2026-06-16 17:34:27 +01:00
Alex 26d741ab94 feat: notifications on share 2026-06-16 14:50:55 +01:00
Alex 8c8eb3c679 fix: ci fixes 2026-06-16 12:22:07 +01:00
Alex 884bea7286 fix: test 2026-06-16 12:14:39 +01:00
Alex d21f4758be feat: teams 2026-06-16 09:21:21 +01:00
Alex 9a349401f4 feat: admin dashboard 2026-06-15 11:30:02 +01:00
Alex 42678e95cd feat: admin role rbac 2026-06-14 21:36:07 +01:00
Alex ee4abf1038 fix: keep agent published on update 2026-06-14 14:44:27 +01:00
Alex 3e968be6cc Merge origin/main into agent-exports; renumber agent-slug migration to 0019
main added migration 0018_tool_attempts_attribution (revises 0017_oidc_scim), which collided with the feature's 0018_agent_slug. Renumbered the agent-slug migration to 0019 (revises 0018_tool_attempts_attribution) so the Alembic chain stays linear (single head). Auto-merge was conflict-free — models.py, the frontend API layer, and all 7 locale files merged additively.
2026-06-13 14:42:50 +01:00
Alex f8e2fb715e Merge pull request #2534 from arc53/logs-and-anal-revamp
revamp analytics & logs — per-agent attribution, unified log timeline
2026-06-13 14:25:32 +01:00
Alex cd6f40471a fix: sanitise on request 2026-06-13 13:55:13 +01:00
Alex 5f29a384e2 feat: agent import / export 2026-06-13 13:31:46 +01:00
Pavel 9fb59785b2 Second fixes batch 2026-06-13 15:39:16 +04:00
Pavel 59704b0f73 sec fixes 2026-06-12 18:19:15 +04:00
Alex b8eaa5a68d fix: mini tool call issues 2026-06-11 16:49:52 +01:00
Alex 636d4d35d1 feat(prompts): revamp preset prompts, tool naming, and prompt templating
Rewrite the default/creative/strict presets (classic + agentic) into
structured sections: grounding and cite-by-title guidance, insufficient-
context behavior, current date, respond-in-user-language, scoped mermaid
usage, an untrusted-content guardrail, and a conditional XML-tagged
document context block. A memory directory listing is injected at render
time via the template prefetch mechanism so the model starts oriented
without burning a tool call.

Fixes along the way:
- Agentic preset swap was dead code: _get_prompt_content cached the
  classic preset before create_agent's swap check ran, so agentic and
  research agents always got the classic preset. The swap now happens
  inside _get_prompt_content.
- Jinja autoescape corrupted document content in custom prompts
  (< -> &lt;); prompts are not HTML, autoescape is now off.
- Literal {summaries} leaked into the prompt when no docs were
  retrieved; the placeholder is now stripped.
- Agentic/research prompts referenced tool names from a dropped naming
  scheme (search_internal, reason_think); they now reference the real
  names (search, reason).
- The strict preset told the model to "be very creative and use your
  imagination" right after "never make up information".
- extract_tool_usages recorded intermediate attribute chains as
  bare-tool usages, which meant "run all actions" at prefetch; only
  maximal chains are recorded now.
- Headless runs retrieved docs but never rendered them into the
  prompt; the prompt is now rendered like the streaming path.
- Default tools were unreachable by name in prompt templates
  (prefetch results were keyed by synthetic id only); defaults now
  claim the name key unless an explicit row shadows it.

Tool layer: memory/notes/todo actions are namespaced (memory_view,
note_overwrite, todo_create, ...) with legacy unprefixed names still
accepted via prefix stripping; duplicate action names across tools are
disambiguated with the owning tool's name instead of numeric suffixes;
thin tool descriptions rewritten (brave, duckduckgo, telegram, ntfy,
cryptoprice, read_webpage, internal_search, think).

Docs are now wrapped per chunk in <document index>/<source>/<content>
tags for citation-by-title support.
2026-06-11 11:18:46 +01:00
Pavel 64e19f4d11 revamp analytics & logs — per-agent attribution, unified log timeline
- Move analytics endpoints to Postgres with agent filtering that matches both stamps (api_key for external traffic, agent_id for owner/headless)
- Add tool & schedule analytics, token grouping (model/agent/source) and side-channel toggle
- Merge chat/system/webhook/workflow/schedule events into one logs timeline with level/type/search filters
- Stamp user/agent on tool_call_attempts at propose time (migration 0018) and backfill via parent message
- Frontend: revamped Analytics charts and Logs page
2026-06-11 13:35:57 +04:00
Alex 10cc16eab0 Merge pull request #2530 from arc53/oidc-login
feat: oidc login
2026-06-10 17:36:36 +01:00
Alex 321d88d5be fix(oidc): close re-review gaps in the hardening commit
Follow-up to the OIDC security hardening, from a max-effort re-review:

- Refresh now re-checks the denylist immediately before minting, against the
  (possibly remapped) identity but anchored on the original session `iat` — so a
  back-channel logout / SCIM deny that lands during the IdP grant, or one
  targeting the refreshed sub/sid, still blocks renewal instead of being escaped
  by the renewed token's fresh iat. Completes the watermark revocation fix.
- SCIM PUT `userName` immutability check is now case-insensitive, matching the
  case-insensitive list/create — a differently-cased userName echo no longer
  400s "userName is immutable" and blocks deprovision. Completes the SCIM
  case-insensitivity fix.
- Drop the unconditional state-cookie deletion on every callback exit: it let
  one tab's callback clear another in-flight tab's cookie, breaking concurrent
  logins. The cookie self-expires (max_age) and the Redis state is single-use,
  so the delete wasn't needed.

Tests added for the refresh revocation re-check and the case-insensitive PUT.
2026-06-10 14:15:32 +01:00
Alex a3d09e8890 fix(oidc): harden session security from PR review (CSRF, revocation, refresh)
Address the high/medium correctness findings on the OIDC/SCIM PR:

- Login CSRF / session fixation: bind `state` to a Secure/HttpOnly/SameSite=Lax
  cookie at login and require the callback to echo it, so a code+state captured
  from another browser can't silently sign a victim into the attacker's account.
- Require `exp` on session JWTs under AUTH_TYPE=oidc (require_exp), so an
  exp-less HS256 token signed with JWT_SECRET_KEY can't authenticate forever or
  outlive the denylist.
- Denylist now keys revocation on an `iat` watermark instead of a deletable
  flag: a fresh login (newer iat) self-supersedes a revocation without clearing
  it, so sessions revoked on other devices stay revoked. Drops the
  login/SCIM-reactivation denylist-clearing paths (allow_user/allow_idp_sub).
- Refresh: gate the disabled-account check on the post-grant identity (not just
  the old sub); attempt the IdP grant before consuming the refresh token and
  return a retryable 503 (restoring the token) on transient IdP errors instead
  of force-logging-out a live session.
- Gate the oidc blueprint at request time on AUTH_TYPE=oidc, so non-oidc
  deployments cleanly 404 these routes instead of 500-ing on an unset
  OIDC_ISSUER (mirrors SCIM_ENABLED).
- Surface revocation write failures: back-channel logout returns 502, and SCIM
  deactivation rolls back and returns 503, when the denylist write fails — so
  the IdP retries instead of recording a logout/deprovision that didn't revoke.
- Back-channel logout: require `jti`, run the replay check unconditionally, and
  reject stale `iat` beyond the replay-cache window.
- Make migration 0017 idempotent (IF NOT EXISTS) so re-apply can't wedge startup.
- SCIM userName matching is case-insensitive (caseExact=false) for the list
  filter and create-dedup.

Tests added/updated across test_oidc.py, test_scim.py, test_auth.py,
test_app_routes.py and the SCIM integration test.
2026-06-10 13:58:58 +01:00
Alex fc88d11594 fix: test 2026-06-10 01:46:26 +01:00
Alex 160736f075 feat: visibility settings 2026-06-10 01:40:53 +01:00
Alex 054f0f1b8b feat: oidc renewal, groups, logout, scim 2026-06-10 00:57:18 +01:00
Alex 8bc777a428 feat: oidc login 2026-06-09 23:24:44 +01:00
Alex 5d8187126d fix: tests 2026-06-09 19:08:15 +01:00
Alex 28d62f4199 feat: better tool call followup on compat mode 2026-06-09 18:20:14 +01:00
Alex 82bacfdb2b feat: conv visibility 2026-06-08 23:46:28 +01:00
Alex 73ed1cf607 feat: async events on asgi 2026-06-08 22:02:21 +01:00
Alex 1f06f31aa3 feat: better shutdowns 2026-06-08 15:07:31 +01:00
Alex 59e9523666 fix: revoke stale UI surfaces when durable state goes terminal
The "Tool approval needed" toast (and several sibling surfaces) could
linger after the state they represent was already gone. User-scoped SSE
events (tool.approval.required, schedule.autopaused, attachment.queued,
…) are durable and replayed on reconnect, but no terminal path emitted a
matching clearing event and the reconciler only wrote operator-facing
stack_logs — so a failed/expired message replayed its approval prompt
with nothing to act on, and the toast trusted event presence over the
actual message state.

Backend — emit a user-facing event on every terminal path:
- reconciler deletes pending_tool_state and publishes
  tool.approval.cleared when a stuck message is failed;
  cleanup_pending_tool_state does the same for TTL-reaped rows
- reconciler now publishes source.ingest.failed (stalled ingest),
  schedule.run.failed (timeout/pending) and schedule.completed (once)
- schedules PATCH-resume / DELETE publish schedule.resumed / .cancelled
  so a stale schedule.autopaused can't outvote them on replay
- store_attachment gains the on_poison terminal-event hook the ingest
  tasks already have; mcp_oauth_task gains a soft/hard time limit so a
  hung flow self-reports mcp.oauth.failed
- tighten the reconciler exemption to the (conversation_id, user_id)
  composite key

Frontend — stop trusting event presence over truth:
- notificationsSlice.resolveToolApproval evicts the matching
  tool.approval.required and persists its (stable) id dismissed so the
  backlog replay stays suppressed
- ToolApprovalToast drops approval events older than the resumable TTL
  window as a backstop for a lost clearing event
- schedulesSlice handles schedule.resumed / .cancelled / .completed
- upload dismissals now outlive the SSE backlog retention window

Tests cover the new clearing events at the reconciler, slice and
dispatch layers.
2026-06-07 13:17:50 +01:00
Alex 45cae8f6b8 test(v1): cover structured-output, reasoning-leak, multimodal and stateless continuation
Raise patch coverage on the new v1 code paths:

- translator: content_to_text (text/image/string parts), extract_response_schema
  (response_format json_schema/json_object + response_schema shapes),
  _split_leaked_reasoning (single/multi/double-quoted/brace-in-value),
  translate_request structured-output + sampling + multimodal surfacing
  (normal and continuation branches), and translate_response /
  translate_stream_event reasoning-leak gating (answer + structured_answer).
- stream_processor: build_continuation_from_messages — pending-call rebuild,
  invalid/dict argument handling, last-tool-call selection, prior-message
  slice, and the no-tool-calls error.

translator patch coverage 54% -> ~100% of changed lines; stream_processor
continuation path fully covered.
2026-06-04 13:39:48 +01:00
Alex b212c5cedd test(v1): align tests with valid-chunk envelope and leak-strip signature
- test_standard_clients_can_ignore_docsgpt: docsgpt-only events now ride
  on a valid empty chat.completion.chunk (choices[0].delta == {}) so
  strict OpenAI clients accept the frame; assert the envelope plus the
  docsgpt extension instead of the absence of choices.
- split continuation persistence into stateful (conversation_id present
  -> save) and stateless (no conversation_id -> no save) cases.
- _fake_translate_stream_event mock accepts strip_reasoning_leak kwarg.
2026-06-04 13:03:04 +01:00
Alex 82426e3860 fix: cosmetic nit 2026-06-03 23:23:50 +01:00
Alex fbd37e627c fix: issues with local tool calling on non openai endpoint 2026-06-03 22:50:03 +01:00
Alex 9a69e89096 fix: assistant input content must be output_text, not input_text
Live /v1/responses rejects input_text for the assistant role
(input[N].content[0] -> "Supported values are: 'output_text' and
'refusal'"). Revert the previous review-driven change: encode assistant
turns as output_text and user/system turns as input_text. The SDK type
union suggested input_* only, but the API enforces output_text per-role —
confirmed against the live endpoint.
2026-06-03 15:00:14 +01:00
Alex 27f9ddc8e0 fix: address PR review — Responses input encoding + finish Azure removal
- Replay prior-turn assistant text as input_text, not output_text: a
  Responses easy-input message only accepts input_* content parts, so the
  old shape would 400 on the second turn of every conversation (default
  store=false resends prior turns inline).
- Always request include=["reasoning.encrypted_content"] so in-turn
  reasoning carryover works whether or not the response is also stored
  server-side (OPENAI_RESPONSES_STORE).
- Add detail="auto" to input_image parts.
- Remove the now-dead azure_openai option from setup.sh / setup.ps1 and
  the docs, completing the AzureOpenAILLM removal.
- Tests: correct the input_text / image-detail / include assertions and
  add parallel tool-call and stream-error coverage.
2026-06-03 14:56:28 +01:00
Alex 02d1458467 Merge origin/main into responses-api
Reconcile the reasoning-passthrough change (#2520) with the Responses API
work: dedupe the shared reasoning_effort capability, keep upstream's
upstream_model_id YAML plumbing and the _apply_reasoning_effort chat path,
and run the Responses branch ahead of it so responses-flavor models skip
the chat-only top-level reasoning_effort injection.
2026-06-03 14:03:15 +01:00
Alex 142a9201f4 feat: add OpenAI Responses API support via per-model api_flavor
Route opted-in models to /v1/responses while keeping Chat Completions as
the default. The flavor is selected per-model in the catalog YAML and
threaded through ModelCapabilities (with a new reasoning_effort field).

- Translate chat-shaped messages/tools/structured-output into the
  Responses input / text.format / flattened-tool shapes at the provider
  boundary; stored history and intra-turn tool round-trips need no
  migration.
- Normalize the typed SSE event stream back into the existing handler
  contract (content / thought / tool-call chunks) so the streaming
  accumulator and OpenAI handler stay unchanged.
- Carry encrypted reasoning items across the in-turn tool loop
  (provider-local, keyed by call_id) for stateless reasoning continuity.
- OPENAI_RESPONSES_STORE opt-in enables server-side state and
  previous_response_id chaining across turns, persisted in message
  metadata.
- Set gpt-5.5 to api_flavor: responses, reasoning_effort: medium.
- Remove AzureOpenAILLM and the azure_openai provider/catalog.
2026-06-03 13:43:04 +01:00
Alex ae6400a709 feat: resoning passthrough 2026-06-02 17:27:02 +02:00
Alex a5f735575f fix: backup model response parsing 2026-06-01 11:30:37 +02:00
Alex badd3830b7 feat: log gen end 2026-05-31 18:00:51 +02:00
Alex 85a2811e44 feat: log gen starts 2026-05-31 17:42:42 +02:00
Alex 7bb54e0f10 Fix: change model retry strategy 2026-05-31 16:43:40 +02:00
Alex 94bc4d1eb6 Fix remote-device tool timing out on scheduled runs (Redis-backed broker) (#2511)
* fix: route remote-device tool through Redis so scheduled runs reach the device

The remote-device tool worked interactively but timed out on every scheduled
run. DeviceBroker was an in-process, in-memory singleton, but scheduled runs
execute in the Celery worker — a different process from the gunicorn web tier
that holds the device's SSE session — so a worker-side dispatch never reached
the device and the tool always hit its deadline.

Make the broker Redis-backed so every hop crosses the process boundary:
- queued commands       -> Redis list   dev:cmd:{device_id}
- output chunks         -> Redis stream dev:out:{invocation_id}
- invocation metadata   -> Redis hash   dev:inv:{invocation_id}
- SSE upgrade tickets    -> Redis key    dev🎫{device_id}
Per-connection SSE session state stays in the web process. Reuses the existing
get_redis_instance()/CACHE_REDIS_URL; no new infrastructure. Also makes the web
tier safe to scale past one worker.

Concurrency hardening (from adversarial review + real-Redis e2e):
- XADD the output/control chunk before flipping completed=1, and have
  drain_output do a final non-blocking flush after observing completion, so a
  reader can't see completion and stop before the control chunk lands (this had
  reintroduced the false "device did not respond (timed out)" under a race).
- _collect_result builds the result from drained chunks, checks the deadline
  only after capturing a chunk, and falls back to the authoritative snapshot
  (before cleanup) when no control chunk was observed.
- Audit outcome is written from locally-known fields so it survives the worker
  racing to delete the invocation; a denied command now records a terminal
  "denied" outcome instead of staying "dispatched".
- cmd-queue TTL raised to 900s (>= max drain deadline); dispatch-failure and
  reaped-invocation cleanup; UTF-8 byte counts.

Tests: new tests/devices/{conftest (FakeRedis double), test_broker_cross_process,
test_broker_race, test_submit_output_audit}; drain/cleanup/ticket tests rewritten
for the Redis contract. The race tests fail against the pre-fix code. ruff clean;
device + tool-executor suites green.

* fix: log instead of silently passing on failed-dispatch cleanup

Addresses the code-quality lint on the best-effort hash delete in
dispatch_invocation's failure path: replace the bare `except: pass` with a
logger.debug carrying the invocation_id. No behavior change — cleanup stays
best-effort and still returns a failed Invocation.
2026-05-29 13:43:02 +01:00
Alex 418e2d989a feat: add model id on token usage (#2507) 2026-05-28 19:33:01 +01:00
Alex 8e0f7ced5f feat: remote device connection as tool (#2506)
* feat: remote device connection as tool

* fix: test

* fix: fix issues

* fix: ruff

* fix: mini issues

* fix: bugs

* fix: tests and mini issues
2026-05-28 18:23:25 +01:00
Alex b2d1d7c2c3 Deepseek (#2499)
* feat: add deepseek

* feat: deepseek reasoning tool fix
2026-05-25 23:49:25 +01:00