Repository navigation
Conversation
hey-cli had no way to reach the HEY desktop app on this computer. #640 wrote one: a JSON-RPC client for the door the Linux app opens at $XDG_RUNTIME_DIR/hey/agent.sock while "Let Agents Use HEY" is on, and a fake door for tests on a temporary Unix socket. This brings internal/localapp over from #640 at fcf918b unchanged, so the profile endpoint built next talks to the app through it. Verified by go test ./internal/localapp/..., the package's own tests. #640's hey local commands do not come with it: the design that replaces them routes the ordinary commands through the app when a profile says so.
An agent on this computer could reach HEY only through HEY's server, with the person's full sign-in. #640 reached the HEY Linux app's door instead, but through a command tree of its own (hey local), apart from the commands an agent already knows. The design that replaces it makes the endpoint a field of a profile, so the ordinary commands answer from the app when the profile says so. A profile may now name "endpoint": "app" (or "server", the default). With HEY_EXPERIMENTAL=app set, such a profile is answered by the HEY app on this computer: hey box view imbox, hey screener list and history, and hey draft list call the door's imbox.list, screener.list and drafts.list, and answer under the server's field names (postings with topic_id and creator, email_address, status, updated_at), the app's own facts beside them. Every envelope names meta.endpoint, and meta.profile as before. On an app profile nothing reads a credential or signs in; every other command that reads a server is refused (exit 1), hey mcp and hey auth with their own words, and so are --account, HEY_ACCOUNT_ID and a repository's account_id; a shut door is exit 6 with the way to open it, never HEY's server instead. A hidden hey app tools|call gives raw access to the door's read tools. Without the gate an app profile is refused as not in this build, and help, .surface and the docs are unchanged. Verified by the new tests in internal/cmd/app_endpoint_test.go and internal/config/profiles_test.go, each against a fake door on a temporary Unix socket and a server that fails the test if reached; each was shown red with its behaviour removed. make check passes, and the build compiles for darwin, windows and linux. Rejected: falling back to the server when the gate is off or the door is shut, which would answer with the person's full sign-in where they chose the app; hey local's own command tree; writes, sends and exit 9, which come in the next pull request; hey profile create --endpoint, left to the setup step that creates the app profile.
There was a problem hiding this comment.
All reported issues were addressed across 19 files
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
The fresh review of the app endpoint found four small gaps. Tab completion was refused on an app profile as a command the app does not offer. hey profile show and list looked for the app profile's credential in the keyring, which it never has and which can prompt on macOS. The Imbox rows carried the server posting's empty note and bubble_up_schedule and a thread read breadcrumb the app profile refuses. And an endpoint written as true or 1 was read as the server. Completion now runs on an app profile; profile show and list name the app in place of a sign-in and read no credential; the Imbox rows carry only what the app has, with no breadcrumb; an endpoint that is not a string is a config error. Verified by TestAppProfileKeepsCompletionAndReadsNoCredential, TestAppImboxRowsCarryNoEmptyServerFields and the non-string case of TestParseProfilesReadsTheEndpoint, each red with its fix removed, and make check. Not changed: --ids-only on the app's screener prints sender ids where the server prints clearance ids, which --json names in meta.id_kind; no command on the app takes either id yet, so it waits for the writes.
There was a problem hiding this comment.
All reported issues were addressed across 6 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
The bot review of the app endpoint found eight gaps. --all was accepted on the app's reads, which answer only what the app has loaded, so a partial list could answer a request for all of it. hey app took --account and ignored it. The app quitting mid-answer was exit 7, not the shut door's exit 6. hey app call on a signed-out app, whose door lists nothing, was a usage error rather than exit 3. A screener count below the senders listed was taken as is; a draft with no Cc read as null; a thread with no sender read as sender 0. An explicit "endpoint": "server" in a nearer config could not override a farther "app", and an app-only entry was not counted as a profile by profile create's first-profile default or by profile delete's repository guard. Now --all is refused on the app like --page; --account is refused on hey app; a connection the app closes is exit 6 with the way to open the door; an empty tool list is exit 3 for hey app call; the screener's count must cover its list; address lists stay lists; creator.id is left out when the app gives none; "server" is kept as written, so it overrides; and config.EntryIsProfile counts an app-only entry wherever entries are counted. Verified by the new and widened tests in app_endpoint_test.go and profiles_test.go, each red with its fix removed, and make check.
…adable store TestAppProfileKeepsCompletionAndReadsNoCredential ran profile show alone, with no credential store at all, so a read of the app profile's credential by profile list, or by show, would have passed unseen. It now writes a credential file nothing can parse and runs both: either read fails the run. Shown red with each command's skip removed.
There was a problem hiding this comment.
All reported issues were addressed across 7 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
There was a problem hiding this comment.
🟡 Changes recommended
Active issues leave early error envelopes without endpoint metadata, prevent explicit server overrides of app profiles, and cause endpoint-only app profiles to be mishandled during creation and deletion.
3 open issues
What changed in this PR
Builds on named profiles to let existing read commands use the HEY desktop app instead of the server, behind HEY_EXPERIMENTAL=app.
Changes:
- Adds profile endpoint selection, unsupported-command refusals, and endpoint metadata.
- Routes Imbox, Screener, and draft listings through the app’s Unix socket, with hidden read-only tool access.
- Adds protocol and command tests using a fake app connection, plus contributor guidance.
[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or rungh pr ready --undo.
Click "Ready for review" or rungh pr readyto reengage.
| File | Description |
|---|---|
internal/localapp/socket.go |
Resolves the app’s socket path. |
internal/localapp/localapptest/door.go |
Provides a fake Unix-socket server for tests. |
internal/localapp/errors.go |
Defines connection and protocol errors. |
internal/localapp/client.go |
Implements MCP initialization, discovery, and calls. |
internal/localapp/client_test.go |
Tests protocol handling, results, and timeouts. |
internal/config/profiles.go |
Parses and validates profile endpoints. |
internal/config/profiles_test.go |
Tests endpoint parsing and selection restrictions. |
internal/cmd/screener_list.go |
Routes pending-sender reads to the app. |
internal/cmd/screener_history.go |
Routes screening-history reads to the app. |
internal/cmd/root.go |
Integrates endpoint routing and response metadata. |
internal/cmd/profile.go |
Displays endpoints and adjusts credential checks. |
internal/cmd/local_config_trust.go |
Exempts direct app commands from repository configuration. |
internal/cmd/endpoint.go |
Centralizes routing, refusals, and app error handling. |
internal/cmd/drafts.go |
Routes draft listings to the app. |
internal/cmd/box.go |
Routes supported box reads to the app. |
internal/cmd/app.go |
Adds hidden tool discovery and read-only calls. |
internal/cmd/app_reads.go |
Maps app listings into CLI output formats. |
internal/cmd/app_endpoint_test.go |
Tests app routes, gating, refusals, and output. |
AGENTS.md |
Documents experimental endpoint conventions for contributors. |
Review effort: Balanced
The delete guard counts a repository entry with "endpoint": "app" and no base_url as a profile since bc9a34e, but no test pinned it: the old base_url-only check passed every test. TestARepositoryAppOnlyEntryHoldsUpADelete deletes a global profile a trusted repository also names as app-only, and is red with the old check.
RegisterProfile's and localEntrySetsProfile's comments still said an entry with no base_url is no profile; since bc9a34e an entry the app answers is one, by config.EntryIsProfile. Comments only.
|
Review summary for cfd5879. Every thread is answered and resolved; the fixes are in 7241ab2, bc9a34e, 4225008, bd7d6e4 and cfd5879, each shown red with its fix removed, and Declined, each with its reasoning in the thread:
Also left as is, in the description's Alternatives: Unrun: a check against a running HEY Linux app, since none had the door open on thelio. Reviewers on cfd5879, a comment-only change: cubic ran on it with no new finding; Greptile last reviewed bd7d6e4 and Copilot 7241ab2, re-requested once. |
…er --all Deleting a profile the HEY app answers looked up its CLI and MCP credentials and revoked any it found, the one place left where the app profile touched the credential store: on macOS that can prompt for the keychain, and it reaches HEY's server for a profile that has no sign-in of its own. A limited app listing (box view imbox, draft list) said "Use --all to see everything", which the app endpoint refuses, and on the Imbox the truncation notice replaced the app's own warning that it has not loaded every thread. Now delete skips the credential lookup and revocation for an app profile, as profile list and show already do; without the experimental gate the profile is treated as a server one and its credentials go as before. App listings say "Run it again without --limit to see them all", and the Imbox appends its not-loaded warning after that line. Verified: TestAppProfileDeleteReadsNoCredential (an unreadable credential store fails any lookup; red before, "could not delete credentials") and TestAppLimitedListingsOfferOnlyWhatTheAppTakes (red before on both routes and on the lost Imbox warning) pass; make check green. Rejected: dropping the leftover credential of a profile hand-edited from server to app on delete. The app profile's contract is that it has no credential; a hand edit that leaves one is cleaned by deleting the profile with the gate off, or by hey auth logout before the edit.
|
Review threads: 2 resolved (2 fixed, in c588eee, with a red-first test each).
CI and cubic are green on c588eee; no threads open. |


Context
$XDG_RUNTIME_DIR/hey/agent.sock, open while Settings › General › "Let Agents Use HEY" is on), but through ahey localcommand tree of its own.heycommands answer from the app when the profile says so, behindHEY_EXPERIMENTAL=app, the default stays the server, no auto-switching.sent/waitingand exit 9 next; setup and the skill after.Change
"endpoint": "app"(or"server", the default); an app profile needs nobase_url. Any other value is a config error, never read as the server.HEY_EXPERIMENTAL=app, an app profile is answered by the app's door:hey box view imbox(imbox.list),hey screener listandhey screener history(screener.list),hey draft list(drafts.list).topic_id,creator,seen;email_address;statusapproved/denied;updated_at,scheduled_delivery_at); the app's own facts ride along (made_as,sendable,kind, addresses);meta.id_kind: "sender"on the screener, whose ids are senders, not clearances.meta.endpoint(apporserver) in an experimental build, besidemeta.profile.--account,HEY_ACCOUNT_ID, a profile's own and a repository'saccount_idare refused (exit 1); every other command that reads a server is refused as "not offered by the HEY app on this computer" (exit 1),hey mcpandhey auth/login/logoutin their own words; another box,--pagelikewise; bareheyprints help, no wizard.hey app tools|call, registered only under the gate, runs the door's read tools; a tool of any other effect is refused before it is called.profile listandprofile showname the endpoint under the gate.internal/localappcomes from hey local: ask the HEY app on this computer to act for you #640 at fcf918b unchanged..surfaceand the docs are unchanged.Verification
internal/cmd/app_endpoint_test.go(21 tests) and two ininternal/config/profiles_test.go, each against a fake door on a temporary Unix socket and a server that fails the test if reached.hey app's registration,profile list's endpoint, each of the four routes, the refusals of other verbs,--accountandHEY_ACCOUNT_ID, the shut door's exit 6, signed out's exit 3, the requiredhas_more,hey app call's read-only check,meta.endpoint, a mistyped or non-string endpoint, completion and the credential-freeprofile showon an app profile, and the Imbox rows without the server's empty fields..surfaceunchanged (make check-surface),hey commandslists noapp,profile listrows and server envelopes carry no endpoint,hey appis an unknown command.make checkgreen;GOOS=darwin,windows,linuxbuild.Alternatives
creatorvs. a door-onlysenderfield:creator {id, name}chosen so a script reads both endpoints alike; the id is the app's.hey profile create --endpoint app: left to the setup step that creates the app profile; the config is written by hand until then.hey auth status,hey doctorandhey config showlines for an app profile: refused or unchanged here, follow-ups with setup.box view sent|spam: the door serves no such list yet, so they are refused like any other box.--ids-onlyon the app's screener prints sender ids, the server's clearance ids;--jsonnames it inmeta.id_kind. Left as is: no command on the app takes either id until the writes land.XDG_RUNTIME_DIRunset and noHEY_LOCAL_SOCKETis exit 1 (usage: run from the desktop session), not exit 6: there is no door to call shut.