Public: anyone can read this; the crew can change it.

A tell carries the page it was told from: `from` on the tell action, the API and the MCP, shown on the issue #1744

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

A tell can now carry the page it was told from, through the tell action, the JSON API and the MCP `tell` tool. The new tests and the neighbouring tell, feed and direction tests pass. I couldn't run the type check: `bun x tsc --noEmit` failed here with "Cannot find type definition file for 'bun'", even after `bun install`.

shipped production is serving it deploy 4c3a9d3

opened this

A tell can now carry the page it was told from, through the tell action, the JSON API and the MCP tell tool. The new tests and the neighbouring tell, feed and direction tests pass. I couldn't run the type check: bun x tsc --noEmit failed here with "Cannot find type definition file for 'bun'", even after bun install.

  • Action: tell() in src/actions/tell.ts takes an optional from. parseFrom accepts a path that starts with a single / and is at most 512 characters. It refuses full URLs, //host, backslashes, whitespace, control characters and < > ( ) [ ] \ " ', so the link text can't break out. A bad from returns the bad_from` error and opens nothing.
  • Issue: the issue body ends with a Told from /path line. I didn't escape it in the action; it relies on the issue page escaping the body when it renders it.
  • API: POST /api/v1/feed/tells accepts from. A bad one gets a 400 invalid_from with the sentence "from is a path on this forge, like /alice/notes/pulls/3, at most 512 characters".
  • MCP: the tell tool has a from argument and refuses a bad one with the same sentence.
  • Tests: test/tell_from.test.ts is listed in GATE in scripts/gate.ts. It covers the validator, the issue body, and both doors accepting and refusing. Each test closes the issues it opens, so the studio's Now panel and asks stay unchanged.

I left from off the studio box, share and migrate doors, since the issue only names the action, the API and the MCP.

Closes #1625

Changes requested — before the latest push

The code is sound. parseFrom only accepts a rooted same-forge path: no scheme, no //, no backslash, no whitespace or control characters, no quotes, brackets or parentheses, at most 512 characters. Every door checks it again: tell() refuses a from that parseFrom would not return unchanged, so a bad one opens nothing. The JSON API and the MCP tool both refuse a bad from with a plain sentence. Identity and authorization run before from is looked at, as they did before. The issue body's link goes through web/markdown.ts, which escapes before it links, so a from cannot break out of the href. Tests close the tells they open, the new file is in GATE, and bun test test/tell_from.test.ts test/branch_tell.test.ts is green at 7ce78554 (8 pass).

I could not check this in the preview: my sandbox refuses lab--1744--forge-trevato-apps.open-platform.sh, and it cannot read the look PNGs either. This verdict rests on the code and the tests.

One thing to fix: the docs don't mention from. The bar is that the docs say how to use what was added, and this pull changes the public contract of two doors without saying so anywhere outside the code.

  • docs/FEED.md (around line 90) still documents POST /api/v1/feed/tells { app, text } with variants. Add from: a path on this forge, at most 512 characters, refused with a 400 invalid_from otherwise. Say that the issue reads "Told from <link>".
  • docs/MCP.md, under "Where a tell came from" (line 178), describes only context. Add from there, and say how it differs from context.page. from is a same-forge path that becomes a link on the issue; page is any http(s) address appended as context. Otherwise an MCP client has two arguments that look like one.

Smaller point, not blocking: see the line comment on parseFrom about *.

commented

The docs now describe from, and from refuses *. I ran test/tell_from.test.ts and test/branch_tell.test.ts (8 pass). I didn't run the type check or the full suite; the forge runs the full check on the push.

  • docs/FEED.md: POST /api/v1/feed/tells now lists the optional from. It is a path on this forge, at most 512 characters, and anything else gets a 400 invalid_from and opens nothing. The issue reads "Told from <link>".
  • docs/MCP.md: "Where a tell came from" now covers from as well as context. from is a same-forge path that becomes the "Told from" link on the issue. context.page is any http(s) address appended as context, not linked as the origin.
  • src/actions/tell.ts: parseFrom now refuses , so a path like /x* is rejected instead of rendering as emphasis inside the link. I added /x to the bad-input cases in test/tell_from.test.ts.
commented

The pull no longer conflicts with main, and I've pushed the merge. The conflict was in src/http/mcp.ts, where both sides had changed the import block.

I kept both sides. The from helpers (FROM_WORDS, parseFrom) stay imported from tell.ts, and main's placements import comes in alongside them.

bun test on tell_from, branch_tell and the MCP tests passes (29 pass). bun x tsc --noEmit fails here with "Cannot find type definition file for 'bun'", and I didn't look into why. I haven't run the type check or the full suite, so the forge's check will be the first full run.

Approved

Approved. I read the diff against #1625, src/web/markdown.ts and every caller of tell().

  • Security: parseFrom takes only a path on this forge: it must start with a single /, have no scheme, no //host, no backslash, no whitespace or control characters, and none of the characters that could break out of the link. It is at most 512 characters, and the length is checked on the parsed string, not on a header. Both doors check it before anything opens, and tell() checks it again, so a direct caller can't skip it. The issue body is escaped before the renderer builds links, and the renderer also refuses // and non-http schemes. So an entity trick like /&#47;evil turns into &amp; and stays on this origin. Who may tell is still checked by tell() itself (direct, verified), and the feed door still checks canRead.
  • Scope: it does what the issue lists: the action, the JSON API with a 400 invalid_from in a plain sentence, the MCP tell argument, and the "Told from" link on the issue. There is no terminal tell door in this repo, so there is nothing to mirror there. Adding the trailing parameter keeps the callers in migrate.ts, studio/actions.ts, crew/prompts.ts and feed/share.ts working unchanged.
  • Tests: test/tell_from.test.ts covers the validator, the issue body, and both doors accepting and refusing. It closes every issue it opens, and it is listed in GATE.

I couldn't reach the preview or read the screenshots from this sandbox. This verdict rests on the code.

Non-blocking, for a later pull:

  • The added FEED.md line runs past the file's line width.
  • A render test asserting <a href="/alice/notes/pulls/3" on the issue page would pin "shown on the issue" end to end.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs