Commit Graph
4 Commits
Author SHA1 Message Date
DottaandPaperclip 2e340d6235 fix(ui): show unread message counts in agent chat pickers (#19627)
## Thinking Path

> - Paperclip helps people manage AI agents and their work.
> - People switch between agent chats to read replies.
> - The agent picker does not show how many messages are unread.
> - People need that count before they select a chat.
> - The chat list already has the viewer's read state.
> - This pull request adds numeric badges from that shared list.
> - People can find unread replies without extra requests for each
agent.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The chat header agent picker and the secondary sidebar's Add chat
picker.

**Subsystem affected**

Agent Chat UI, shared task types, and the chat read-state service.

**Current behavior**

The pickers do not show the unread message count for each agent.

**Proposed behavior**

Show the unread count beside each agent. For example, show 3 for three
unread agent messages. Hide the badge when the count is zero. Keep
switching available while chat data loads.

**Reason and benefit**

People can see which chats need attention before they switch. The count
uses the existing shared request and cache.

**Breaking changes**

The chat response gains an optional `unreadMessageCount` field. Existing
fields and read receipts keep their behavior. There is no database
migration.

**Additional context**

Related: #19425 changes chat structure. This change adds counts to the
current agent pickers. The duplicate search found no PR for these count
badges.

## What Changed

- Count unread agent comments in the existing grouped read-state query.
Keep company and viewer scope.
- Exclude human messages, deleted comments, system notices, and
fast-response receipts.
- Pass memoized count maps to both agent pickers. Use a direct lookup
for each row.
- Show numeric badges and accessible singular or plural labels.
- Test count changes, account isolation, cached opens, search, and large
agent lists.
- Update the chat stories, fixture data, and development guide.
- Make the Slack owner retry-deadline test deterministic after
reproducing its CI failure.

## Verification

- On current head `05f5fa6201`, the [main CI
workflow](https://github.com/paperclipai/paperclip/actions/runs/42305845930)
passed, including build, typecheck, all applicable test shards, browser
shards, and final verification. The prior server shard 2, 6, and 7
failures do not recur.
- [Greptile's current-head
review](https://github.com/paperclipai/paperclip/pull/19627#issuecomment-6696161631)
is 5/5, its check succeeded, and both previous review threads are
resolved. Current head check inventory is complete: 55 successful
checks, three intentional skips, and no pending or failed checks. The
Paperclip application review summary reports paused; no new completed
Paperclip review is claimed. Its existing findings are resolved, and the
independent current-head Greptile review is 5/5.

- Current head `05f5fa6201` merges master `8621bcf53e` to resolve an
import conflict. Master now includes the same model-default test repair.
This branch keeps its upstream import, so that file no longer differs
from master.
- After this merge: all 21 server tests in the Slack owner and
model-default suites pass, all 22 tests in the three picker/sidebar
suites pass, and token gates pass. The standard preflight still cannot
build Rust because `cargo` is absent. Fresh CI must supply the broad
checks.

- Head `7d125fb8c7` replaces the Slack owner test's real timer wait with
the worker's existing clock input. It checks that the intent stays
pending one millisecond before the retry deadline and clears at the
deadline. All 15 Slack owner integration tests pass locally. Runtime
retry behavior is unchanged.
- On `4cfb1e9d55`, both review checks gave 5/5. CI [server shard
2](https://github.com/paperclipai/paperclip/actions/runs/42302458830/job/128183671349)
failed the Slack credential-lease retry test. The same assertion failed
in an unchanged local run. A diagnostic run with added logging passed;
the exact timer deviation was not captured. The new test removes the
real-time wait assumption and checks the clock boundary directly. The
earlier model-default and cleanup failures did not recur in that CI run.

- On `a3378dfc40`, both Greptile and Paperclip Review gave 5/5 with no
open findings. Typecheck, build, all eight browser shards, and most test
shards passed.
- That CI attempt failed two server shards. The model-default test still
expected `gpt-5.6-sol`, while the current default is `gpt-6.1-sol`. I
reproduced both failures locally. Commit `4cfb1e9d55` uses the exported
default-model constant; all six tests in that suite now pass. Production
model selection does not change.
- The retained-cleanup backlog test exceeded its 15-second limit in
[server shard
6](https://github.com/paperclipai/paperclip/actions/runs/42299858777/job/128175193320).
The unchanged test passes locally with the same limit. Its CI timeout
cause is not established. No assertion or timeout was changed. The fresh
CI run must recheck it.

- Review repair: commit `0db631b215` keeps the existing-chat and
unread-agent arrays stable when chat data and the active agent do not
change. A new 500-agent test fails before the repair and passes after
it. The test also checks count refreshes.
- The first CI typecheck failed on duplicate `IMAGE_ONLY` and
`TESTOPS_REQUESTED` keys in `tests/runner-e2e/workflow-target.test.ts`.
This was present in upstream code. Master commit `e482f0f632` removes
the duplicate keys. This branch now includes that commit. All nine
workflow-target tests pass locally.
- After the latest master merge: all 22 tests in the three affected
picker/sidebar suites pass. Before that merge, all 38 focused UI tests
passed.
- The current head is `05f5fa620134ad199a3d85b66fb2eb93d3bb6077`. All
current status checks are complete, the aggregate verify gate succeeds,
mergeability is CLEAN, and no unresolved review threads remain. The
maintainer has instructed normal merge after checks pass.

- After merging current master: 37 focused UI tests pass across
AgentChatPicker, CoordinatorAgentSwitcher, AgentConversationsSidebar,
useAgentChatRead, and SidebarAgentChats.
- After merging current master: all 38 proactive-chat integration tests
pass with a real disposable PostgreSQL database. They check counts of 2,
1, and 0 at exact read cutoffs.
- The prior revision passed UI typecheck and build, shared build,
isolated low-memory server typecheck, token gates, and 30 map/catalog
tests.
- The 500-agent test bounds index work and count lookups. Search does
not rebuild the existing-chat index.
- Browser checks used production components with simulated API data.
Dark, light, mobile, and Add chat cases passed. Counts of 3 and 12
stayed visible through search. Read agents had no badge. Cached opens
made no chat API requests.
- Twenty local cached opens took 34.5–57.6 ms, with a 38.4 ms median.
This is an unthrottled fixture measurement.
- Earlier full local checks could not complete because Rust cargo is
absent and the workspace has limited memory. The standard worktree
preflight also stops at missing cargo. Required CI must supply broad
checks for this PR head.
- The first server typecheck ran out of memory while Storybook was
active. Its isolated low-memory retry passed. No visual baseline
comparison or live provider delivery is claimed.

## Risks

- Low risk. The count replaces a boolean aggregate in the same grouped
query. Very long chat histories still need that existing scan.
- A count can be stale until the existing chat-list cache refreshes.
Read receipts still control the unread cutoff.
- The change adds no polling, per-agent history request, database
migration, or permission change.

## Model Used

OpenAI GPT-6 through Codex, with code editing, terminal tools, and
browser verification. The runtime did not expose a more specific served
model ID or context-window size.

## 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 tests locally and they pass
- [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>
2026-11-06 17:10:56 -06:00
f2593b7905 feat: make Coordinator and Agent Chat standard and speed up navigation (#19360)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - People enter that work through the workspace, agent conversations,
and tasks.
> - Coordinator and Agent Chat still require experimental enrollment.
> - Task link prefetches do not warm the compact queries used by the
default task view. Chat also waits for a request when its navigation
list already holds the conversation.
> - This pull request makes Coordinator and Agent Chat standard and
reuses the reads needed for navigation.
> - People can choose Classic workspace in Experimental settings. Cached
work can appear while fresh data loads.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Workspace selection, Agent Chat availability, and navigation between
Chat, Tasks, and task details.

**Subsystem affected**

Cross-cutting: UI, workspace preferences, instance settings, and the
database migration.

**Current behavior**

People must enable Coordinator and Agent Chat in Experimental settings.
Opening a task can repeat requests because prefetch warms the full
detail and comment queries, while the default view reads compact task
data and a recent comment window. Opening Chat waits for a detail
request even when the current person's conversation list contains it.

**Proposed behavior**

Coordinator is the standard workspace for everyone. Experimental
settings offer Classic workspace, off by default. Old Coordinator
experiment values do not select Classic. Agent Chat is available to
everyone, including instances with an old saved or managed off value.
Task link intent warms the queries used by the mounted view. Chat can
use the current person's company-scoped conversation list during
refresh.

**Reason and benefit**

People can use the standard workspace without setup. Repeat navigation
can show cached content while the server refreshes it.

**Breaking changes**

Existing Coordinator experiment and Agent Chat off values no longer
disable these surfaces. Classic requires an explicit new opt-in. The
workspace API keeps its existing response shape and audit checks. The
migration retains legacy values and adds a separate Classic preference.
Execution permissions, company access, and chat privacy checks still
apply.

**Additional context**

Roadmap check: this graduates existing surfaces and improves their
navigation. It does not add a new agent execution capability. Duplicate
searches found no open Coordinator workspace PR. Related server cache
work, Refs: #17828, addresses compact issue-list invalidation; this PR
addresses client navigation queries.

## What Changed

- Add a default-off Classic workspace preference. Derive the existing
workspace API response from that choice. Keep personal persistence,
account isolation, and audited writes in the workspace preference
service.
- Replace the Coordinator experiment control with Classic workspace. Use
Coordinator while preferences load. Update the existing settings
examples and navigation recipe.
- Retire the Agent Chat experiment control. Ignore old stored and
managed off values. Retain the compatibility key and the existing
execution and access checks.
- Reuse account- and company-scoped chat list data with its original
freshness timestamp.
- Share compact task and recent-comment query options between prefetch
and the task view. Keep full-history prefetch for Classic tasks and
expanded history.
- Add upgrade, replay, cache, account isolation, and browser switching
regressions. Update product, database, development, and roadmap
documentation.

## Verification

- Targeted preference, settings, migration, cache, and UI regression
tests passed during implementation. The cache tests hold refresh
requests pending and check that content still renders. They also check
account isolation and stale refresh behavior.
- A real browser test verified fresh Coordinator defaults, Classic
opt-in persistence, and return to Coordinator. It used an isolated
source checkout with fresh data on macOS.
- Token, migration numbering and safety, map, and Storybook catalog
checks pass after the rebase.
- Repository typecheck and build pass. Typecheck and token gates also
pass after review repairs. The broad local run passed all six chat
integration shards (1,931 tests). It then found the host-CLI-dependent
recovery fixture described below. Its remaining duplicate work was
stopped after the full current-head CI matrix passed; this is not a
claim that the broad local command completed green.
- Review repairs passed 419 UI/migration fixture tests, 19 Coordinator
navigation tests, and all 214 tests in the five affected server suites.
The 24 Chat/workspace browser tests pass, including standard Coordinator
switching and explicit Classic persistence.
- The broader browser repair run passed 85 tests. It retained two failed
assertions and skipped five later serial cases. The affected files then
passed all 11 tests after the fixes: reopen the desktop panel after
reload and open More for Audit and Connectors. This includes the five
serial cases that had not run.
- The broad local suite exposed a recovery fixture that executed the
host Codex CLI during a simulated remote probe. On a host with Codex
0.154.0, model fallback broke its fixed-model assertion. The fake
sandbox now reports Codex 0.160.0. All 20 selected recovery/provider
compatibility cases pass after the repair.
- A CLI company import/export suite timed out during server startup in
the second CI run. The port fallback is intentional in that fixture. No
root cause was established. That suite passes on the final commit in CI.
This records recovery, not a claimed product fix for the earlier
timeout.
- The first CI run exposed remaining assumptions about the retired flags
and Classic UI. The repair commit updates those fixtures and navigation
steps. It also restores the missing enabled Cases destination in
Coordinator. Earlier local recovery attempts used the wrong fixture
notification and advanced fake time during live execution; the corrected
tests use the subscribed recovery queue and wall time during execution.
- Greptile reviewed commit `98972b030d4505d838427a2fca6b196b8cad6d47` at
**5/5** with no new comments. The original finding is resolved.
- Current-head CI is green: **56 successful checks**, with three checks
skipped by workflow filters. This includes typecheck, build, the
complete Vitest matrix, all eight browser shards, Runner verification,
release checks, and security checks. [Final CI
run](https://github.com/paperclipai/paperclip/actions/runs/42132651414).
- Typecheck caught missing `pid` and `startedAt` fields in the simulated
command result. The fixture now returns the complete process contract,
and the server test-layer typecheck passes.
- Storybook catalog alignment was checked. Storybook play tests were not
run. Hosted latency and cold bundle loading were not measured.

## Risks

- The new default changes the workspace for people who previously left
the Coordinator experiment off. They can opt into Classic in
Experimental settings.
- The additive migration must run before the new server code. It is safe
to replay and preserves explicit Classic choices.
- Cached content can be stale during background refresh. Existing query
invalidation and source timestamps still control freshness.
- These changes reduce avoidable loading waits. They do not establish a
measured improvement in hosted request latency or initial bundle
download time.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, repository tools, code
execution, and browser automation. The exact serving revision and
context-window size are not exposed in this session.

## 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 tests locally and they pass
- [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: Scott Tong <scott@scottsmbpm5max.lan>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-05 16:28:25 -08:00
Nicky LeachandClaude Opus 5.5 11ac90e31f fix(build): typecheck server/src/__tests__ and fix every type error (#18739)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The server has about 1,100 test and helper files in
`server/src/__tests__`
> - `server/tsconfig.json` excludes that directory, Vitest does not
check types, and no script runs the tsconfig in that directory
> - So a test can call a function with the wrong arguments and still
pass, and no tool reports it
> - This gap hid 2,168 real type errors, and some of them hid tests that
did not test what they claim
> - This pull request fixes all of these errors and adds the directory
to the server layers from paperclipai/paperclip#18737
> - The benefit is that a type error in a server test now fails the
typecheck, with no baseline and no backlog

## Linked Issues or Issue Description

Refs paperclipai/paperclip#18737, which is merged and added the server
layers. Refs paperclipai/paperclip#18738, which is merged and checks the
test projects outside `pnpm -r`, so it is not a duplicate. Refs
paperclipai/paperclip#18137, which records that this directory has no
check.

**What happened?**

No tool checked the types of 1,074 of the 1,101 files in
`server/src/__tests__`. I searched `package.json`, the workflows, and
all tsconfig files. 27 files are in the program of another tsconfig, and
these 27 have 0 errors. 11 of the 27 are in
`helpers/maintenance/tsconfig.json`. No script ran that project when I
measured this. paperclipai/paperclip#18738 now runs it.

**Expected behavior**

The server typecheck checks these files, and a type error fails the
check.

**Steps to reproduce**

1. On the base commit, run `tsc -p server/src/__tests__/tsconfig.json
--noEmit`. It reports 6,536 errors and needs 12.3 GiB.
2. Add a type error to any file in `server/src/__tests__`.
3. Run `pnpm --filter @paperclipai/server typecheck`. It passes.

**Paperclip version or commit**

I measured the error breakdown below on commit `8dfd632452`, with
TypeScript 7.0.2. The branch now includes master up to
paperclipai/paperclip#18865.

**Deployment mode**

Not applicable. This issue applies to development and CI. No production
source file in the server changes.

### Error breakdown

Each row adds one change to the row above.

| Step | Total errors | Change | Cause |
| --- | --- | --- | --- |
| Config as committed | 6,536 | | |
| Add `src/types/*.d.ts` | 4,447 | -2,089 | Config. The Express request
types were missing. |
| Set `rootDir` to the repository root | 2,436 | -2,011 | Config. |
| Set `allowImportingTsExtensions` | 2,181 | -255 | Config. Vitest
accepts `.ts` imports. |
| Add `ES2024.Promise` to `lib` | 2,138 | -43 | Config. Node 24 has
`Promise.withResolvers`. |
| Set `allowJs` for `.mjs` imports | 2,148 | +10 | Config. It removes 8
`TS7016` errors and shows new real errors. |
| Set `declaration` to false | 2,142 | -6 | Config. Tests emit nothing.
|
| Count only files in `server/src` | 2,058 | -84 | Other packages: 53
Daytona, 29 Runner test, 2 UI. |

So 4,478 of the 6,536 errors (69%) came from config or scope, and 2,058
are real type errors in 314 files. The 29 Runner test errors are also
real, which gives 2,087 errors. Tests that master added after this
measurement had 81 more errors, so this pull request fixes 2,168 errors.

This table classifies the first 2,058 errors.

| Kind of real error | Errors |
| --- | --- |
| Wrong argument or assignment type | 1,023 |
| Stale or partial fixture objects | 525 |
| Property that the type does not have | 317 |
| Missing narrowing of null or unknown | 101 |
| Implicit any | 60 |
| Missing name or module | 16 |
| Other | 16 |

### What the type errors hid

Most errors were stale fixtures and loose mocks. These errors hid real
test problems, and this pull request fixes them:

- **A test that never ran.** `instrumentation.test.ts` required
`sdk-trace-base`, which is not installed. So 3 span tests were skipped.
They now run and pass.
- **Assertions that could not fail.**
- `issue-thread-interactions-service` used a company ID as an issue ID.
- `status-card-evaluation-work` compared `[]` with `[]` in a rollback
check.
- `slack-conversation-lifecycle` read `item.issueId`, which does not
exist.
  - `email-channels` used a permission key that does not exist.
- `heartbeat-project-env` imported `LOW_TRUST_REVIEW_PRESET`, which is
not exported. The value was undefined.
- **Postgres processes that did not stop.** 11 test files called `stop`
on the test database helper. That method does not exist, and the helper
method is `cleanup`.
- **Arguments that the service ignored.**
- `methodKey` is now `connectionMethodKey` in the Daytona, Unikraft,
1Password, and Railway tests.
- 108 calls passed `"owner-user"` to a parameter that expects an options
object.
- 9 chat wizard fixtures passed `managementContext` as `source`, so they
ran `existing_app` and not `manifest`.
  - 2 tests passed a `{ userId }` actor that the function ignores.
- Inserts into `environments` passed a `companyId` column that the table
does not have.
- **Problems in tests that master added during this work.**
- `sandbox-creation-operations` read `database.connectionString` when an
external test database was set. In that mode `database` is undefined, so
the fixture would stop with a TypeError. It now uses the external URL.
- `idle-completed-history` set a Dot operation to `dispatched`. That is
a Muse status, and the Dot type does not have it. The fixture now uses
`admitted`, which is a real in-flight Dot status.

### Possible product problems, not changed

These may be real problems in production code or types. I did not change
them, because this pull request does not change production behavior:

- `decisions-service` `service.create` does not validate the lenient
`cancel_issue_tree` input.
- `createConnection` ignores `input.connectionPurpose`.
- The `createConnection` route schema default stores `{}` for
`transportConfig`.
- `prepareManagedAiRuntime` accepted the invalid value `mode:
"connection"`.
- `createBinding` accepts invalid `projectionClass` strings outside
class 3.
- `ensureManagedProjectWorkspace` forwards an untyped `repoRef` by
spread.
- `routineVariableSchema.label` is optional, but `RoutineVariable.label`
is required.
- `ToolRunContext.projectId` may be too strict.
- `withdrawInteraction` reads `issue.status`, which its type omits.
- `issueService.list` and `reconcileNativeFinalizations` omit fields
that they return at runtime.
- `createGitHubExternalObjectProvider` handles `tokenProvider: null`,
but its type forbids this value.

## What Changed

- Fixed the 2,168 type errors. The diff changes 322 test and helper
files.
- Corrected `server/src/__tests__/tsconfig.json`. Editors and the
typecheck now read the same options and report the same errors.
- Added three `test-tree` layers to
`server/scripts/typecheck-layers.mjs`. They read the server layers as
declarations, and any error fails them. A hash of the path selects the
shard, so a new file does not move other files.
- Added `server/scripts/typecheck-stubs/daytona-sdk.d.ts`. Server tests
reach Daytona plugin source, and the root install does not have
`@daytonaio/sdk`.
- Changed two imports in `ui/src/lib/issue-execution-policy.ts` to use
`.js` extensions, because server tests import this file.
- Made `declarationsConfig` and `--declarations-only` skip the
`test-tree` layers, because these layers emit nothing. Other projects
read only the source layers. Added a test for this.
- Updated `doc/DEVELOPING.md`.

## Verification

Run these commands:

```sh
pnpm --filter @paperclipai/server typecheck
pnpm exec vitest run server/scripts/typecheck-layers.test.mjs
```

Results on macOS (arm64), Node 24, 16 cores. Each check ran after the
merge of paperclipai/paperclip#18738 unless the row says otherwise:

| Check | Commit | Result |
| --- | --- | --- |
| Server typecheck, `PAPERCLIP_TYPECHECK_LOW_MEMORY=1`, all 7 layers |
`6e45a08573` | Exit 0, 0 errors, 62 seconds, peak 3.52 GiB |
| `node scripts/typecheck-test-projects.mjs --low-memory` | `a3306bd13e`
| Exit 0, 0 errors, 42 seconds, peak 3.25 GiB |
| The same script with `--reuse-declarations` | `1b878aabc0` | Exit 0, 0
errors |
| `typecheck-layers.test.mjs` and `live-recovery-work.test.ts` |
`01db5634e4` | 90 passed. These include real compiler runs for a pass, a
type error, and an invalid tsconfig. |
| `tsconfig.declarations.json` after a full server typecheck |
`0fb1e4fb0a` | It references only `below`, `cycle`, and `above`. No
generated config names a `tsconfig.layers.json` file. |
| Owned runtime test in `idle-completed-history` | `e8f2f448ac` |
Passed, with the fix that master later replaced |
| `idle-incoming-wake`, `plugin-idle-manager`, and the changed
`chat-channels` test | `6e45a08573` | 30 passed |
| Runner `native-execution.test.ts` | `eab4dcc1f4` | 114 passed |
| The 19 server test files that changed since `eab4dcc1f4`, and
`typecheck-layers.test.mjs` | `259737b700` | 4,275 of 4,277 passed. The
2 failures are in `chat-channels` and are in the list of 7 below. |

The peak is the largest single process, from `/usr/bin/time -l`. Peaks
for the server typecheck were between 3.42 GiB and 3.56 GiB across 8
runs. Peaks for the test projects were 3.25 GiB and 3.37 GiB across 2
runs. I did not measure a cgroup.

**Runtime check of the fixes.** Types do not prove that a test still
works. So I ran all 309 changed server test files, 11,929 tests, before
the rebase onto paperclipai/paperclip#18737:

| Result | Tests | Detail |
| --- | --- | --- |
| Passed | 11,913 | Includes the 2 tests and the 54 `dot-runner` tests
that passed in a later run, as described below. |
| Failed | 7 | The same 7 tests also fail on the commit before the
fixes. |
| Skipped | 9 | These tests have their own skip conditions: `sentry` 5,
and 1 each in `bundled-plugin-registration`, `customer-success-cloud`,
`onepassword-connection`, and `workspace-runtime-https-live-exercise`. |

The 7 tests that fail with and without this change, on my machine:

- 3 tests in `chat-channels` and `email-channels` stop with `ENOENT` on
a `SKILL.md` file. The path is in the parent checkout, above the git
worktree that I used, so I expect that the worktree is the cause. I did
not prove this.
- `workspace-instance-cleanup` compares `/private/var/...` with
`/var/...`. This is a macOS symlink difference.
- 2 tests in `git-reproducible-managed-checkouts` get
`unknown_base_git_files` and expect `unpushed_managed_checkout`. I did
not find the cause.
- 1 more test in `chat-channels`, `materializes direct external-chat
finals once`, gets `undefined`. I did not find the cause.

CI is the check for these 7 tests. `ci / verify` passed on `eab4dcc1f4`.

Failures during this work:

- My fix to `agent-runtime-connector-access` changed an invalid
permission key in the setup, but not in the assertion. The runtime check
found this. I corrected the assertion, and the file passes.
- `heartbeat-plugin-environment` failed one wait in the full run. It
passed three times alone, so I think that load caused the failure. It
can fail again under load.
- `dot-runner` skipped all 54 tests in the full run because `cargo` was
not on my `PATH`. It then passed 54 of 54 with the toolchain on the
`PATH`.
- My first `lib` value removed the default DOM types, and the error
count went up by 375. I added the DOM libraries.
- My Daytona stub with bare `any` left 48 errors, because generic
helpers then inferred `unknown`. I added shapes that return promises.
- One fix to `agent-secrets-routes` used `class_3_static_lease`, and the
test failed. That class allows only specific paths. I reverted to
`unclassified`.
- `ci / General tests (server (8/12))` failed on `259737b700` because of
the flaky port in `idle-completed-history`. paperclipai/paperclip#18853
fixed this test on master, so this pull request keeps that fix.

**Audit of the diff.** I counted these patterns in added and removed
lines, to find fixes that only hide an error:

| Pattern | Added | Removed |
| --- | --- | --- |
| `@ts-ignore` | 0 | 0 |
| `@ts-expect-error` | 0 | 1 |
| `.skip` | 0 | 0 |
| `as any` | 9 | 9 |
| `: any` | 11 | 2 |
| `as never` | 8 | 2 |
| `as unknown as` | 35 | 4 |
| `Promise<unknown>` | 16 | 5 |
| `expect(` | 128 | 113 |

No hunk removes more `expect(` calls than it adds. Three removals of
test content were intentional, and I checked each one:

- `muse-control` removed `signalToken`. Line 1012 already asserts that
it is absent.
- `merged-pr-confirmation-sweep` removed legacy aliases. Other tests
cover them.
- `heartbeat-workspace-branch-containment` removed a `finalize` branch
that no test row can reach.

Some partial fixtures stay unchecked through `as unknown as` and
`Promise<unknown>`, mostly in `issue-agent-mutation-ownership-routes`
and `issue-comment-reopen-routes`.

**Accuracy of the check.** One program over the tree and the three
shards reported the same errors at the same positions. Memory, as the
peak of the largest process on one thread, was 3.93 GiB for one program
and 2.43 GiB for the largest shard. I measured these two values before
the fixes.

Not run or not proven:

- I did not run the full `pnpm test:run`, or a root `pnpm build`. I
expect CI to run the full suites.
- I did not run the 4 GiB container check, or the full
`scripts/typecheck-low-memory.mjs`. The peaks above come from macOS, so
they are not a Linux cgroup measurement.
- I did not run the UI typecheck after the merges.
- `workspace-runtime-https-live-exercise` is skipped, so its `cleanup`
fix did not run.

## Risks

- **A large diff in test files.** 328 files change. A pull request that
edits the same tests can get merge conflicts. New tests on master can
also add type errors before this merges. Any such error fails this check
and needs a fix here.
- **A higher cost for changes to source types.** A change to a type can
add errors in test files that the author did not touch. The author must
then fix them. This is the purpose of the check, but it is new work.
- **Changed test behavior.** Some tests now run paths that they did not
run before, such as the 9 chat wizard fixtures and the 3 span tests.
They pass here, but they can be more sensitive to timing or to the
environment.
- **The Daytona stub can go stale.** The stub is written by hand. If the
plugin uses a new SDK member, the typecheck reports an error until
someone updates the stub.
- **The release path.** `release-verify.yml` and `scripts/release.sh`
run `pnpm -r typecheck`, so they now run this check.
- **The tsconfig change.** The test transform reads
`server/src/__tests__/tsconfig.json`. The options that I changed are for
type checks, and the runs above passed.
- **A gap that remains.** Tsconfigs in `packages/paperclip-runner` and
in most sandbox provider plugins exclude `*.test.ts`. This check does
not read most of those test files, so they can still drift. That work is
separate from this pull request.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

Anthropic Claude Opus 5.5 (`claude-opus-5-5`) through Claude Code, with
reasoning, tool use, code execution, and subagents. The runtime does not
show the context window size.

## 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
- [ ] I have run tests locally and they pass (7 tests fail locally, with
and without this change. See Verification.)
- [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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:27:38 -08:00
DottaandPaperclip 8ff812cdde feat(chat): deliver proactive task messages to agent chats (#18374)
## 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>
2026-10-30 21:39:07 -05:00