mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-01 23:43:16 +01:00
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent Chat gives each person a persistent conversation with each
agent.
> - Tasks and routines produce results outside that conversation.
> - Those runs need a safe way to bring a requested result back to the
person.
> - The server must select the agent's own chat and check the source
task's access rules.
> - This pull request adds proactive chat messages and unread
indicators.
> - People can see the result, open its source task, and reply in the
same chat.
## Linked Issues or Issue Description
**Subsystem affected**
Server, native runner tools, shared contracts, database, and UI.
**Problem or motivation**
An agent can report progress on a task or comment on an authorized task.
It cannot send a routine result to its own standing chat with the
responsible person. A morning review routine therefore cannot leave a
visible digest in that person's chat.
**Proposed solution**
Add `send_chat_message({body, idempotencyKey})` and `POST
/api/agents/me/chat/messages`. The server selects the company, agent,
and responsible person from the active run. It saves an attributed
comment and audit record in one transaction. The chat shows a
source-task link and per-person unread state.
**Alternatives considered**
A destination parameter on a generic comment tool would expose
unnecessary routing authority. A new delivery worker would add another
durable delivery path. This change uses a dedicated tool and the
existing comment and live-event paths.
**Roadmap alignment**
This extends the experimental Agent Chat capability in `ROADMAP.md`. The
existing `enableAgentChat` setting controls it. Related public work:
Refs #13282, Refs #13283, and Refs #14408. Searches for proactive chat
and unread chat found no duplicate PR.
## What Changed
- Add a focused messaging service shared by the native tool and HTTP
route. It checks current run ownership, execution mode, recipient
membership, task access, and task privacy.
- Reuse the canonical company/agent/person conversation. Save the
comment and audit record together. Keep task completion and chat
execution separate from delivery.
- Add source-task provenance and source-scoped retry keys. Identical
retries return the saved receipt. Changed content returns a conflict.
- Enforce the conversation's fixed agent at the shared comment-write
boundary. Keep normal reply cancellation and session checks.
- Add chat unread state, accessible agent dots, aggregate Chat
indicators, and visible-message read cutoffs. Refresh through existing
live events.
- Add the request-key constraint, native tool contracts, documentation,
capability maps, stories, and regression coverage.
## Verification
- Local delivery/security proof: all 38 proactive-message cases pass.
Eight credential-scope regressions and four quota regressions fail
against the prior source. The repaired delivery and canonical
cross-issue cap suites pass all 49 cases; all 31 OpenAPI tests, server
typecheck, and capability-map checks pass. The shared per-run cap covers
native and HTTP sends, mixed ordinary mutations, concurrent last-slot
contention, and retry/recovery without double charging.
- Credential tests preserve the authenticated person and authorizer;
reject bridge/test credentials and persisted skill-test runs after
task-mode changes; and recheck API-key revocation, scope, and
responsible person after destination locking. The earlier
delivery/native-authority suites passed all 74 cases before the four
quota additions.
- Four PostgreSQL contention cases cover native and HTTP sends plus
retries during a native chat mutation. All four deadlock against the
earlier source and pass with destination-before-source/run locking.
Recovery and human-reply delivery enqueue only the human comment; a
proactive digest never wakes the chat agent.
- Passed 108 companion conversation, native-authority, and OpenAPI
tests; 112 runner catalog/workflow-report tests; 47 completion-delivery
tests; and 25 project-handoff tests. Focused runner tests cover native
tool availability without generic API tools.
- All 327 affected UI tests, UI typecheck, token gates, and catalog
checks pass. The read hook retries at most three times, respects server
retry delay, coalesces only seen cutoffs, recovers on reconnect, and
cancels on scope changes.
- Real browser journeys with a deterministic process agent verify
routine-to-chat delivery, first-chat creation, unread state across
reload, the source link, secondary chat navigation, and a follow-up
reply referring to the digest. This does not qualify live GitHub
retrieval or model output quality.
- The Storybook showcase uses production components with deterministic
network fixtures. Rendered interactions pass for the landing, ordinary
read success, transient read recovery, and persistent failure after
bounded retries. Both Chat navigation variants update from two unread
chats to one while only Kodi's dot clears. Mobile and light variants
were reviewed. The existing Coordinator Recents label contrast finding
is unchanged; no Linux visual baseline archive exists.
- Current master's independent sandbox migration retains 0418.
Regenerated the feature migration as 0419 from the combined schema and
retained exactly the newest five snapshots; numbering/safety, replay,
and schema-drift checks pass.
- Workspace typecheck and build passed before PR preparation; canonical
server/UI typechecks passed again on the combined tree. Migration
`0419_slippery_impossible_man.sql` passes safety and schema drift checks
on disposable PostgreSQL, including interrupted index repair, concurrent
migrators, one-time owner read-state backfill, preserved receipts, and
two SQL replays. A default five-second local snapshot comparison timed
out; a diagnostic 30-second run passed without drift.
- Earlier failures are retained in the record: full local `pnpm
test:run` and broad shards encountered embedded-PostgreSQL startup,
timing, and provider-recovery failures. Thirteen selected
external-channel cases pass in isolation on the original and changed
sources; an unordered comment-row assertion now explicitly orders both
reads. The earlier route registry, native tool count, runner catalog,
and accessible Chat badge-name failures were fixed and passed full
remote CI on `920b3bb7db`.
- Repeated managed-skill server8 failures came from CI's newer synthetic
merge: its Cloud guard correctly rejected the old local-disk fixture.
Master independently repaired the fixture in #18385; this branch keeps
that repair unchanged. All 57 managed-skill and Cloud prerequisite cases
pass on the combined tree, and that fixture has no remaining PR diff.
- Current reviewed head `dc16f314739a6d977bed21cb1446d0e80a541578`:
Greptile 5/5 with no findings, Superagent security assessment passed,
advisory Paperclip Review 5/5 with no findings, and zero open review
threads. All 55 current-head check runs pass, with five expected skips
and the advisory review neutral at 5/5; Snyk also passes. No checks are
pending or failing. Protected squash auto-merge is enabled and is
waiting for required code-owner approval. A normal exact-head merge was
refused by the base-branch policy; no admin bypass was used. The
previous head's Superagent quota finding is fixed and covered above.
## Risks
- Chat remains company-visible. Delivery rejects a source task with
private-task, project, or ancestor restrictions.
- Read state uses a comment timestamp cutoff. The server retains
database precision and never moves a read receipt backward.
- The new unique constraint changes comment query planning. An existing
regression now uses an explicit row order. The migration builds the
unique index concurrently, then takes a brief table lock to attach it.
It uses the existing top-level migration and interrupted-index recovery
path.
- The upgrade backfills missing chat-owner read-state rows at the latest
existing agent comment. Existing read receipts remain intact. New chats
can still be unread before the human participates.
- Quota observation commits atomically with delivery. A rejected send
rolls back its attempted denial audit row and any first-chat creation;
the canonical warning remains in server logs, and previously spent quota
remains enforced. Identical/conflicting retries and receipt recovery
spend no extra slot.
- This version delivers in-app only. It adds no external-channel, push,
or email delivery.
- Local full-suite acceptance remains limited by the failures recorded
above. The feature stays behind `enableAgentChat`.
## Model Used
OpenAI Codex. PR preparation and CI work used `gpt-6.1-sol` with tool
use and code execution. The earlier implementation also used Codex, but
its exact model ID was not recorded in the visible session. The
context-window size was not provided.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run focused tests locally and they pass; the broad local
failures are recorded above
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
160 lines
13 KiB
Markdown
160 lines
13 KiB
Markdown
---
|
||
navigation: {"section":"Tasks and conversations","sectionOrder":20,"order":40}
|
||
---
|
||
|
||
# Agent conversations and project handoff
|
||
|
||
A person can hold a persistent conversation with an agent, return to its history, and hand useful work into a project or task. The gated conference-room chat is a separate conversation surface.
|
||
|
||
Feature ID: `agent-chat`.
|
||
|
||
Implementation: [agent chat](../../ui/src/pages/AgentChat.tsx), [chat landing](../../ui/src/pages/AgentChats.tsx), [conference room](../../ui/src/pages/BoardChat.tsx).
|
||
|
||
Agent access: [companion](../agent-access-map/agent-chat.md).
|
||
|
||
## Sub-features
|
||
|
||
- `discovery`: `agent-chat/discovery` — find an agent and reopen an accessible recent conversation.
|
||
- `history`: `agent-chat/history` — send messages, inspect replies/files, and return to the same conversation.
|
||
- `handoff`: `agent-chat/handoff` — create or identify a project/task from chat and follow the resulting work.
|
||
- `conference-room`: `agent-chat/conference-room` — use the separately gated board chat without assuming parity with agent chat.
|
||
|
||
## How to get to it (user POV)
|
||
|
||
### `agent-conversation`
|
||
|
||
Task and routine results can arrive proactively in the agent's standing chat.
|
||
Each message has a source-task link; the agent row and Chat navigation indicate
|
||
unread messages, including first delivery before any human chat. Foreground
|
||
messages at the bottom of the chat are marked read with a comment cutoff.
|
||
The existing ordering and per-human/company isolation remain in effect.
|
||
|
||
Open `/chats`, choose an agent, or use `/chats/:agentRef` and its conversation sidebar.
|
||
|
||
With Coordinator enabled, the composer exposes the shared model/thinking-level picker for supported agents. The agent stays fixed in this control; switching agents remains in the chat header. Choices save as conversation overrides on send, preserving the agent's configuration. A first message creates the conversation before saving settings and submitting through the ordered comments endpoint.
|
||
|
||
Standing chats open with the latest 20 contiguous messages. Scroll upward to fetch earlier messages, answer receipts, and created tasks. The loading indicator stays in the thread; history failures offer an inline Retry while preserving the draft and reading position. Opening a chat does not fetch its lifetime comment, activity, or run history. Already observed messages survive live refresh and Back within the query-cache session.
|
||
|
||
Coordinator answer receipts from this conversation jump to the answer bubble without leaving chat. Answer updates from an execution task link to that source task and its decision context. Compact document links read “Open [document title] in the sidebar”.
|
||
|
||
### `project-handoff`
|
||
|
||
Ask for project work in Agent Chat and follow its created project/task links. New tasks and accepted task suggestions default to the agent in that conversation. An explicitly requested agent or person takes precedence; no best-fit agent routing is inferred. Ordinary task creation outside chat keeps its existing assignment behavior.
|
||
|
||
### `conference-room`
|
||
|
||
Open `/board-chat` only when its conference-room gate is enabled.
|
||
|
||
## Driving it with Playwright and the Paperclip CLI
|
||
|
||
Preconditions: follow the [baseline](./README.md#before-driving-a-journey). Enable the applicable chat feature, use a configured test agent, and distinguish model replies from a mocked conversation fixture.
|
||
|
||
### `agent-conversation`
|
||
|
||
Local browser recipe: [long-chat startup and scrolling](../../tests/perf/agent-chat/agent-chat-startup.spec.ts), with fixture and command details in the [performance README](../../tests/perf/agent-chat/README.md). It covers recent-only reveal, delayed and failed pages, Back, older comment links, and live refresh. Synthetic local data is not deployed acceptance evidence.
|
||
|
||
Automated: [chat landing](../../ui/src/pages/AgentChats.test.tsx) checks recent-chat routing; [conversation service](../../server/src/__tests__/agent-conversations.test.ts) covers persisted conversation behavior.
|
||
|
||
Automated: [proactive delivery](../../server/src/__tests__/agent-chat-messages.test.ts)
|
||
exercises native/HTTP delivery, concurrency, retries, authority, privacy, active
|
||
turns, resets and unread cutoffs. [Read visibility](../../ui/src/hooks/useAgentChatRead.test.tsx)
|
||
checks foreground/bottom gating and unseen arrivals. The [routine digest journey](../../tests/e2e/agent-chat-messages.spec.ts)
|
||
uses real app APIs and a deterministic process agent to verify first-chat discovery,
|
||
unread state across reloads, source navigation and follow-up context. It does not
|
||
qualify live PR retrieval or a model's editorial choices.
|
||
|
||
Automated: [composer picker](../../ui/src/components/task-chat/ComposerRunSettingsPicker.test.tsx) covers fixed-agent model/effort selection on desktop and mobile; [composer](../../ui/src/components/task-chat/TaskChatComposer.test.tsx) covers Coordinator gating and settings submission without reassignment. [Task detail](../../ui/src/pages/IssueDetail.test.tsx) verifies canonical first/existing-chat IDs, settings-before-comment ordering, and no comment submission when settings fail to save. These tests mock the API and do not prove provider execution with the chosen model.
|
||
|
||
Automated: [conversation delivery](../../server/src/__tests__/agent-conversation-delivery.test.ts)
|
||
checks that paused or otherwise non-invokable agents leave comments pending
|
||
without aborting startup recovery, and that a later unpause allows one delivery.
|
||
The test persists queue admissions only; it does not start agents or providers.
|
||
Automated: [historical chat](../../ui/src/pages/AgentChat.test.tsx) keeps an older
|
||
conversation reachable after a cached roster refresh ends in HTTP 429. It checks
|
||
lookup Retry and preserves permission and company boundaries. This is component
|
||
evidence with real query state, not live provider proof.
|
||
|
||
Automated: [thread and composer](../../ui/src/components/TaskChatThread.test.tsx)
|
||
keeps an in-flight attachment through first-chat creation on desktop and mobile,
|
||
while initial history for an existing conversation stays deferred. The
|
||
[first-upload browser journey](../../tests/e2e/agent-chat-projects.spec.ts)
|
||
checks the visible file chip, server attachment, no premature agent run, and
|
||
attachment count after send and reload. That journey uses a simulated agent.
|
||
|
||
Manual: In a conversation with no messages, attach a file before the first
|
||
send. Confirm the same file stays in the composer after the chat is created.
|
||
Send it once, reload, and confirm the file and message remain.
|
||
|
||
Manual: Start a conversation, send a recognizable request, and inspect its reply.
|
||
Navigate away and reopen the same history. Use `/new` for a fresh session within
|
||
the same standing conversation. Test an unavailable historical agent and an
|
||
inaccessible saved conversation; verify no cross-user or cross-company history appears.
|
||
|
||
### `project-handoff`
|
||
|
||
Automated: [chat project tools](../../server/src/__tests__/chat-project-tools.test.ts) checks tool contracts; live model choice and UI handoff require a manual run.
|
||
|
||
Automated: [Coordinator handoffs](../../server/src/__tests__/coordinator-handoff.test.ts)
|
||
checks that a suggested task remains a proposal until acceptance, that its task,
|
||
initial plan and source-chat audience are saved atomically, and that retries do
|
||
not create another task. A conversation is a privacy/provenance source rather
|
||
than a structural task parent. The same tests cover compact canonical decision
|
||
receipts, exact uploaded-artifact bindings, current task access, reset-session
|
||
boundaries, and pagination of older handoffs.
|
||
Explicit live evals: [chat feature journeys](../../tests/runner-e2e/CHAT-FEATURES.md)
|
||
send natural requests to save a backlog task or delegate an announcement. They
|
||
check project/assignee, execution or non-execution, saved output, return to chat,
|
||
and click the actual same-instance task link after reload. Registration alone
|
||
does not establish a pass. Production chat guidance uses company-prefixed relative
|
||
task links unless the platform supplied a verified absolute origin.
|
||
|
||
Manual: Ask the configured agent to create bounded project work, inspect the proposed/created target, and open the resulting task. Verify the request context and intended project survived and the task produces a useful output.
|
||
|
||
Manual: With the personal Coordinator workspace enabled, discuss a vague request
|
||
until the agent suggests a task. Confirm that no execution task exists before
|
||
acceptance. Accept it, stay in chat, and open the created card on demand. An
|
||
explicit request to create a task may create it directly under normal authority.
|
||
Approve a work product or answer a question in its task or Needs You, then return
|
||
to the originating chat. Its compact update should link to the exact recorded
|
||
decision and reviewed artifact, without needing another coordinator turn.
|
||
Approval of artifact quality alone must not publish it or complete the task.
|
||
|
||
Coordinator task clarification: create a task with only a description. When material context is missing, native task guidance asks one focused canonical question inside that task. Its actionable form is also projected into Needs You and the originating standing chat; direct user-created tasks appear in the assigned agent’s standing chat. Answer on either surface and verify the pending card/count clear and a compact recorded answer replaces it in chat. This projection is event-refreshed and does not wake another agent merely to repeat the question. Current direct-task questions survive a chat reset; their answered receipts start at the current chat boundary. The task brief remains editable context, not another stored question. Model choice and actual resumed execution still require live verification.
|
||
|
||
### `conference-room`
|
||
|
||
Automated: [board chat gate](../../server/src/__tests__/board-chat-route-feature-flag.test.ts) proves route gating, not a live conversation.
|
||
|
||
Preconditions for this entry point: `local_trusted` mode and an installed,
|
||
authenticated `claude` CLI, in addition to the Conference Room flag. These are
|
||
separate from Agent Chat's configured-agent prerequisites.
|
||
|
||
Manual: Check disabled access first, enable in the disposable instance, then send and reload a conversation. Inspect the actual responding agent and persisted history; report this result separately from Agent Chat.
|
||
|
||
## Gotchas
|
||
|
||
- Coordinator Markdown references to a standing agent conversation display “Chat with [agent]” and route to that chat, preserving anchors. Labels resolve once from the existing current-person/current-company chat list and agent roster; no task status glyph or task quicklook is shown for these references. Ordinary task links retain their existing treatment.
|
||
|
||
- Idle chat is not failed execution.
|
||
- See [steering](./steering.md) for in-flight messages and [questions](./questions-and-approvals.md) for interactions.
|
||
- An API tool test does not prove that every harness will choose the same handoff tool.
|
||
|
||
### Storybook verification
|
||
|
||
#### `conference-room`
|
||
|
||
Capability ID: `agent-chat/conference-room`.
|
||
|
||
Scope: ui
|
||
Storybook: Conference-room flag disabled, empty/history/reply, busy and explicit failure states.
|
||
Production: Exercise separate BoardChat route gate, local-trusted deployment and authenticated Claude CLI; send/reload real conversation with responding agent attribution.
|
||
Sources: Source: [BoardChat.tsx](../../ui/src/pages/BoardChat.tsx), [board-chat.ts](../../server/src/routes/board-chat.ts). Tests: [BoardChat.test.tsx](../../ui/src/pages/BoardChat.test.tsx), [board-chat-route-feature-flag.test.ts](../../server/src/__tests__/board-chat-route-feature-flag.test.ts)
|
||
|
||
### Explicit output quality review (Coordinator functional slice)
|
||
|
||
Accepted suggestions and native explicit task creation may carry `requiredQualityReview` into the task's existing execution policy. The opt-in requirement names output slots and is separate from the existing reviewer eligibility policy. Each slot requires a canonical human verdict bound to an exact current output document revision or attachment identity. Plans, questions, file presence, model completion claims, rejected/deferred verdicts and stale versions do not satisfy it. Only a human may change an existing requirement. Quality approval does not publish or automatically complete the task. Tasks without this optional requirement retain existing completion behavior.
|
||
|
||
Owning service: `server/src/services/required-quality-review.ts`; enforcement: interaction creation, issue update, native completion feedback/finalization. Focused proof: `server/src/__tests__/required-quality-review.test.ts` plus native arbiter and chat completion delivery tests. Completion updates include bounded canonical output-decision identities for linking existing review, never worker-authored review prose. These tests do not establish unattended live provider reliability.
|
||
|
||
Attachment approval is for the exact immutable file identity. Removal or a mismatched hash invalidates that target; registering a replacement beside the approved file does not redefine its output slot. A replacement requires a fresh bound review. Document targets additionally track the current revision.
|