Skip to content

feat(cli): land upstream #1564 display_links on the fork - #222

Open
heavygee wants to merge 27 commits into
mainfrom
feat/land-1564-display-links-fork
Open

heavygee wants to merge 27 commits into
mainfrom
feat/land-1564-display-links-fork

Conversation

@heavygee

@heavygee heavygee commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Summary

  • Lands the upstream tiann/hapi#1564 tip (e8cbc5a00) onto fork main so display_links arrives in soup by construction.
  • Upstream feat(cli): Cursor-only display_links MCP for unmangled URLs tiann/hapi#1564 stays open and untouched (no rebase / force-push of its head branch).
  • Conflict resolution kept fork MCP tools (search_content et al.) and added the per-session Cursor-only display_links registration from the upstream tip.
  • Retires the need for held driver/display-links-delta (and the earlier fat feat/display-links layer) once this merges; manifest DROP follows in the retirement PR.

Why

Compose-on-fork means the base is fork main. Re-thinning soup deltas is the expensive shape. Promote clean upstream-shaped product into the base instead.

Test plan

  • bun run --cwd cli test -- displayLinks startHappyServer buildHapiMcpBridge happyMcpStdioBridge
  • bun run --cwd shared test -- displayLinks
  • bun run --cwd web test -- displayLinks
  • bun run typecheck:cli / bun run typecheck:web
  • CI green on this fork PR
  • After merge: remat soup; confirm display_links live via base (no layer)

heavygee and others added 27 commits September 16, 2026 02:04
Cursor-routed agents drop doubled letters when recalling URLs; store
href bytes built outside the model (MCP display_links + CLI fallback)
and render a tappable SessionChat card without a fake user turn.

Fixes tiann#1516

Co-authored-by: Cursor <cursoragent@cursor.com>
Doubled-letter URL recall is a Cursor-routed failure mode. Keep the
paint path and CLI fallback, but do not advertise display_links to
Claude/Codex/OpenCode/Grok tool lists or system prompts.

Co-authored-by: Cursor <cursoragent@cursor.com>
Estate hosts bind Cursor state under /var/lib/hapi/cursor via a home
symlink; refusing the symlink directory left overlay install dead and
pushed agents toward stale project mcp.json sidecars. Resolve to the
real directory (and honor HAPI_CURSOR_MCP_CONFIG_DIR); keep refusing
symlinked mcp.json files.

Co-authored-by: Cursor <cursoragent@cursor.com>
hapiMcpUrl is 127.0.0.1 on the owning CLI. Opening it for another hub
session hits the caller host. Restrict the CLI/script fallback to self.

Co-authored-by: Cursor <cursoragent@cursor.com>
Cursor drops doubled letters in secrets and tokens the same way it
drops them in hosts. Extend display_links with texts so agents construct
byte-accurate copy cards in tool args instead of typing them in prose.

Co-authored-by: Cursor <cursoragent@cursor.com>
Duplicate hrefs with different titles collided on React keys. Card
chrome was English-only in a file that already uses i18n.

Co-authored-by: Cursor <cursoragent@cursor.com>
Protected hubs already inject HAPI_EXTRA_HEADERS_JSON via
buildHubRequestHeaders. display-links was skipping them on /api/auth
and session GETs, so the fallback could 403 at the proxy.

Co-authored-by: Cursor <cursoragent@cursor.com>
Same proxy-header hole as the CLI command. Read
HAPI_EXTRA_HEADERS_JSON, else settings.json extraHeaders, and merge
onto auth and session fetches.

Co-authored-by: Cursor <cursoragent@cursor.com>
Cursor routes duplicate bare tool names to one hapi-* server, so
Kinrupt display_links calls painted Sparling. Disable sibling overlays
and refuse paints whose sessionId is not this HappyServer.

Co-authored-by: Cursor <cursoragent@cursor.com>
… share

Exclusive sibling disable stranded live Cursor sessions and could hit
user-owned hapi-* ids. Fail-close misroutes with sessionId instead.
Exclude exact-copy rows from Share Turn and session markdown export.

Co-authored-by: Cursor <cursoragent@cursor.com>
ACP persisted display_links rawInput, so session markdown still leaked
secrets after Share Turn exclusion. Redact texts values on the wire and
add --text-stdin so secrets are not process arguments.

Co-authored-by: Cursor <cursoragent@cursor.com>
Rebase onto upstream/main added an exhaustive ChatBlock projector;
handle the new display-links kind so web typecheck stays green.

Co-authored-by: Cursor <cursoragent@cursor.com>
Bump FIXTURE_VERSION for the new projection shape, add cursor-display-links
golden, and implement normalize/reduce/project/UI on both native ports.

