A profile whose endpoint is the HEY app answers the reads from it

JE
15h ago
+3023-51

Context

  • An agent on this computer reaches HEY only through HEY's server, with the person's full sign-in.
  • #640 reached the HEY Linux app's door ($XDG_RUNTIME_DIR/hey/agent.sock, open while Settings › General › "Let Agents Use HEY" is on), but through a hey local command tree of its own.
  • The design that replaces it: the endpoint is a field of a profile (#644), the ordinary hey commands answer from the app when the profile says so, behind HEY_EXPERIMENTAL=app, the default stays the server, no auto-switching.
  • This is the first of three: reads here; writes, sent/waiting and exit 9 next; setup and the skill after.

Change

  • A profile may name "endpoint": "app" (or "server", the default); an app profile needs no base_url. Any other value is a config error, never read as the server.
  • With HEY_EXPERIMENTAL=app, an app profile is answered by the app's door: hey box view imbox (imbox.list), hey screener list and hey screener history (screener.list), hey draft list (drafts.list).
  • Answers keep the server's field names (postings with topic_id, creator, seen; email_address; status approved/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.
  • Every envelope, errors included, names meta.endpoint (app or server) in an experimental build, beside meta.profile.
  • On an app profile: no credential is read, migrated or signed in; no account scope; --account, HEY_ACCOUNT_ID, a profile's own and a repository's account_id are 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 mcp and hey auth/login/logout in their own words; another box, --page likewise; bare hey prints help, no wizard.
  • A shut door is exit 6, "Open HEY … turn on Let Agents Use HEY", never HEY's server instead; the app signed out is exit 3; an answer missing what a listing must have is exit 7.
  • Hidden 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 list and profile show name the endpoint under the gate.
  • internal/localapp comes from #640 at fcf918b unchanged.
  • Without the gate an app profile is refused at selection ("not in this build", exit 1), the design's choice over a quiet fallback; help, .surface and the docs are unchanged.

Verification

  • internal/cmd/app_endpoint_test.go (21 tests) and two in 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 behaviour shown red with its code removed, 30 of 30: the gate's check, hey app's registration, profile list's endpoint, each of the four routes, the refusals of other verbs, --account and HEY_ACCOUNT_ID, the shut door's exit 6, signed out's exit 3, the required has_more, hey app call's read-only check, meta.endpoint, a mistyped or non-string endpoint, completion and the credential-free profile show on an app profile, and the Imbox rows without the server's empty fields.
  • Without the gate: .surface unchanged (make check-surface), hey commands lists no app, profile list rows and server envelopes carry no endpoint, hey app is an unknown command.
  • A fresh review in its own context: no High or Medium; its four Lows fixed in 7241ab2 (completion, no keyring read, narrower Imbox rows, non-string endpoint).
  • Bot review (cubic, greptile, Copilot): 26 threads: 21 fixed, 5 declined on 4 points with reasons in their threads (summary comment); fixes in bc9a34e, 4225008, bd7d6e4, cfd5879, each red without its fix; a second fresh review of the fixes found no High or Medium.
  • make check green; GOOS=darwin, windows, linux build.
  • Unrun: a check against a running HEY Linux app, since none had the door open on thelio.

Alternatives

  • Falling back to the server when the gate is off or the door is shut: rejected, it answers with the person's full sign-in where they chose the app.
  • Mapping the door's sender to creator vs. a door-only sender field: 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 doctor and hey config show lines 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-only on the app's screener prints sender ids, the server's clearance ids; --json names it in meta.id_kind. Left as is: no command on the app takes either id until the writes land.
  • XDG_RUNTIME_DIR unset and no HEY_LOCAL_SOCKET is exit 1 (usage: run from the desktop session), not exit 6: there is no door to call shut.