Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .agents/skills/dsh-dev/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,4 +416,15 @@ it look newly qualified.

The launcher and `verify-app` share a temporary cookie jar named from `basename(DSH_HOME)`. Two isolated homes named `home` in different parent folders overwrite that jar, so the second launch can make the first Host's doctor return 401 while its own browser still works. Give concurrent homes distinct basenames (`host-a`, `host-b`), preserve the first refusal and verify each Host separately. Do not treat this verification-cookie collision as a product authentication failure.

The qualified workspace retains a hidden sidebar copy beside the visible Channels panel. The two Add remote buttons can have the same accessible name. A first-match selector can click or fill the hidden copy; choose visible elements as the two-Host journey requires, and wait for the submit button to become enabled after input. The real Windows Web baseline paired successfully after the driver selected visible controls and used keyboard input; no product change was required.
The qualified workspace retains a hidden sidebar copy beside the visible Channels panel. The two Add remote buttons can have the same accessible name. A first-match selector can click or fill the hidden copy; choose visible elements as the two-Host journey requires, and wait for the submit button to become enabled after input. The real Windows Web baseline paired successfully after the driver selected visible controls and used keyboard input; no product change was required.

### Connect route provenance in pinned DSH 0.2.1-rc.2 (#2541)

Arbitrary HTTP route headers do not enter Typert invocation context, and cookie-authenticated
browser calls share a Peer. The `connection/request` waterfall does not admit `remote.mux`
opens. A header-only or unary-only guard therefore cannot restrict all Connect calls.
BotHarness stamps its cloudflared-only origin with a process-local capability and owns both
unary admission and a bounded mux adapter at the Device Gateway, before native dispatch.
The shared client refuses before serializing secret arguments: a Host refusal cannot remove
plaintext already sent through Cloudflare. Verify HTTP, mux and ordinary streams against the
pinned Host; this adapter is not encryption or an upstream DSH API extension.
2 changes: 2 additions & 0 deletions .agents/skills/verify-deepseekbot/features/phone-pairing.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ A Human pairs the phone app with their Host by a one-time QR code, and the phone

## Sub-features

- `full-client-removed`: no settings/chat/Inbox/routine/create action opens the full Host WebView; old `/full-client` deep links cannot load it. Private input directs the Human to the Host or Tailscale browser.

- `offer` creates a one-time pairing offer (5 minutes) with a QR code and a `deepseekbot://pair?p=…` link.
- `pair` redeems the offer once; a second redeem answers `401 offer-invalid`.
- `link-errors` words a refusal by how the phone got the offer: a pasted or opened link that is broken, expired or already used says the link is wrong, and a pasted one empties the field; a scan keeps the code wording.
Expand Down
4 changes: 2 additions & 2 deletions .agents/skills/verify-deepseekbot/features/phone-routines.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A paired phone runs, pauses, resumes, deletes, creates and edits a PersonaBot's
- `run-now` calls `scheduleRunNow`; the manual firing shows under Recent runs.
- `pause` calls `scheduleUpdate` with `enabled`; the button flips between Pause and Resume.
- `delete` asks in place, then calls `scheduleDelete`.
- `author` uses a nested Edit or New routine page inside that same drawer, and creates (`scheduleCreate`) or edits (`scheduleUpdate`) a daily, weekly or every-N-hours routine, previewing the next runs with `schedulePreview`. Minute, once and cron triggers say the phone cannot edit them and open the full client.
- `author` uses a nested Edit or New routine page inside that same drawer, and creates (`scheduleCreate`) or edits (`scheduleUpdate`) a daily, weekly or every-N-hours routine, previewing the next runs with `schedulePreview`. Minute, once and cron triggers say the phone cannot edit them; no full-client action is available.

## How to get to it (user POV)

Expand All @@ -18,7 +18,7 @@ A paired phone runs, pauses, resumes, deletes, creates and edits a PersonaBot's

Preconditions: no model credential is needed.

- **Scripted journey.** `node scripts/e2e-mobile-routines-web.mjs [--skip-export]` exports the app for web, pairs it through a phone-origin proxy, creates a daily and a cron routine through the API, then checks Pause and Resume on the Host, Run now in the Host history, Delete after confirming, the cron routine's full-client note, and a new weekly routine whose preview matches the Host's `schedulePreview`, in light and dark. Evidence lands in `.humanlayer/verify/mobile-routines-web/evidence/`.
- **Scripted journey.** `node scripts/e2e-mobile-routines-web.mjs [--skip-export]` exports the app for web, pairs it through a phone-origin proxy, creates a daily and a cron routine through the API, then checks Pause and Resume on the Host, Run now in the Host history, Delete after confirming, the cron routine's read-only note, and a new weekly routine whose preview matches the Host's `schedulePreview`, in light and dark. Evidence lands in `.humanlayer/verify/mobile-routines-web/evidence/`.

