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
2 changes: 2 additions & 0 deletions .agents/skills/dsh-dev/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ A saved AX model id may disappear from the pinned native catalog. Preserve the l

## Pitfall log

An AX launch pins `agent-default-model` through a command-line Patch. On DSH `0.2.1-rc.2`, changing that default through `onboardingModel` can therefore refuse with `Configuration for "agent-default-model" is overridden by a home patch or command-line overlay`. This is native configuration ownership, not a Remote Host transport failure. Preserve the refusal and unchanged default; verify preset writes separately. A no-op `home-model-set` does not prove the default can change. Qualify successful default changes on an unlocked isolated Profile, without editing another session's Patch or the shared AX selection (#2500).

Native approval `never` means reject before the answerer waterfall, even under `danger-full-access`; it does not mean automatic approval. In #2080, combining that policy with a BotHarness Human-only permission-management exception swallowed the intended card. Top-level Bot Full Access now uses `ask` and answers ordinary requests automatically at its existing application responder; delegated Subagents keep their native policy. Verify both an ordinary action and a permission-management Human card on the real Host.

On DSH `0.2.1-rc.2`, the first-run Preview Notice can hide a shell whose Settings entry now lives under the account menu. An older verification prelude that waits for a visible `Settings` button before dismissing overlays times out although Client diagnostics report `shell-ready`. Preserve the failure capture, dismiss the actual `Continue` dialog, then address the observed `Bot mode` or `Account menu` entry by accessible name. This qualifies shell navigation only; it does not establish a model reply or native IM delivery.
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/verify-deepseekbot/features/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

| Feature | Needs a model | Scripted journey | File |
| --- | --- | --- | --- |
| Browser Remote Host pairing, tabs and Host Feed | only for a reply | `verify-app` two-Host path | [remote-hosts.md](remote-hosts.md) |
| Browser Remote Host pairing, icon strip, Host Feed and Host-level settings | only for a reply | `verify-app` two-Host path | [remote-hosts.md](remote-hosts.md) |
| VRM companion Panel | yes for speaking | `scripts/e2e-vrm-companion.mjs` | [vrm-companion.md](vrm-companion.md) |
| Desktop whole-window zoom (controlled S1 / Windows candidate) | no | `scripts/e2e-desktop-zoom-launch.mjs`, `scripts/e2e-desktop-zoom-windows.mjs`, `scripts/e2e-dock-zoom.mjs`, `scripts/e2e-workspace-chrome.mjs` | [desktop-window-zoom.md](desktop-window-zoom.md) |
| Global search: Alt/Option+K, settings results and pinyin matching | no | `verify-app` Bot mode path | [global-search.md](global-search.md) |
Expand Down
5 changes: 5 additions & 0 deletions .agents/skills/verify-deepseekbot/features/remote-hosts.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ The browser pairs a Remote Host as a Paired Device and reads its Host Feed throu
- `remove`: removing a Host forgets its rows, stops its Inbox and releases cached avatar object URLs.
- `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.

## How to get to it (user POV)

Expand All @@ -33,6 +34,10 @@ For control races, delay an input response, queue text, Resume, then take contro

Keep the remote screen stationary during frame recovery. The Host must keep its sequence unchanged and answer matching `after` polls with 204 after ten seconds; the viewer recovers by fetching the current frame without that stale cursor, not by requiring a screen change. In both UI languages, inject a frame/input failure: the Web panel must show localized recovery guidance and a reconnect action, while the viewer snapshot retains the original diagnostic notice rather than rendering it as product copy.

For Remote settings, use two disposable Hosts with different home basenames and distinguishable model presets and chat data. Open Host B's icon menu in Host A's browser. Capture the menu and all three settings sections in light and dark. Change B's default model or a preset, then read both Hosts with `model-catalog` and `model-presets`; A must be unchanged. Compare Usage and Memory with B's `overview-usage` and `overview-memory`. In Storage, select a disposable chat, preview media or whole messages, verify Clear/Delete is disabled before acknowledgement, then confirm. Read B's storage and timeline and verify A's data is unchanged. Exercise both cleanup targets; use their matching CLI preview/confirm commands against B as well. Disconnect B and revoke its browser pairing in separate runs: settings must show an unavailable state or refusal, with no local data fallback or replayed write. Never clean up real conversations for this journey.

The default-model proof must change the model value on a Profile whose native settings owner can save it; a same-value no-op is insufficient. Keep a page's observed revision, change the default from another client, and verify the old page refuses without overwriting it. Explicit refresh must permit a subsequent save. Omit the catalog revision at the test transport boundary to verify the shared client refuses before any write; label that case as fault injection. For existing presets, verify Enter plus blur increments the revision once, a later blur saves independently, composition Enter does not submit, and a delayed write remains on its original Host/preset after closing and switching Hosts. Preserve the draft/error on a rejected save. New presets retain an explicit create action. Reject a remote storage preview token on the other Host, and verify neither cleanup command proceeds without its confirmation flag.

## Gotchas

- The workspace hides `data-panel-empty-action` descendants. Viewer reconnect must stay in its own controls; verify the button is visible and clickable inside the real workspace, not only in a standalone panel render.
Expand Down
24 changes: 24 additions & 0 deletions .github/pr-screenshots/2500-t8/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Remote Host settings evidence

Requested by Vain, issue #2500, PR #2540. Captured 2026-10-22 JST at 1440 × 900, English, light/dark, in disposable local Web Hosts. No Desktop, native phone or physical-machine tailnet qualification.

These captures run source `e04195b49ad952b4c5da5e391ee5c720609b25f7` on main `30f62e48c7d9b79a085a52df691af277d0fe6fe9`, published at `68003945df1b81fb61f1574535e00b7447ef75e8`. Later rebases integrate Activity, viewers and the Expo patch baseline; these images remain historical runtime evidence, not fresh captures of the integrated head. `bl.png` / `bd.png` retain the original pre-feature menu baseline; new settings pages use that absence state as their predecessor.

| Files | Observed result |
| ------------ | ---------------------------------------------------------------------------------------------------- |
| al/ad | Remote settings added beside Change icon and Remove |
| ml/md | Source Host default genuinely changed from Flash to Pro; native revision 1 → 2; local Host unchanged |
| pl/pd | Existing preset name saved on Enter/blur; composition Enter does not submit |
| st | Another client changed revision 2 → 3; the old page refused without overwriting |
| mr | Test transport omitted catalog revision; shared client refused before sending a write |
| ul/ud | Source Host usage and Memory activity |
| sl/sd, mc/xc | Source Host storage, media/message previews and explicit confirmation |
| ol/od | Offline settings content unmounted without local fallback |
| rl/rd | Revoked pairing unmounted settings content |
| xl/xd | Removing the only Remote Host forgot its browser record and hid the strip |

The source Profile retained a provider-only AX Patch so the native default-model settings owner could persist changes. No owner or revision check was bypassed. CLI also performed a real Pro → Flash change, rejected stale revisions, rejected cleanup without `--confirm`, and rejected the source Host's preview token on the other Host. The browser's delayed preset write stayed on its original Host after closing and switching to This host.

The fresh live-model attempt selected only `deepseek-v4-pro` and received `429 GoUsageLimitError: Go usage limit exceeded`. No successful new reply is claimed, and no retry was made. Existing task messages visible in the background are older fixtures.

At that source version, full build, typecheck, lint, format, docs build, 119 related tests and 7 artifact/docs tests passed. Browser diagnostics showed no page exceptions; expected request cancellations and disconnect failures are retained in private task evidence. Task Hosts were stopped and their ports released after QA. Native iOS/Android light/dark/large-text checks remain an external handoff for the shared phone model-settings change. The integrated Expo baseline requires fresh native development builds in the coordinated device lane; source tests do not qualify existing device binaries. Native acceptance is a unified follow-up, not a separate merge gate under Vain's updated direction.
Binary file added .github/pr-screenshots/2500-t8/ad.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/al.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/bd.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/bl.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/mc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/md.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/ml.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/mr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/od.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/ol.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/pd.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/pl.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/rd.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/rl.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/sd.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/sl.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/st.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/ud.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/ul.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/xc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/xd.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-screenshots/2500-t8/xl.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions apps/mobile/src/host/bot-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface ModelPreset {

export interface BotModel {
revision: number;
defaultRevision?: number;
plan?: ModelPlan;
repair?: string;
models: ModelCatalogEntry[];
Expand Down Expand Up @@ -251,8 +252,14 @@ export async function loadGlobalModel(client: HostClient): Promise<BotModel> {
const models = record(catalog)?.['models'];
if (!Array.isArray(models)) throw new HostAnswerInvalid('modelCatalog returned no models.');
const fallback = route(record(catalog)?.['default']);
const defaultRevision = record(catalog)?.['defaultRevision'];
return {
revision: 0,
...(typeof defaultRevision === 'number' &&
Number.isSafeInteger(defaultRevision) &&
defaultRevision >= 0
? { defaultRevision }
: {}),
models: some(models, catalogEntry),
presets: some(record(presets)?.['presets'], preset),
...(fallback === undefined ? {} : { fallback }),
Expand Down
13 changes: 12 additions & 1 deletion apps/mobile/src/host/model-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,18 @@ export async function setModelRoute(
throw new HostAnswerInvalid('MODEL_UNAVAILABLE');
if (scope.kind === 'global') {
if (field !== 'orchestrator') throw new HostAnswerInvalid('GLOBAL_MODEL_REQUIRED');
await client.call('onboardingModel', { expectedRevision: 0, route, globalDefault: true });
if (
state.defaultRevision === undefined ||
!Number.isSafeInteger(state.defaultRevision) ||
state.defaultRevision < 0
)
throw new HostAnswerInvalid('MODEL_DEFAULT_REVISION_REQUIRED');
await client.call('onboardingModel', {
expectedRevision: 0,
expectedDefaultRevision: state.defaultRevision,
route,
globalDefault: true,
});
return;
}
const previous = modelRoutes(state);
Expand Down
17 changes: 12 additions & 5 deletions apps/mobile/test/model-settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function setup() {
credential: fake.credential,
carrier: fake.carrier,
});
fake.methods.set('modelCatalog', () => ({ models, default: flash }));
fake.methods.set('modelCatalog', () => ({ models, default: flash, defaultRevision: 5 }));
fake.methods.set('modelPresets', () => ({ presets: [preset] }));
fake.methods.set('modelPlan', () => ({ revision: 4, plan }));
return { fake, client };
Expand Down Expand Up @@ -108,10 +108,17 @@ describe('native Model Plans and Presets', () => {
it('writes the global default without a Bot slug or any Bot override mutation', async () => {
const { fake, client } = setup();
fake.methods.set('onboardingModel', () => ({ revision: 0 }));
await setModelRoute(client, { kind: 'global' }, state, 'orchestrator', pro);
expect(fake.calls.map(({ method, args }) => [method, args])).toEqual([
['onboardingModel', { expectedRevision: 0, globalDefault: true, route: pro }],
]);
const observed = await modelSettingsResource(client, { kind: 'global' }).refresh();
await setModelRoute(client, { kind: 'global' }, observed, 'orchestrator', pro);
expect(fake.calls.at(-1)).toMatchObject({
method: 'onboardingModel',
args: {
expectedRevision: 0,
expectedDefaultRevision: 5,
globalDefault: true,
route: pro,
},
});
});

it('customizes only the Bot Orchestrator and uses the observed revision', async () => {
Expand Down
13 changes: 13 additions & 0 deletions changes/2500-remote-settings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
section: Added
---

## en

- Open a Remote Host's settings from its icon menu to change its default model and model presets, inspect usage and Memory activity, and preview and confirm storage cleanup on that Host ([#2500](https://github.com/BotHarness/DeepSeekBot/issues/2500)).
- Remote Host and phone default model changes preserve the observed Host revision and refuse stale or missing versions. Existing remote preset names save on Enter or blur; creating a preset remains explicit ([#2500](https://github.com/BotHarness/DeepSeekBot/issues/2500)).

## zh

- 从远程图标菜单打开设置,修改该 Host 的默认模型和模型预设、查看用量与 Memory 活动,并预览和确认清理该 Host 的存储空间([#2500](https://github.com/BotHarness/DeepSeekBot/issues/2500))。
- 远程 Host 与手机修改默认模型时保留读取到的 Host 版本,拒绝过期或缺失版本的写入。已有远程预设名称按 Enter 或失焦保存;新建预设仍需明确创建([#2500](https://github.com/BotHarness/DeepSeekBot/issues/2500))。
2 changes: 2 additions & 0 deletions docs/architecture/botharness-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,8 @@ The application-defined browser `RemoteHosts` owner keeps its pairings and selec

Activity Center consumes each Remote Host's shared Inbox through `RemoteHosts.activity`, reusing the existing `HumanInboxView` for Host groups. Its Host-scoped source supplies identity, pagination, source navigation and commands; it never receives This Host's `BridgeActions`. `createHostActivityActions` adapts the shared attention and request operations to the existing approval and question cards. Each operation resolves the current pairing before calling its Host client, and confirmed changes refresh that Host's Inbox. Filtered observers share the Host client and stop with their last subscriber. This Host still reads its existing bridge/store until that Host Feed slice migrates. Device Notification Preference remains a subsequent device-owned slice; the browser has no closed-page notification path.

Remote Host settings resolve the selected Host's existing client from `RemoteHosts`. The browser reuses `apps/mobile/src/host/model-settings`, `usage` and `storage` for default models, presets, usage/Memory activity and cleanup; the existing Web settings, model picker, charts and destructive confirmation controls present those operations. Settings unmount their content when the source is disconnected or removed. Model resources are keyed by client and scope; there is no This Host fallback, Bot-profile link or automatic write replay. Cleanup retains the owning Host's preview token, scope and explicit confirmation. Host looks remain device-local and independent of these Host-owned settings.

### Version-bound Memory image previews (#2214, first Web slice)

The application-defined Memory Service exposes bounded PNG representations alongside its existing current-file and Git-diff queries. Metadata identifies worktree/index/exact reachable commit plus a SHA-256 byte revision; authenticated image transfer rechecks that owner, repository selector, path and revision before returning `image/png` with no-store semantics. It does not accept arbitrary blob IDs, revision expressions or Host paths. Current reads retain path containment, control-directory exclusion and safe-open checks; Git reads accept regular-file blobs only. A changed mutable source returns an explicit stale refusal rather than current bytes under an old version label.
Expand Down
4 changes: 4 additions & 0 deletions docs/bot-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ For a blank bot, `--core-memory <text>` or `--core-memory-stdin` writes the root

## Live Host commands

### Remote Host settings

The browser's Remote Host icon menu → **Remote settings** uses the same Host owners as these online commands. Select the source Host with `--host <origin>` and its private `--token-file <file>` (or the existing environment variables); never point a remote-setting command at the serving Host by default. `model-catalog`, `home-model-set`, `model-presets`, `model-preset-create` and `model-preset-update` cover models; `overview-usage` and `overview-memory` cover usage and Memory activity; `storage-usage` and both storage cleanup preview/confirm pairs cover storage. Destructive confirmations still require `--confirmation-stdin --confirm`, using the preview from that same Host. An unavailable or refusing Host never falls back to a local profile. These commands do not read browser pairing secrets or change device-local Host looks.

Set `DEEPSEEKBOT_HOST` to the running Host's loopback origin, or a tailnet HTTPS origin. A tailnet origin (for example `tailscale serve` in front of a loopback Host) works only when the Host was started with `--trusted-host <machine>.<tailnet>.ts.net`; otherwise login succeeds but DSH's `/api` Host fence refuses every request and the CLI returns `host-untrusted`. Supply its current launch token through `DEEPSEEKBOT_HOST_TOKEN` or `--token-file <private-file>`, never a token argument. `--host <origin>` overrides the environment. Login happens per invocation, cookies remain in memory, and restarting the Host requires its new token. Live commands never open the profile database. See [ADR-0159](adr/0159-live-cli-verbs-ride-the-dsh-http-carrier.md).

### Online creation and headless debugging
Expand Down
4 changes: 4 additions & 0 deletions docs/bot-cli.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ deepseekbot search <words>

## 在线 Host 命令

### 远程设置

浏览器远程图标菜单中的「远程设置」与以下在线命令使用同一个 Host 所有者。通过 `--host <origin>` 和私有的 `--token-file <file>`(或已有环境变量)指定来源 Host,不要默认指向提供当前网页的本机。模型使用 `model-catalog`、`home-model-set`、`model-presets`、`model-preset-create` 和 `model-preset-update`;用量与 Memory 活动使用 `overview-usage`、`overview-memory`;存储使用 `storage-usage` 及两组清理预览/确认命令。删除仍须传入 `--confirmation-stdin --confirm`,且预览必须来自同一 Host。连接失败或被拒绝时,不会改用本地 Profile。这些命令不读取浏览器的配对密钥,也不修改本设备保存的 Host 外观。

将 `DEEPSEEKBOT_HOST` 设为运行中 Host 的 loopback origin 或 tailnet HTTPS origin。使用 tailnet origin(例如 loopback Host 前面的 `tailscale serve`)时,Host 必须以 `--trusted-host <machine>.<tailnet>.ts.net` 启动;否则登录能成功,但 DSH 的 `/api` Host 校验会拒绝每个请求,CLI 返回 `host-untrusted`。`--host <origin>` 可覆盖它。当前启动令牌通过 `DEEPSEEKBOT_HOST_TOKEN` 或 `--token-file <私有文件>` 提供,不得把令牌值放入参数。每次调用重新登录,cookie 只留在内存;Host 重启后须使用新令牌。在线命令不打开 Profile 数据库。参见 [ADR-0159](adr/0159-live-cli-verbs-ride-the-dsh-http-carrier.md)。

### 在线创建与无浏览器调试
Expand Down
Loading
Loading