Co-authored-by: Cursor <cursoragent@cursor.com>
Kotlin refuses smart-casts on public API properties from another module;
bind title locals before Text/clipboard use so :app:compileDebugKotlin passes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Catch ActivityNotFoundException via the existing ChatLinkHandler helper so
kiosk/work-profile devices toast instead of crashing the chat screen.

Co-authored-by: Cursor <cursoragent@cursor.com>
Register hapi_<sessionId>_display_links so concurrent Cursor hapi-* overlays
do not collide on bare display_links (forum 148059). Bridge/CLI/mjs forward
the same name; redaction still matches *_display_links.

Co-authored-by: Cursor <cursoragent@cursor.com>
Retention now uses the same URL/text parsers as normalize so invalid-only
payloads are not kept. Doctor prints each Cursor session's
hapi_<sessionId>_display_links name.

Co-authored-by: Cursor <cursoragent@cursor.com>
Whitelist exact-copy row shape so backup/malformed texts cannot persist
secrets; mark ClipData EXTRA_IS_SENSITIVE so Android 13+ hides clipboard
previews for token cards.

Co-authored-by: Cursor <cursoragent@cursor.com>
Rebuild the persisted/exported tool-call shape from urls/texts/sessionId so
arbitrary top-level or nested backup fields cannot leak secrets.

Co-authored-by: Cursor <cursoragent@cursor.com>
Fail closed when ACP feeds a string/array as tool input. Exact-copy on iOS
uses localOnly + 120s expiration instead of Universal Clipboard.

Co-authored-by: Cursor <cursoragent@cursor.com>
Upstream chat blocks gained optional roundSummary; keep display-links in
the same ChatBlock union for reducer/runtime typechecks.

Co-authored-by: Cursor <cursoragent@cursor.com>
Rebase brought a new golden still stamped at v1; regenerate so drift-gate
and native FixtureDecodingTest stay aligned with FIXTURE_VERSION.

Co-authored-by: Cursor <cursoragent@cursor.com>
Exact-copy values were still landing in hub-synced requests/completedRequests via PermissionAdapter; also bump fixture README to version 2.

Co-authored-by: Cursor <cursoragent@cursor.com>
Upstream plan/permission/tool-group golden outputs drifted; regenerate so drift-gate stays green.

Co-authored-by: Cursor <cursoragent@cursor.com>
java.net.URI / Foundation URL reject path spaces that web URL accepts; validate with a %20 copy while keeping original href bytes, and cover it in the cursor-display-links fixture.

Co-authored-by: Cursor <cursoragent@cursor.com>
Upstream permission/plan golden outputs drifted; regenerate so drift-gate stays green.