## Gotchas

Expand Down
3 changes: 3 additions & 0 deletions .agents/skills/verify-deepseekbot/features/remote-hosts.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ The browser pairs a Remote Host as a Paired Device and reads its Host Feed throu
- `message-body`: remote images, files, rich text and request cards use the existing message body, with authenticated resources and decisions on the source Host.
- `upgrade`: existing browser pairings and the selected tab survive the storage-key rename.
- `remove`: removing a Host forgets its rows, stops its Inbox and releases cached avatar object URLs.
- `secret-route`: Connect private input shows a bilingual trusted-route notice without secret fields. Cancellation remains available; the shared Host client refuses before serialization. Loopback/Tailscale retain submission.
- `viewers`: a remote Bot DM opens Browser or Computer when that Bot has access. The shared Host viewer modules read frames and send input to that Host only; closing, switching source or losing the connection tears down the viewer and releases requested control.
- `viewer-control`: queued input belongs to one takeover. Resume or frame loss invalidates it; frame recovery remains read-only even when the Host Feed never disconnected. Remove closes viewers and waits up to five seconds for takeover/release cleanup before revoking the pairing.
- `settings`: right-click a Remote Host icon → Remote settings opens that Host's models, usage and storage. Default model and preset changes use its shared Host client. Usage includes Memory activity, without a link to a This Host Bot page. Storage retains preview, acknowledgement and destructive confirmation.
- `notifications`: right-click a Remote Host icon and choose Remote notifications; enable browser permission explicitly and choose Tool approvals, Questions, Private input requests, Mentions & replies. Choices survive reload in this browser only. Verify the initial Inbox and reconnect backlog are silent, a new permitted Host attention item alerts once, disabled kinds stay silent and removing the Host closes its alerts. Private input text is never shown. Capture the new entry and dialog in light/dark; page-open API evidence does not qualify closed-page push or Desktop.

Discovery uses the Host's notification query, not the Activity display window. Keep more than 30 old mentions and produce a new ordinary DM reply and a non-mention group reply; both must alert once when enabled. Traverse `deepseekbot inbox --notifications` cursors and compare the Host-decided items. Verify stable same-timestamp pagination, a live invalidation during paging, a partial-page failure followed by recovery, and no historical replay. A refused Notification constructor records a bounded redacted `remote-notification-show-*` diagnostic through the existing Client diagnostics surface. Settings apply immediately and close through the normal close control.


## How to get to it (user POV)

Enter Bot mode, then Messages. Open the channel header's add menu and choose Add remote. Paste a link from the other Host's Settings → Devices → Pair a phone. Choose All or the Remote Host icon, then a Bot or group row. Right-click the Host icon and choose Remove to remove it. A remote Bot DM with Browser or Computer access shows the corresponding header button. Open its viewer, choose Pause Bot and take control, interact with the screen or send text, then Resume Bot or Close viewer. Groups have no viewer entry.
Expand Down Expand Up @@ -55,6 +57,7 @@ For message bodies, upload an image and a text file to Host B through its attach
- Control release uses the same device credential. An unreachable Host is retried when this viewer reconnects or reopens; a revoked credential cannot resume a Browser. In that case the Host operator resumes it. The Computer also has its existing Host-side pause lease.
- Dismiss Continue and Skip onboarding overlays before selecting Messages. Retain any failed drive evidence before retrying.

For #2541, pair a Connect Host and open a pending private input card. Confirm the notice in both themes, absence of password fields and working Cancel. Verify marked unary and mux calls return `secret-route-refused`, and ordinary Host Feed traffic still works. The removed `/botharness-device/web/` route must return 404. Use synthetic secrets only; a model quota failure blocks live card creation and must remain an explicit QA gap.
Activation race regression: while replies are disabled, write 35 old mentions and keep a notification page pending, then enable replies. The switch/CLI write commits only after a complete fresh baseline; old mentions stay silent. After success, send a new group reply and ordinary DM (including tied timestamps in automated fixtures): each alerts once. Fail a page and retry, disable/re-enable, change Host, close settings, revoke permission and dispose while activation waits; failed/cancelled operations cannot commit later. Preserve the original 37-alert failing trace separately from any subsequent pass.

