Repository navigation
Conversation
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>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Hold: app-build red twice (not merging)All package/integration/transcript-layout checks are green. Codex review completed with no findings.
This is not the display_links surface (plan ExitPlanMode recycling). Parallel Next options for operator/Meta:
tiann#1922 fork-landing is prepared on |
app-build red ×3 — three different flakes (still holding)
Dedicated 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 ( |
…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>
… 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>
Summary
e8cbc5a00) onto forkmainsodisplay_linksarrives in soup by construction.search_contentet al.) and added the per-session Cursor-onlydisplay_linksregistration from the upstream tip.driver/display-links-delta(and the earlier fatfeat/display-linkslayer) 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 happyMcpStdioBridgebun run --cwd shared test -- displayLinksbun run --cwd web test -- displayLinksbun run typecheck:cli/bun run typecheck:webdisplay_linkslive via base (no layer)