mirror of
https://github.com/coder/coder.git
synced 2026-09-23 22:20:22 +08:00
27ed052d86b5c8a630ccaff45ca39632da2c419c
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
cd56ab9e33 |
refactor: remove legacy live-read and injected-history chat context paths (#26585)
This PR makes the agent-pushed pinned snapshot (`chat_context_resources`) the sole source of workspace context for chats, completing the "Release 5" cleanup. It removes legacy mechanisms now superseded by the snapshot that agents push over dRPC (`PushContextState`) and refresh via `chat-context/refresh`. Removed: - **Live-read at turn time.** MCP tool discovery, skill live-body reads, and the instruction/skill history fallback that dialed the workspace on every turn. - **Context injected as message history.** The `persist_workspace_context` generation action and its decision-loop guard. - **The legacy write path.** `POST`/`DELETE /api/v2/workspaceagents/me/experimental/chat-context`, the agentsdk `AddChatContext`/`ClearChatContext` methods, and the CLI one-shot writer. - **The `chats.last_injected_context` column** and all of its plumbing (migration `000529`, queries, `db2sdk`, `dbauthz`, audit table, and the frontend `ContextUsageIndicator` fallback). Subagent context inheritance no longer copies parent context messages; children now hydrate the parent's pinned `chat_context_resources` on create, which yields an identical pin for the same workspace and agent. What stays (still served by the live agent connection, not the snapshot): `read_skill_file` supporting-file reads, `read_skill` supporting-file listing, and MCP tool execution. > [!NOTE] > Migration `000529` drops `chats.last_injected_context` and recreates the `chats_expanded` view without it. The down migration restores both. <details> <summary>Decision log (D1-D5)</summary> - **D1 (subagent inheritance):** Re-point inheritance from the legacy message copy to a pinned hydrate. Children call `hydrateChatContextOnCreate` instead of copying parent context messages. - **D2 (`persist_workspace_context`):** Remove the generation action entirely along with the decision-loop guard it existed to satisfy, since context is never injected into history anymore. - **D3 (legacy HTTP + CLI):** Remove the experimental `chat-context` POST/DELETE endpoints, the agentsdk methods, and the CLI one-shot. The dRPC push + `chat-context/refresh` replace them. - **D4 (frontend fallback):** Remove the `last_injected_context` fallback in `ContextUsageIndicator`; pinned `resources` are the sole source. - **D5 (sequencing):** Ship as a single PR rather than a stacked pair. </details> --- Coder Agents generated on behalf of @kylecarbs. |
||
|
|
1c78bd84b7 |
feat(coderd): copy agent context resources into the per-chat pin (#26438)
## What Populates `chat_context_resources` (the per-chat pinned copy added in #26430) by copying from `workspace_agent_context_resources` at the points where a chat's `context_aggregate_hash` is set, in the same transaction, so the pinned hash and pinned bodies always agree. No prompt-building change yet; consuming the pinned copy in `prepareGeneration` is a later, experiment-gated PR. ## How - `HydrateAgentChatsContext` now hydrates NULL-hash chats **and** copies the agent's resources onto them in one statement (a data-modifying CTE), so the chat-create and agent-push paths need no Go change. - New queries `InsertAgentContextResourcesIntoChat`, `DeleteChatContextResources`, `ListChatContextResources`, each with a hand-written dbauthz wrapper (per-chat update/read) and a `MethodTestSuite` entry. - `RefreshChatContext` re-pins resources via a shared `repinChatContext` helper (clear-then-copy in a transaction). A dirty chat keeps its old bodies until refresh. - On agent rebind (e.g. a workspace rebuild produces a new agent), the chat's context is re-pinned to the new agent so it stops injecting the previous agent's resources. Best-effort: a context error never fails the binding. ## Invariant A chat's `chat_context_resources` always correspond to its `context_aggregate_hash`. Bodies are (re)written only when the hash is set (hydrate, refresh, rebind); a dirty chat keeps its old bodies until refresh. ## Testing Extends the context integration test to push real resources and assert the copy across hydrate, dirty (no re-copy), and refresh. The dbauthz `MethodTestSuite` covers the three new methods. <details> <summary>Why clear-then-copy (two statements)</summary> The refresh/rebind re-pin clears the chat's rows then inserts the agent's. It uses two sequential statements inside the transaction rather than a single `WITH cleared AS (DELETE ...) INSERT ...`, because a data-modifying CTE cannot see its own delete under snapshot isolation, so overlapping sources (the common case: the same files re-pinned) would collide on the `(chat_id, source)` primary key. The hydrate path inserts into never-pinned (NULL-hash) chats and uses `ON CONFLICT DO UPDATE` defensively. </details> <details> <summary>Follow-ups</summary> - `prepareGeneration` consuming the pinned instructions and skills (experiment-gated). - `codersdk.ChatContext` resources plus changed diff, and the frontend indicator/refresh. - Removing the per-turn pull and `last_injected_context`. </details> --- *This PR was created by Coder Agents on behalf of @kylecarbs.* Builds on #26430. |
||
|
|
bca0ce04ca |
feat: integrate agent context snapshots into chats (#26389)
Makes the chat context foundation from #26385 live. That PR added the storage columns, writer queries, and a dormant `agentapi.ContextDirtyMarker` trigger with no production callers; this PR wires them together end to end. When a workspace agent pushes a context snapshot, bound chats now hydrate to that snapshot's hash, and a later push with a different hash flips already-pinned chats to dirty (emitting a `context_dirty` watch event after the transaction commits). Chat creation pins the agent's latest snapshot when one already exists. The experimental chat API exposes this as `Chat.Context` (`*ChatContext` with `dirty`, `dirty_since`, `error`), and a new `PUT /api/experimental/chats/{chat}/context` endpoint re-pins the agent's latest snapshot and clears the dirty marker. `context_dirty_resources` stays NULL (the resource-level diff is deferred to the UI phase) and the live per-turn context pull is unchanged. The end-to-end test provisions a workspace agent via the echo provisioner, connects it over the Agent API v2.10, and exercises the full path: an initial push hydrates a bound chat (clean), a second push with a different hash marks it dirty, the API reports the dirty state, and the refresh endpoint clears it. <details> <summary>Decision log</summary> - **API shape — sub-struct.** Dirty state is surfaced as `codersdk.Chat.Context *ChatContext { Dirty bool; DirtySince *time.Time; Error string }` rather than flat fields, matching the RFC's named `ChatContext` type and leaving room for future fields (resource diff, sources). `db2sdk.Chat` populates it when the chat is context-tracked (`len(ContextAggregateHash) > 0`), dirty, or carries a snapshot error, and leaves it nil (`omitempty`) otherwise. `Dirty` mirrors `context_dirty_since` being set. - **Marker wiring.** The chat daemon is injected directly as the `agentapi.ContextDirtyMarker`. It is unconditionally constructed (only its background worker is gated), so the marker is always non-nil and the wiring matches every other `api.chatDaemon` call site. `agentapi` still treats a nil marker as "chatd absent", so `PushContextState` stays a pure write path for any future caller that does not wire chatd in. - **Refresh is atomic.** `RefreshChatContext` reads the agent's latest snapshot and re-pins the chat in one repeatable-read transaction, so a concurrent push cannot land between the read and the write and leave the chat pinned to a stale hash with the dirty marker cleared. - **Hydrate + dirty run inside the push transaction.** The fan-out shares the push's transaction so a concurrent refresh cannot interleave with the version gate; `context_dirty` watch events publish only after commit. The pinned hash on dirtied chats is intentionally left unchanged — the refresh endpoint re-pins it. - **Dirtied chats keep their pinned hash.** Drift is advisory: a dirty chat stays usable, and refreshing is the only path that advances the pinned hash. - **Test binds `chats.agent_id` directly.** In production the binding is set lazily during a chat turn (`chatd.persistBuildAgentBinding`); the test sets it via `dbgen` so it exercises the context flow rather than turn resolution. Plan: `coderd/x/chatd` context integration + E2E (sub-struct API, create-time + push-time hydration, refresh endpoint; `context_dirty_resources` and the per-turn pull untouched). </details> 🤖 Generated by Coder Agents on behalf of @kylecarbs |