CLI deadline regression: let the real appearance relay refuse at eight seconds, then complete the notification baseline at nine seconds. Browser preferences and persisted storage must remain unchanged, and no late acknowledgement is sent. Repeat after control unmount; a pre-deadline successful write remains operable. Change the app locale while the DOM language is unchanged and verify private-input notification copy follows the bound translator without exposing the input.
Expand Down
21 changes: 21 additions & 0 deletions .github/pr-screenshots/2541/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Connect secret safety evidence

Captured 2026-10-22 for #2541; all names and credential values are synthetic.

- Before: `5210bf2a9` (includes the exact #2533 dependency `0f4b87633105`). The baseline copies of the three pictured components were checked byte-for-byte against that revision.
- After: Track A plus main `30f62e48c7d9`, integrated at `c2c21f80c`, with the routine Edit accessible label restored.
- Settings and routine pairs: Expo Web, 390 × 844 CSS pixels, scale 3, English, light/dark; real isolated DSH 0.2.1-rc.2 and Device Gateway. These are not native-device screenshots.
- Private-input pairs: cropped real Web Client remote card, 1280 × 800 browser viewport, English, light/dark. A synthetic Connect hostname maps to the actual local Connect origin; no real Cloudflare deployment or Tailscale connection is exercised. The pending card has one password field before, zero after; submit is disabled after.
- Routine capture waits for the empty history to settle. Daily edit, pause/resume, manual run/history, confirmed deletion, weekly creation and preview match were driven against the owning Host; no page errors were recorded.

## Verification

17 focused files / 173 tests passed after main integration: gateway, route provenance, refusal policy, HostClient preflight, remote cards/read state, Browser viewer registration and changed phone screens. Root/mobile type checks, lint, format and build passed.

A rebuilt native DSH Host refused all eight marked endpoints over unary and mux at the Connect origin, allowed roster reads/streams and loopback credential set/unset, and returned 404 for the removed full-client path. CLI `ask-secret-submit --values-stdin` accepted a synthetic value and the canonical card status became configured. The model-created card used `deepseek-official/deepseek-flash` (catalog: DeepSeek-V4.1-Flash); this is not a claim of a completed DM reply while the Bot awaited input.

The earlier broad local suite did not finish and is not green: source-policy timeout, docs public-surface build failure and a daylight assertion were observed. The daylight assertion also fails on the baseline. Focused tests do not replace the full CI plan.

## Pending acceptance

Native iOS/Android, old full-client deep links, chat/create/Activity entrances and native Browser/Computer regression captures remain pending. The shared Mac device queue is paused by the Human for combined acceptance. The runnable review path is Settings, chat overflow/private input, Activity private-input request, create menu, daily and advanced routines, followed by an old `/full-client` link. Use both themes. Actual Cloudflare/Tailscale, Human QA and the complete proposed ADR-0244 approval remain separate gates. No encryption, merge or release is claimed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions apps/mobile/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ The shell is set by [ADR-0211](../../docs/adr/0211-the-mobile-app-has-two-tabs-w
- **+** opens a modal action panel in the island position, with close in the circular position. Back, accessibility escape and backdrop only dismiss it. The panel is always a scrollable vertical menu: each full-width row has a leading icon and left-aligned wrapping label. Target choices use the same row treatment. On iOS 26 the panel uses a native rounded-rectangle glass backdrop, including at larger text sizes; Reduce Transparency and older iOS keep an opaque raised surface. This panel opt-in does not change the existing multiline drawer-title fallback. The panel still uses the existing modal fade, not a native glass morph transition.
- On Android, creation is a Material 3 **FAB** on Chats only: 56dp, 16dp corners, `canvas.chosen` (primary container), level-3 elevation, 16dp from the trailing edge and 16dp above the navigation bar. It opens a **FAB menu** (m3.material.io FAB menu tokens, `fab`): the FAB becomes a 56dp circular close button in the accent colour, and the actions stack above it, trailing-aligned, as full-pill items 56dp tall, 4dp apart, hugging their icon and label with 24dp side padding. Target choices and Back use the same items under a notice; errors show in that notice. The items scroll behind the close button when space runs out. Back, accessibility escape and the scrim dismiss it. Expo UI 58 has no Compose FAB menu, so it is drawn to the token values.
- Computer: new Channel/new Bot. Owner Organization: new Channel/invite. All: new Channel/new Bot/join with link or QR, followed by explicit destination choice where needed. Member/guest workspaces do not gain owner controls. Pairing and Organization administration stay in the drawer.
- Creation binds the chosen Host ID or cloud account scope plus Organization ID. Cloud rechecks current identity and ownership before opening/submitting; stale scope refuses. Existing Host full-client and CloudClient idempotent mutations remain the business owners. Dismissal never starts a background operation.
- Creation binds the chosen Host ID or cloud account scope plus Organization ID. Cloud rechecks current identity and ownership before opening/submitting; stale scope refuses. Computer creation is unavailable on the phone after full WebView removal; CloudClient idempotent mutations remain the cloud business owner. Dismissal never starts a background operation.

- **Chats** shows the current Host:
- Conversation rows keep the title and timestamp on one horizontal line at every system text size. The title takes the remaining width and truncates with tail ellipsis; the timestamp keeps its own single-line slot. Preview/status and count stay on a second line. A conversation without a timestamp does not reserve an empty metadata line. Preserve native font scaling and the complete accessible title/preview.
Expand All @@ -116,7 +116,7 @@ The shell is set by [ADR-0211](../../docs/adr/0211-the-mobile-app-has-two-tabs-w
- Three equal-width tabs each center the icon and label as one group. Only the selected tab shows its decorative leading icon. Its underline fills that entire third of the strip without outer gutters and follows the page's swipe position. Use `react-native-tab-view` with Expo-compatible native `react-native-pager-view`. Rows own horizontal swipes in the feed, so the pager's own swipe is off and a horizontal swipe on the tab strip moves to the next or previous tab; keep each page's virtualized list mounted after its first visit so its vertical position survives switching. Previous Activity collapse preferences do not hide content. Labels retain native system scaling and wrap within their third at large text. Switching pages preserves search, sort, Bot and conversation filters. All/handled filters temporarily show that category in the current page without a selected top tab; selecting or swiping to another tab resumes its category. Tabs remain selectable during loading or error. Respect reduced motion for programmatic transitions.
- Bound list summary previews and their accessibility labels before passing them to native Text; a two-line preview must not measure an entire long reply. Keep the original item for opening its conversation. Loaded action-card ports keep their identity across page re-renders so returning to a page retains its loaded content and draft instead of flashing a loading card.
- Keep all three decorative tab icons mounted. Derive their opacity, subtle scale and icon/label group translation from the same continuous native pager position as the underline, so the incoming icon appears during a drag and reverses when the drag is cancelled. At rest, the selected icon/label pair is centered; inactive labels are centered alone. Avoid selection-triggered mount animations and per-frame React updates. Reduced motion uses settled icon states without the morph transform.
- Approvals, questions and folder requests can be decided in place, on the same cards as chat. Auto-rejected calls (Allow once), group join requests (Accept / Decline) and Assignment asks (reply that carries the Assignment target) open a native detail with the same card. Private input retains its full-client route: its chat card and Inbox detail open the full client at that request.
- Approvals, questions and folder requests can be decided in place, on the same cards as chat. Auto-rejected calls (Allow once), group join requests (Accept / Decline) and Assignment asks (reply that carries the Assignment target) open a native detail with the same card. Private input cards and Inbox details explain that entry requires the Host client or a browser over Tailscale. There is no full Host WebView or in-app fallback.
- Posts and rows are separated by space and layer fills, not divider lines.
- Activity is cleared one item at a time, like an email inbox. Swiping a row or post left reveals its actions, as in Mail; swiping through the row runs the first one, with a haptic tick as it arms. A tap elsewhere on an open row closes it, and opening another row closes the first. Long-press opens the same actions in a native menu, and screen readers get them as accessibility actions. Rightward drags stay with the screen.
- **Needs you**: a pending request is never cleared, since the Bot would keep waiting. Its actions are **Tomorrow** (snooze until 9:00 tomorrow, the swipe-through action) and **Later…** (1 hour, this evening, tomorrow or next week). The item returns on its own at that time. The one exception is an **interrupted message** (the Bot stopped partway through a Human's message after it had started acting, so the Host will not replay it). Nothing resolves it on its own, so its actions are **Clear** (the swipe-through action, with Undo) and **Later…**; its detail page explains what happened and offers Open conversation and Clear.
Expand Down
Loading
Loading