Co-authored-by: Cursor <cursoragent@cursor.com>
Cursor-only display_links MCP + CLI fallback + tappable SessionChat cards.
Upstream tiann#1564 stays open and untouched; this is a clean fork-landing
off origin/main (same shape as #204 / tiann#1933).

Conflict resolution kept fork MCP tools (search_content et al.) and added the
per-session display_links registration from the upstream tip.

Co-authored-by: Cursor <cursoragent@cursor.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-08T09:45:10.973681Z ad7ebce PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@heavygee

heavygee commented Oct 8, 2026

Copy link
Copy Markdown
Owner Author

Hold: app-build red twice (not merging)

All package/integration/transcript-layout checks are green. Codex review completed with no findings.

app-build failed twice on the same iOS UICollectionView recycling assertion:

ToolTranscriptPresentationTests.testPlanPublicationUpdatesRecyclingAndInspectorPreserveTheDocumentAndAnchor
XCTAssertNil failed at ToolTranscriptPresentationTests.swift:264 (cell still present after scroll-away; expects recycle).

This is not the display_links surface (plan ExitPlanMode recycling). Parallel transcript-layout (29)/(36) jobs passed on the same tip. First failure looked like classic flake; second failure on --failed rerun means I am stopping rather than merging-while-red (Overseer instruction).

Next options for operator/Meta:

  1. Third rerun / full app-build re-run if believed flake
  2. Quarantine/fix that assertion separately (orthogonal to feat(cli): Cursor-only display_links MCP for unmangled URLs tiann/hapi#1564)
  3. Merge with explicit acknowledgment that app-build is advisory (main has no classic branch protection; still violates the no-merge-while-red bar we set today)

tiann#1922 fork-landing is prepared on feat/land-1922-peer-stamp-fork (stacked on this tip; local tests + typechecks green) but not opened as a PR until #222 can land cleanly.

@heavygee

heavygee commented Oct 8, 2026

Copy link
Copy Markdown
Owner Author

app-build red ×3 — three different flakes (still holding)

Attempt Failing test
1 ToolTranscriptPresentationTests.testPlanPublicationUpdatesRecyclingAndInspectorPreserveTheDocumentAndAnchor (XCTAssertNil recycle)
2 HomeFilterPresentationTests.testFilteringResetsScrollButLiveUpdatesAndChatReturnPreserveIt (XCTUnwrap UICollectionView nil)
3 TranscriptTypographyTests.testShrinkingTextClampsAnOffsetBeyondTheNewRowHeight (offset 196 vs 1)

Dedicated transcript-layout (29)/(36) jobs remain green on the same tip. Package/integration/windows-codex-mcp green. Codex review: completed, no findings.

Per Overseer: not merging while app-build is red. Need an explicit call to treat app-build as advisory / override, or a fourth rerun after the iOS suite settles. tiann#1922 landing branch is pushed (feat/land-1922-peer-stamp-fork) but PR held until #222 can land. Catalog DROP is #224.

heavygee added a commit that referenced this pull request Oct 15, 2026
…2 x6)

- Preserve non-assertion vitest failures: a runner-level crash (import
  error, zero tests discovered) exiting non-zero with no per-test
  assertions previously normalized to an empty results list and the gate
  reported a clean pass. check now takes --runner-exit-code and
  unconditionally blocks when it's non-zero with nothing attributable.
- Require the known assertion text AND its call site to both match, not
  either alone: signature now supports " && "-joined required substrings.
  A location-only anchor (this session's own prior fix) could still
  mis-excuse a genuinely different failure that happens to throw from the
  same line; the live cli: entry now requires both.
- Reject day-overflow dates (2026-02-30 normalizes to 2026-03-02 under
  plain `new Date()` instead of failing) via a round-trip check.
- Make quarantine test fixtures independent of wall-clock expiry — they
  used the same near-term dates as the real registry, so every "currently
  active" scenario (including the ceiling fixture) would silently flip to
  expired/inactive on 2026-10-30 and permanently redden the tooling-tests
  CI job. Dates are now computed relative to the actual test run.
- Honor an inherited $BUN instead of bare `bun`/`bunx` throughout the gate
  script; hapi-driver-rebuild.sh now passes it through explicitly (it was
  never exported, so the previous refactor into a separate script silently
  dropped a custom bun path despite the rebuild script itself resolving it
  via "${BUN:-$HOME/.bun/bin/bun}").
- Reject partially-numeric issue/renewal_count values (parseInt("27O") ===
  27) — now requires the entire scalar to match integer syntax.
- Parse every quarantine list item regardless of field order: only a
  literal `- id:` line started an entry before, so `id` reordered or
  misspelled silently dropped the whole entry instead of raising a schema
  error.
- Default issue-open check now derives the repo from the `origin` git
  remote (overridable with --repo) instead of hardcoding heavygee/hapi,
  matching the module's own "generic mechanism" header.

Six new regression tests cover all of the above; full suite now 16/16.
Re-verified end-to-end against the real registry, the real PR #222/#270
captured failures, and a full gate run on the driver tree.

via HAPI (https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>
heavygee added a commit that referenced this pull request Oct 15, 2026
… x2, P2 x4)

- Represent suite-level vitest failures (collection/import errors, zero
  assertions) as their own normalized result instead of silently vanishing
  when results also contains an unrelated quarantined assertion failure —
  the id never matches a registry entry, so it blocks as "not quarantined"
  by the same path a real failure would.
- Require EVERY attached failure message to match the signature, not just
  one joined into a blob: normalized results now carry `messages: string[]`
  end to end. A quarantined test with a known-signature failure plus a
  second, different message (multiple expect.soft() failures is a real
  shape already used in cli/src/agent/permissionJson.test.ts) now blocks
  on the second message instead of being excused by the first.
- Start a YAML list item on ANY `- <key>:` marker, not only known field
  names — `- iid: "..."` (typo'd id) now still creates an entry (which
  then correctly raises "id is required") instead of silently discarding
  the whole item and everything indented under it.
- Distinguish "renewal_count absent" (defaults to 0, fine) from "present
  but malformed" (now a schema error) — coerceField returns an {invalid}
  marker instead of collapsing both cases to the same default.
- Reject a future `added` date — a year typo (2099 vs 2026) previously
  still satisfied the 14-day added/expires window and the entry read as
  currently active.
- Reject an empty " && "-joined signature clause (trailing delimiter, or
  an all-whitespace clause) — message.includes('') is true for every
  message, so an empty clause silently excused anything under that id.
  matchesSignature also defends against this directly, independent of
  validation having run first.

Six new regression tests cover all of the above (22/22 total), including
one against the REAL vitest collection-error JSON captured from a live
`import of a nonexistent module` run, not just synthetic fixtures.
Re-verified end-to-end against the real registry and the real PR #222/#270
captured failures with the new messages[] shape.

via HAPI (https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant