56 Commits
Author SHA1 Message Date
David Gageot 5ddb4409b9 feat: add GET /api/sessions?active=true for lightweight attached-session listing
Returns only runtimes currently attached to this server with working_dir and
streaming status, no session-history read.

Assisted-By: Claude
2026-09-11 19:08:41 +02:00
Sayt-0 448f1365e9 feat(safety): add restricted mode for unattended runs 2026-08-13 11:34:36 +02:00
Trung Nguyen 934784b7e3 feat: three-mode safety policy (strict / balanced / autonomous) with native shell classification
Tool approval today is a coarse two-option choice: approve a call once,
or approve everything for the rest of the session. Prompt fatigue
pushes people to the blanket option without them realising it also
covers destructive commands. There is no middle ground, and no obvious
way back. The shell classifier (safer: true) was also entangled with
the approval verdict, which made its judgement impossible to reuse.

Introduce three explicit safety modes and split the classifier into a
pure labeller so the mode is what actually gates the call:

               safe     destructive   unknown
  strict       ask      ask           ask
  balanced     ALLOW    ask           ask
  autonomous   ALLOW    ALLOW         ALLOW

Every call gets a safe / destructive / unknown label from a new
dependency-free pkg/safety package (shell commands via the pattern
taxonomy, other tools via their MCP annotations), and the pipeline
becomes: preempt hooks -> custom rules -> mode x label -> default
hooks -> ask user. Custom rules always win over the mode; session
ask rules always prompt while agent-author ask rules yield to a
user-chosen auto-approving mode.

Escalation is guided and reversible: confirmation prompts offer the
next rung, a new --safety flag and the PATCH safety-policy endpoint
set or undo the mode at any time, and downgrading genuinely revokes
the blanket approval (the yolo toggle restores the pre-escalation
mode, surviving branch and fork).

Compatibility: legacy policy values (unsafe / safer / safe-auto) and
resume verbs are accepted and normalized; safer maps to balanced,
which is deliberately narrower (unknown commands now prompt). Sessions
that never choose a mode keep the historical default. The shell
toolset's safer: flag stays parseable but is ignored; the safer_shell
builtin degrades to a metadata-only labeller and warns at config build
time. Confirmation events and the on_tool_approval_decision hook now
carry safety_label. Resuming a session stored as autonomous keeps
autonomous without re-passing --yolo; an explicit --safety on resume
overrides.
2026-07-27 10:10:48 +02:00
Trung Nguyen 3de760ded0 pkg/api: expose root-agent command names in /api/agents
Add a Commands []string field to api.Agent so clients can enumerate
the slash commands defined on the root of each loaded config.

Populated from cfg.Agents.First().Commands (already merged with any
UseCommands references by config.Load), sorted alphabetically, and
omitted from the JSON when empty for backward compatibility.
2026-07-20 17:23:04 +02:00
David Gageot e33eed4eae refactor: address review feedback on compaction cost attribution
Add chat.Usage.Add helper; validate/normalize summary attribution in
server; thread model/usage through client API via session.Item; deep-copy
usage pointer in persistence observer and snapshotItems; document
last-model-wins in runUsage.

Assisted-By: Claude
2026-07-20 13:46:59 +02:00
David Gageot eb784878ca fix: attribute compaction summary costs to the model that generated them
Adds Model+Usage to compactor.Result and session.Item (migration 25),
threads them through SessionSummaryEvent, persistence, and the server
AddSummary path so the TUI /cost By Model breakdown includes compaction
spend instead of silently dropping it.
2026-07-20 13:32:57 +02:00
David Gageot b84ebbe8b9 Merge pull request #3647 from trungutt/safer-shell-safe-auto
feat(safer_shell): add safe-auto safety policy
2026-07-15 18:06:49 +02:00
Trung Nguyen 5ce05869b6 feat(safer_shell): add safe-auto safety policy
Introduces a middle-ground session mode between 'ask for everything'
(strict) and 'auto-approve everything' (unsafe/--yolo). Under
safe-auto, safer_shell auto-approves classifier-verified safe shell
calls (docker ps, ls, git status, ...) and still prompts for
destructive and unknown commands.

The classifier now emits blast_radius=safe metadata on the safe
branch too, so embedders can render a SAFE badge even under strict
mode and offer a one-click opt-in via the new approve-safe resume
type that flips the session into safe-auto.

- pkg/session: SafetyPolicySafeAuto + SetSafetyPolicy helper
- pkg/hooks/builtins/safer_shell: emit safe verdict; auto-Allow under
  safe-auto
- pkg/runtime/toolexec: ResumeTypeApproveSafe + user_approved_safe
  approval source
- pkg/api + pkg/server: safety_policy on session responses + snapshot;
  new PATCH /api/sessions/:id/safety-policy endpoint
- pkg/tui: safe case for blast-radius badge with a green reassuring
  heading
- docs + examples: describe the four-mode matrix
2026-07-15 17:46:33 +02:00
Sayt-0 5cb5f1589c Merge remote-tracking branch 'origin/main' into fix/session-cost-regression
# Conflicts:
#	pkg/runtime/runtime.go
2026-07-15 17:31:14 +02:00
Sayt-0 cddf1b0122 fix(session): keep cost monotonic across compaction 2026-07-15 17:30:45 +02:00
Arnaud Héritier 811d91dd5f fix(runtime): reliable, correlation-safe, non-blocking elicitation delivery (#3584) 2026-07-13 16:39:20 +02:00
David Gageot fd8820fb08 feat(server): 404 snapshots of unknown sessions carry a machine-readable code
GET /snapshot of a session the server does not know now 404s with
{"code":"unknown_session"} in the body. Clients that own their session
ids (the board) can finally tell the two 404s apart: with the code, the
server is current but the session is not created/attached yet — keep
waiting; without it, the route itself is missing (a binary that predates
/snapshot) — relaunch to pick up the current binary. Previously both
looked identical and the board could only guess.

The new server tests cover heartbeats, the aggregate snapshot cost, and
this error code.
2026-07-06 21:59:28 +02:00
David Gageot 730fa4bf6f feat(server): report the session's aggregate cost in GET /snapshot
The snapshot now carries the session's total cost (TotalCost, which
includes sub-session and item-level costs such as compaction). External
consumers like the board no longer need to fetch every message and sum
per-message costs — which undercounted — to display what a session has
spent.
2026-07-06 21:59:20 +02:00
Trung Nguyen aedb2e6e14 feat(server): fork session by user-message ordinal
POST /api/sessions/:id/fork now takes a 0-based ordinal counting only
user-role messages instead of an index into the flat all-roles list.
Clients only have to know which of the user's turns they clicked on;
the server translates that into the underlying item index.

This decouples the contract from the precise layout of tool responses
and assistant turns, which a streaming client cannot fully observe
until persistence completes.
2026-06-26 10:21:43 +02:00
Trung Nguyen c398efdc78 feat(server): expose session forking over HTTP
Add POST /api/sessions/:id/fork that deep-copies a session up to (but
excluding) a chosen user message, returning a new session with a
fork-numbered title. The fork point must be a user message; assistant
turns, tool responses, and sub-session items are rejected with 400.

session.ForkSession wraps the existing BranchSession with the fork
title generator so the TUI's branch-from-edit flow keeps its own
(branched) / (branch N) titling scheme.
2026-06-22 17:45:04 +02:00
David Gageot 058f1523e8 feat(server): idempotency keys for follow-ups so retries are safe
A client whose POST /followup times out can't tell whether the
follow-up landed. Retrying risked delivering the prompt twice; not
retrying risked losing it.

Accept an optional Idempotency-Key header on /followup. A repeat with a
key already seen for the session is acknowledged (status "duplicate",
202) without delivering the message again, so retries are safe. The
reservation is per-session, bounded (recent keys only), made atomically
to settle concurrent retries to a single delivery, and rolled back if
delivery fails so a genuine failure stays retryable.
2026-06-15 11:01:08 +02:00
David Gageot 23f2c9163b feat(server): add GET /sessions/:id/snapshot for gapless resync
A client rebuilding a session's view previously had to stitch together
/status, /sessions/:id and replayed events, with no way to know the
exact event-stream position the assembled state corresponds to \u2014 so a
transition occurring between the reads could be missed.

Add a single snapshot endpoint returning the session's stored state
(messages, tokens, permissions), its live runtime state (streaming,
current agent), and last_event_seq: the sequence number of the most
recent event on its /events stream. The resync pattern is then exact and
gapless: read the snapshot, connect to /events?since=<last_event_seq>,
and any event in between is replayed from the buffer.
2026-06-15 11:01:08 +02:00
docker-agent c85e973680 refactor: fold model override into runAgent request body
Replace the dedicated PATCH/POST /api/sessions/:id/model endpoints with a model field on the runAgent request. The override is applied before user messages are persisted, preventing orphaned messages on validation failure. RemoteRuntime queues overrides internally and forwards them on the next turn.

- Remove SetSessionModelRequest/Response types
- Add RunAgentRequest with messages and optional model field
- Update Client.RunAgent() to accept model parameter
- Remove Client.SetAgentModel()
- Update RemoteRuntime to queue pending overrides
- Fold model logic into SessionManager.RunSession()
- Remove dedicated /model endpoints and handler
- Update API documentation and tests
2026-05-21 13:05:48 +02:00
David Gageot e324eadf8d feat: add GET/PATCH/POST endpoints for runtime model switching
Add GET /api/sessions/:id/models to list available models and the
current override (if any), and PATCH/POST /api/sessions/:id/model to
apply a model override for the session's current agent.

Extend ModelChoice with JSON tags for wire format and add
SessionModelsResponse type. Both endpoints return 422 if the runtime
doesn't support model switching.
2026-05-18 10:24:48 +02:00
David Gageot 0257939e4f feat: add GET /api/sessions/:id/status endpoint
Add a lightweight status endpoint that returns a snapshot of the
session's current runtime state: title, streaming flag, active agent,
token counts, and message count.

This lets late-joining SSE consumers (boards, dashboards) immediately
know the session's state without waiting for the next event transition.
The streaming flag is probed via the existing per-session mutex so no
additional bookkeeping is needed.
2026-05-11 20:31:47 +02:00
David Gageot fd856d70fe feat: queue depth endpoint and 429 backpressure for full queues 2026-05-11 17:12:57 +02:00
David Gageot 0885664fdb feat: batch session delete and export operations 2026-05-11 17:12:57 +02:00
David Gageot 46ae4f28e7 feat: health and readiness endpoints - add /health and /ready with store connectivity 2026-05-11 17:12:57 +02:00
David Gageot 0df94f17ab feat: session metadata updates - add tokens and starred endpoints 2026-05-11 17:12:57 +02:00
David Gageot 7483143e0c feat: session store write operations - add message and summary endpoints 2026-05-11 17:12:57 +02:00
Trung Nguyen 2bb79534ee Add mid-turn message steering for running agent sessions
Addresses #2223.

Allow API clients to inject user messages into an active agent session
without waiting for the current turn to finish. This is a common pattern
in agentic coding tools where the user can steer or provide follow-up
context while the agent is executing tool calls.

New API endpoint: POST /sessions/:id/steer

Runtime changes:
- SteeredMessage type + buffered channel on LocalRuntime
- Steer() enqueues, DrainSteeredMessages() batch-drains
- Agent loop injects steered messages after tool execution and before
  the stop-condition check; emits user_message events so clients know
  when the LLM actually picks them up
- Messages wrapped in <system-reminder> tags for clear LLM attribution

Server changes:
- POST /sessions/:id/steer endpoint (202 Accepted)
- SteerSession() on SessionManager with GetLocalRuntime() helper for
  PersistentRuntime unwrapping
- Concurrent stream guard on RunSession (rejects if already streaming)
- Proper defer ordering: streaming flag cleared before channel close

No behavioral change to the TUI — the existing client-side message
queue continues to work as before. The TUI can adopt mid-turn steering
in a future change by calling LocalRuntime.Steer() directly.
2026-04-09 17:11:47 +02:00
David Gageot bc7f5a52cf Remove /think command and session-level thinking toggle
Thinking is now solely driven by the model's thinking_budget config.
If a model has thinking_budget set or requires thinking (e.g. o-series),
it uses thinking. There is no longer a per-session toggle.

Removed:
- /think slash command from TUI
- Session.Thinking field and WithThinking option
- options.WithThinking model option and applyOverrides
- Thinking toggle from sidebar, server API, session state
- ThinkingConfigured from agent and teamloader
- All related handlers, messages, and tests

Assisted-By: docker-agent
2026-03-19 17:41:23 +01:00
David Gageot cb001a241b Remove package to github.com/docker/docker-agent
Signed-off-by: David Gageot <david.gageot@docker.com>
2026-03-09 12:04:59 +01:00
Stanislav Hamara 2b45001ebe Add always allow this tool 2026-02-02 16:26:58 +00:00
krissetto 67ed3cb66f Editable session titles
Signed-off-by: krissetto <chrisjpetito@gmail.com>
2026-01-30 13:55:11 +01:00
Christopher Petito 76e45f933d allow users to give a reason for rejecting a tool call
includes a new multi-select dialog compoent, used for this feature but reusable elsewhere

Signed-off-by: Christopher Petito <chrisjpetito@gmail.com>
2026-01-20 14:56:14 +01:00
Christopher Petito 0f2332b1ad quick toggling of thinking, session wide
can be set from the api as well, just like "yolo"

Signed-off-by: Christopher Petito <chrisjpetito@gmail.com>
2026-01-17 19:07:23 +01:00
Stanislav Hamara e7339992e3 No need for the extra type here either 2026-01-07 12:31:46 +00:00
Stanislav Hamara 0aaea99243 WIP session level permissions 2026-01-07 11:17:38 +00:00
Trung Nguyen f4d9bc2637 Remove pagination from getSession API
Pagination is not needed for now. The getSession endpoint now returns
all messages directly without pagination support.
2025-12-04 16:24:55 +01:00
David Gageot 02b53da399 Fix cost calculation
Signed-off-by: David Gageot <david.gageot@docker.com>
2025-11-28 10:03:54 +01:00
David Gageot b7cb66e488 Idiomatic Go
Signed-off-by: David Gageot <david.gageot@docker.com>
2025-11-27 13:18:01 +01:00
David Gageot 3f5d48f228 Remove dead code
Signed-off-by: David Gageot <david.gageot@docker.com>
2025-11-27 10:19:24 +01:00
David Gageot 99e9a1aebf Make it easier to bump the schema version
Signed-off-by: David Gageot <david.gageot@docker.com>
2025-11-22 10:06:45 +01:00
David Gageot 8847c0ece8 Bump agent.yaml version to 3
Signed-off-by: David Gageot <david.gageot@docker.com>
2025-11-21 15:18:03 +01:00
Djordje Lukic be219c2ab5 Add unmanaged mode for oauth flow
There are now two modes in the oauth flow

- Managed mode

In this mode, cagent handles, in process, everything oauth related:
asking for user consent, starting the oauth callback server, resuming
once the flow is done

- Unmanged mode

This mode is more for the remote case in cagent, in this mode the remote
cagent will _only_ ask for user consent via an elicitation, up to the
client to do the oauth flow and send the token back in the elicitation
response.

Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
2025-11-17 12:01:25 +01:00
David Gageot 20cb811892 Remove useless comments
Signed-off-by: David Gageot <david.gageot@docker.com>
2025-11-06 19:59:37 +01:00
David Gageot db228137de Merge pull request #561 from stanislavHamara/pagination-for-session-messages
Pagination for session messages
2025-10-25 11:09:23 +02:00
Stanislav Hamara 8bf2236bf2 Support attaching images via API 2025-10-22 13:26:00 +01:00
Stanislav Hamara f5add04d4c Lint 2025-10-21 10:05:36 +01:00
Stanislav Hamara e4b7c71f3b Update test messages to use indexes 2025-10-21 09:39:37 +01:00
Stanislav Hamara 7a629c5a51 Remove hasMore 2025-10-21 09:34:27 +01:00
Stanislav Hamara fdbefb8920 remove after 2025-10-21 09:29:20 +01:00
Stanislav Hamara 9ef0cc40ed Simplify cursor logic 2025-10-21 09:14:07 +01:00
Stanislav Hamara ae3ad0e0cb lint the test file 2025-10-20 14:50:34 +01:00