Skip to content

@ in the chat mentions the workspace's files, sent as links the agent reads - #672

Merged
danbarua merged 3 commits into
mainfrom
chat-mentions
Oct 12, 2026
Merged

danbarua merged 3 commits into
mainfrom
chat-mentions

Conversation

@danbarua

Copy link
Copy Markdown
Owner

Problem

The composer's @ list was built but had nothing to list, and a mention sent nothing to the agent but its text.

Change

Every file's path at HEAD, in the files API

  • GET /workspace/{slug}/paths answers the paths of the files in the tree at HEAD, in name order, as a paths array. Directories and submodules are not listed.
  • At most 5,000 paths are listed. total counts them all, and truncated says when some are left out.
  • The top directory at HEAD links it as paths; a directory below the top, and the tree at a commit, do not.

Mentions in the chat

  • Once the chat has been shown in a workspace, it follows the workspace's file collection to paths and offers each path after @.
  • A file a message mentions is sent with it as a resource_link: uri is file://<workspace folder>/<path>, each segment escaped, and name is the path. The text block is sent unchanged.
  • The mention parser (mentionedLinks in src/ui/agent/mentions.ts):
    • It links each file once, in the order first mentioned.
    • It tries longer paths first, so @a.ts.bak is not @a.ts.
    • It finds paths with spaces.
    • A full stop that ends a sentence also ends the mention.
    • An @word that names no file, or an @ inside a word, stays text.

ACP client. promptBlocks and SessionClient.prompt take links. The blocks go text, links, then attached files, and a prompt of links alone has no text block. ACP requires every agent to take resource_link, so no capability is checked.

