Show each pairing's thread-message permission and let it be revoked alone

Open
#20 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
35/100
Issue type
Feature
Clarity
Clearly specified
Activity status
Active
Tech stack
react, typescript
Domain
api, desktop, frontend

Research direction

Depends on #16, which moves the outbound rows first. Read RemoteConnectionsSection.tsx, the thread-bridge.ts API shapes, and the HTTP transport; start with lib/threadBridges.ts and its direction-helper tests. Then build ThreadMessagesPanel.tsx, wire row facts and actions, add English and Chinese strings, and run the named tests and visual-proof script; done means permission details and single-entry actions work, with the required desktop and touch-context phone screenshots.

Written by the indexing model from the issue text.

Description

enhancement

What feature would you like to see?

Pairing two spaces usually also sets up thread messages between them, so agents in one space can read, send to and wait on threads in the other. The GUI never shows this: a pairing row carries a "Thread messages" tag at most, with no direction, no scope, no operations and no expiry, and the only way to withdraw the thread-message permission without removing the whole pairing is the kiki bridges command. People cannot see, from the place where they manage pairings, what they have allowed.

Decided
  • "Thread messages" is a permission on a pairing, not a separate object; the word "bridge" does not appear in the GUI.
  • Every paired space's row on the Spaces page states, as read facts, whether it can be opened and which way thread messages flow.
  • The row menu gets "Thread messages…", which shows the scope and operations and can revoke the thread-message permission alone.
  • When the permission cannot be read, the row says so; it never shows "none" in its place.
  • This is the GUI's first use of the existing thread-bridge API; no protocol change.
Design

Row facts. Each row for a space on another computer (Settings → Spaces, section "Spaces on other computers", moved there by the settings-merge issue) gets a third line:

 ◍ Workstation B
   b.example.test · Connected · 1 minute ago
   Can be opened · Thread messages: both ways · 3 working · 2 waiting on you   [Switch] ⋯
 ◍ Writer space link
   writer.example.test · Offline
   Can't be opened · Thread messages: outgoing only                                  ⋯
  • Openable: "可打开 / Can be opened" when the record's purposes includes gui and the record is enabled; otherwise "不可打开 / Can't be opened".
  • Thread messages: "线程消息:双向 / 仅发出 / 仅接收 / 无" (en: "Thread messages: both ways / outgoing only / incoming only / none"); when the status call fails, "线程消息:读不到" (en: "Thread messages: can't read").
  • Direction is computed from GET /api/thread-bridges (klient rest.threadBridges.status()), which returns { identity, inboundEnabled, inbound: BridgeGrant[], outbound: BridgeLink[] }:
    • outgoing: an outbound link with connectionId === record.id, not revoked and enabled;
    • incoming: an inbound grant whose source identity (homeId, hostId) equals record.target, not revoked and enabled;
    • both → both ways; one → that one; neither → none.

"Thread messages…" panel (row menu item; desktop side panel, phone full-height sheet). One entry per thread-message permission of this pairing, each showing:

  • direction (this space → that space, or the reverse);
  • scope: which workspace (and, if set, which session) on each side; show this space's workspaces by name; for the other side show what the record says without raw identifiers;
  • operations in words: read 读取, send 发送, wait 等待, wake 唤醒 (starts a turn on the receiving side). A permission without wake leaves sent messages pending instead of starting a turn; say that in the panel when wake is absent;
  • expiry date, and whether it is on;
  • actions: "停用 / 启用" (Turn off / Turn on, setEnabled(direction, bridgeId, enabled)) and "撤销… / Revoke…" (revoke(direction, bridgeId)), each affecting only that entry. Revoking a thread-message permission does not remove the pairing; the row keeps "Can be opened".

Data access goes through a new apps/kiki-gui/src/lib/threadBridges.ts (query keys, a useThreadBridges(client) hook against this window's own home, and the direction helper), and the panel is its own component, apps/kiki-gui/src/components/settings/remote/ThreadMessagesPanel.tsx; the pairing-sheet issue reuses both for the Access page's incoming list. Read from this device's own home (the same control client the pairing list uses), never from the space on another computer.

Strings: add keys to both packages/session-core/src/i18n/en.ts and zh.ts. The zh copy above is fixed by the design; en copy follows its meaning.

Where
  • API: packages/klient/src/core/facade/thread-bridges.ts:4-12 (status, setEnabled, revoke, receipts, retry), HTTP transport packages/klient/src/transports/http/thread-bridges.ts, exposed as rest.threadBridges (packages/klient/src/transports/http/rest.ts:98).
  • Shapes: packages/protocol/src/rest/thread-bridge.ts (bridgePolicySchema with sourceScope, targetScope, operations, expiresAt; bridgeGrantSchema adds id, enabled, revoked; BridgeLink { connectionId, grant, enabled }; BridgeStatus).
  • Rows today: apps/kiki-gui/src/components/settings/RemoteConnectionsSection.tsx (RemoteConnectionRow and its menu at :151; list at :116-121).
  • Server behaviour to reflect: pairing issues read, send, wait by default (packages/kap-server/src/services/connections/admission.ts:7; packages/kap-server/src/services/threadBridge/bridge.ts:94-110); without wake a bridged message stays pending (bridge.ts:309); a reverse bridge can be set up for a paired connection (bridge.ts:111 ensureReversePeerBridge).
  • No GUI file uses threadBridges today (checked with rg threadBridges apps/kiki-gui/src).
Done when
  • The direction shown on each row matches the inbound and outbound entries the status call returns (unit test for the direction helper covering both ways, outgoing only, incoming only, none, revoked and disabled entries).
  • When the status call fails, every row shows "Thread messages: can't read" and no row shows "none" (test).
  • Revoking one thread-message permission calls revoke with that entry's direction and id only, the row's direction updates, and the pairing stays (test).
  • "Can't be opened" appears for a bridge-only record (purposes: ['bridge']) and that row has no "Switch" button.
  • Screenshots at desktop and phone width, light and dark, en and zh, with node apps/kiki-gui/scripts/visual-proof.mjs: the Spaces page with rows in each direction state and the "Thread messages…" panel. UI work is accepted only with these screenshots.
  • Phone-width screenshots are taken in a touch context (isMobile: true, hasTouch: true, as apps/kiki-gui/scripts/visual-proof-mobile-reading.mjs:294-300 does). Phone mode is (pointer: coarse) and (max-width: 639px) (apps/kiki-gui/src/lib/layoutHooks.ts:57), so a width-only --viewport narrow run of visual-proof.mjs shows the desktop layout squeezed, not the phone layout.

Additional information

Already in the tree: phase 1 corrected the Access page note to say that pairing with a code also allows thread messages (read, send, wait) when both sides have a workspace, and that the list does not show them yet (st.inbound.bridgeNote). Once this issue and the pairing-sheet issue land, that note is replaced by the facts on each row.

The GUI has never rendered real BridgeStatus data; look at the scope fields of a real pairing before fixing how the other side's workspace is worded.

Depends on the settings-merge issue, which moves the outbound rows to the Spaces page; both edit RemoteConnectionsSection.tsx, and the merge goes first. The pairing-sheet issue edits the Access page's incoming list and reuses lib/threadBridges.ts and ThreadMessagesPanel.tsx from this issue, so it goes after this one.

Order and scope. Paths and line numbers refer to the code after the pending GUI interaction and mobile pull request is merged into kiki.
Depends on: #16.
Main files: apps/kiki-gui/src/lib/threadBridges.ts (new), apps/kiki-gui/src/lib/threadBridges.test.ts (new), apps/kiki-gui/src/components/settings/remote/ThreadMessagesPanel.tsx (new), apps/kiki-gui/src/components/settings/RemoteConnectionsSection.tsx, apps/kiki-gui/src/components/settings/SpacesSection.test.tsx, packages/session-core/src/i18n/en.ts, packages/session-core/src/i18n/zh.ts.

Dominant language
TypeScript
Stars
54
Forks
5
Avg merge
2h 54m
Merged PRs (30d)
2

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from X-T-E-R/kiki

All issues in X-T-E-R/kiki

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.