Controls

  • Link, not embedded content. ACP prefers an embedded resource when the agent takes embedded context, because it saves a round trip and covers sources the agent cannot reach. Here the agent works in the same folder, and embedding would mean the browser downloading the file from the API and sending it back to the agent. The mention is a link the agent reads itself.
  • How the agent treats the link. The pinned labkit-effect host turns a resource_link into a Markdown link line in the prompt's text (linesOf in host.ts). In a test on :8850 against bonsai-2026, the model saw [README.md](file:///…/bonsai-2026/README.md), called it "just markdown text", and offered to read it. Having the agent read linked files in its folder is a change for the agent runtime, not this repository.
  • HEAD only. Mentions come from HEAD's tree, as the File Explorer does. An uncommitted or ignored file cannot be mentioned. The agent reads the file on disk, which may differ from HEAD.
  • A cut-short list is used as it is, with a warning in the console naming how many files were listed and how many there are.
  • The one address the page builds from data is the file: URI. It is an address for the agent, not an address of the API.
  • The paperclip. It opens the file chooser in Chromium (checked with Playwright's filechooser event). On the device used for the test above, a click did nothing; this PR does not change it.

Verification

  • bun run check: exit 0, 1557 pass, 0 fail.
  • bun run e2e with E2E_PORT_API=9019 E2E_PORT_DEV=8970 E2E_PORT_BUILT=8971: 165 passed, 11 skipped (the dev-only tests, in the built bundle).
  • Unit tests:
    • tests/git-api.test.ts: the top directory links paths. The listing has README.md and src/a.ts, no directories, and is in name order, with total and count equal and truncated false. A lower directory and the tree at a commit link no paths.
    • packages/acp-client/tests/prompt-blocks.test.ts: a link is a resource_link between the text and the attached files, whatever the agent takes. A prompt of links alone has no text block.
    • tests/mentions.test.ts: the file: URI escapes each segment. Each mentioned file is linked once, in order. The longer path wins. A path with a space and a sentence's full stop are handled. An unknown @word and an @ inside a word link nothing.
  • e2e, dev server: typing @README.m offers README.md, and picking it writes @README.md . Sending it posts session/prompt with exactly the text block look at @README.md and one resource_link to file://<alpha's folder>/README.md, named README.md.

Linked issues

None. This follows #671.

🤖 Generated with Claude Code

danbarua and others added 3 commits October 12, 2026 12:53
…rectory as paths

GET /workspace/{slug}/paths answers the paths of the tree's files, in
name order, without directories or submodules: what the chat will offer
to mention. At most 5,000 are listed; total counts them all and
truncated says when some are left out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er the text

ACP requires every agent to take resource_link blocks, so no capability
is asked for. The blocks go text, links, then attached files; a prompt
of links alone has no text block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t with the message

The chat reads every file's path at HEAD through the files API (the
workspace's file collection links paths) once the chat is shown. A file
a message mentions is sent as a resource_link to it in the workspace's
folder; an @word that names no file stays text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the area:web The web app and shared UI: app-web, ui, design, records, view-model, acp-client label Oct 12, 2026
@danbarua
danbarua merged commit 86e4878 into main Oct 12, 2026
10 checks passed
@danbarua
danbarua deleted the chat-mentions branch October 12, 2026 12:13
danbarua added a commit that referenced this pull request Oct 12, 2026
…uttons; File Explorer follows the open file; core-agent deleted (#673)

## Problem

- **`packages/core-agent`:** this old agent runtime is not used. The VS
Code extension runs labkit-effect's agent and imported only two logging
helpers from it.
- **The landing view:** the app started at the API's root list of
workspaces (`/collections/workspace`). A picked workspace was carried in
`?list=` until a record opened.
- **File Explorer:** a file opened from the graph or a link was not
shown in File Explorer when its folder was closed.
- **The filter box:** the Items pane's filter took a row of its own all
the time, and File Explorer and Source Explorer had no filter.

## Change

**`core-agent` deleted**
- `diagnosticError` and `redactDiagnostics` move into
`packages/app-vscode/src/utils/redact.ts`, with their test. The
extension still builds (`bun run build` in `packages/app-vscode`).
- `docs/agent/` was core-agent's guidance and is deleted too, except the
note on what ACP's messages mean, now `docs/acp-language.md`.
- CLAUDE.md, the root `package.json` (`test:agent`), the PR labeler, the
comment-length check's exemptions and the layering-rule baseline no
longer name it.

**File Explorer follows the open file.** When a file in the tree opens
from anywhere else, such as the graph or a link, the folders above it
open, so its row and the box show. A folder closed afterwards stays
closed until another file below it opens (`foldersAbove` in
`explorers.tsx`).

**Filters behind a search button**
- Items, File Explorer and Source Explorer each have a borderless search
button at the right of the list's header. The filter box shows only
after the button is pressed.
- Hiding the box, by the button or by Escape, empties it, so a hidden
filter never narrows a list.
- **File Explorer's filter** searches every committed path (`/paths`),
so it finds a file in a folder not yet opened, and lists the matches
with their folders. At most 200 are drawn, and a line says how many
match out of how many files.
- **`/paths` link template.** The listing now links each path's file
with an RFC 6570 template, `file: { href: ".../file/{+path}", templated:
true }`, so the page opens a match without building the address itself.
- **Source Explorer's filter** narrows the commits loaded so far by id,
message, author or branch, without their lanes. It says how many of the
loaded commits match, and when more can be loaded.

**Workspace-scoped app**
- **API.** `GET /workspace` lists the workspaces, each addressed by its
own index, with no index above it. `/collections/workspace` stays for
other API clients.
- **Landing view.** The app starts at a list of workspaces read from
`/workspace`. A request test checks that the landing view asks for
nothing under `/collections`.
- **Picking a workspace** opens `/app/workspace/<slug>`.
- **Record Explorer.**
- In a workspace it lists the workspace's collections, and below them
the collection picked (`?list=`).
- `?list=` is left out when it would name the workspace's own
collections.
- With no workspace it is disabled, and says "Pick a workspace to browse
its records."
- **Old addresses.** An address that names its workspace only in
`?list=`, such as `/app/?list=/workspace/alpha/question`, is rewritten
to `/app/workspace/alpha?list=/workspace/alpha/question`.
- **Dev server proxy.** The API path pattern now also matches
`/workspace` followed by a query.

## Controls

- **Source Explorer's filter** searches only the commits loaded so far,
and says so.
- **File Explorer's matches** have the plain file icon: the path list
names no media type, and the page maps no extensions.
- **A workspace's own page** shows the graph card, empty until a record
opens, and the note "Pick a collection in Record Explorer, then an item,
to open it here."
- **Test count.** The suite drops from 1557 to 1104 tests, because
core-agent's own tests went with it.

## Verification

- `bun run check`: exit 0, 1104 pass, 0 fail.
- `bun run e2e` with `E2E_PORT_API=9019 E2E_PORT_DEV=8970
E2E_PORT_BUILT=8971`: 173 passed, 11 skipped (the dev-only tests, in the
built bundle).
- `bun run build` in `packages/app-vscode`: built `extension.cjs`.
- **Unit tests:**
- `tests/file-tree.test.ts`: the folders above a file, and none outside
the tree.
- `tests/api.test.ts`: `/workspace` and `/workspace/` list alpha and
beta, with no index link.
  - `tests/git-api.test.ts`: `/paths` links its file template.
- `packages/app-vscode/src/utils/redact.test.ts`: the redaction test,
moved from core-agent.
- **e2e, new or changed:**
- The root lands on the workspace list, Record Explorer is disabled, and
no request goes to `/collections`.
  - Clicking alpha, then Question, then Q_1 opens each in turn.
  - The two old `?list=` addresses are rewritten to their path forms.
- Opening `notes/plan.md` from a link opens `notes` in File Explorer and
marks the row.
- The Items filter is hidden until its button. It narrows the list to
Q_2, and Escape hides and empties it.
- File Explorer's filter for "plan" lists `notes/plan.md`, which was not
loaded, as 1 match, and opens it.
- Source Explorer's filter for "side work" leaves the commit "Side
work", and says how many loaded commits it searched.

## Linked issues

None. This follows #672.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:web The web app and shared UI: app-web, ui, design, records, view-model, acp-client

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant