dsh-remote-bridge 2.5.1 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  openapi: 3.1.0
2
2
  info:
3
3
  title: DeepSeek Harness Mobile API
4
- version: 2.5.1
4
+ version: 2.6.0
5
5
  description: Versioned file and chat BFF for mobile and third-party DSH clients. HTTP and HTTPS are both supported.
6
6
  servers:
7
7
  - url: http://127.0.0.1:3090/api/v1
@@ -24,7 +24,7 @@ paths:
24
24
  properties:
25
25
  ok: { type: boolean, const: true }
26
26
  version: { type: string, const: v1 }
27
- pluginVersion: { type: string, const: 2.5.1 }
27
+ pluginVersion: { type: string, const: 2.6.0 }
28
28
  /debug/host-services:
29
29
  get:
30
30
  operationId: hostServices
@@ -39,8 +39,14 @@ paths:
39
39
  `observed` covers services confirmed present in the web profile but not yet
40
40
  consumed (currently only `workingDirectory`, added by DSH 0.2.1).
41
41
 
42
- Requires a device token or loopback admin, like every other authenticated
43
- endpoint: it exposes host internals.
42
+ Requires a device token or loopback admin **with the `settings.read`
43
+ scope** (a loopback admin inherits all scopes): it exposes host
44
+ internals. Since 2.5.2 the endpoint sits behind authentication like
45
+ every other `/api/v1` route (SEC-H1); it previously returned before the
46
+ principal was resolved. The response may additionally carry a
47
+ best-effort `metrics` object with runtime diagnostics; it is omitted in
48
+ builds without the metrics collector (and a missing probe still yields
49
+ `404`, not `500`).
44
50
  responses:
45
51
  '200':
46
52
  description: Injection probe snapshot taken while the plugin applied.
@@ -79,6 +85,14 @@ paths:
79
85
  injectedAvailable: { type: integer }
80
86
  injectedMissing: { type: array, items: { type: string } }
81
87
  observedAvailable: { type: array, items: { type: string } }
88
+ metrics:
89
+ type: object
90
+ description: |
91
+ Optional runtime diagnostics attached to the same
92
+ `settings.read`-gated report (no separate route). Omitted
93
+ when the build has no metrics collector. Shape is
94
+ diagnostic and additive.
95
+ additionalProperties: true
82
96
  '404':
83
97
  description: The probe is not compiled into this build.
84
98
  /pairings/exchange:
@@ -134,6 +148,32 @@ paths:
134
148
  type: array
135
149
  items: { $ref: '#/components/schemas/Root' }
136
150
  '403': { $ref: '#/components/responses/Error' }
151
+ /roots/resolve:
152
+ get:
153
+ operationId: resolveAbsolutePath
154
+ summary: Map a host absolute path to its authorized root
155
+ description: |
156
+ Chat events carry server-absolute paths while `GET /roots` deliberately
157
+ omits root absolute paths, so clients cannot perform this mapping
158
+ themselves. Resolves `path` against the device's authorized roots using
159
+ realpath-normalized longest-prefix matching, then stat-checks the
160
+ target (rejecting symlinks inside the path) via the same machinery as
161
+ file content reads. The response never contains an absolute host path.
162
+ Requires `files.read`.
163
+ parameters:
164
+ - name: path
165
+ in: query
166
+ required: true
167
+ description: Absolute host path. Relative paths are rejected rather than guessed.
168
+ schema: { type: string, minLength: 1 }
169
+ responses:
170
+ '200':
171
+ description: Root match plus wire-relative metadata for the target.
172
+ content:
173
+ application/json:
174
+ schema: { $ref: '#/components/schemas/ResolvedPath' }
175
+ '400': { $ref: '#/components/responses/Error' }
176
+ '404': { $ref: '#/components/responses/Error' }
137
177
  /roots/{rootId}/entries:
138
178
  parameters:
139
179
  - $ref: '#/components/parameters/RootId'
@@ -271,6 +311,74 @@ paths:
271
311
  '200':
272
312
  description: Item restored without overwriting an existing path.
273
313
  '409': { $ref: '#/components/responses/Error' }
314
+ /image-handoff:
315
+ get:
316
+ operationId: readImageHandoff
317
+ summary: Fetch one handoff image for a chat session
318
+ description: |
319
+ Returns the raw bytes of an image the agent handed off (content-addressed
320
+ `sha256.<ext>` files written under the session working directory's
321
+ image-handoff subdirectory). `name` must be a bare file name only: the
322
+ directory is always derived by the host from the session working
323
+ directory, so no directory or absolute path can be supplied. Requires
324
+ `files.read` plus ownership of the session; "no such session" and "no
325
+ such file" are indistinguishable (both are 404). This authenticated REST
326
+ route is the device-side counterpart of the token-less browser route
327
+ `GET /plugin/dsh-remote-bridge/raw`.
328
+ parameters:
329
+ - name: sessionId
330
+ in: query
331
+ required: true
332
+ schema: { type: string }
333
+ - name: name
334
+ in: query
335
+ required: true
336
+ description: Bare handoff file name, e.g. `a1b2…e9.png`.
337
+ schema: { type: string, pattern: '^[0-9a-f]{64}\.[A-Za-z0-9]+$' }
338
+ responses:
339
+ '200':
340
+ description: Image bytes; handoff files are temporary artifacts, so the response is private and must be revalidated.
341
+ headers:
342
+ Cache-Control: { schema: { type: string }, description: private, max-age=0, must-revalidate }
343
+ content:
344
+ application/octet-stream:
345
+ schema: { type: string, contentEncoding: binary }
346
+ '400': { $ref: '#/components/responses/Error' }
347
+ '404': { $ref: '#/components/responses/Error' }
348
+ /attachments/{attachmentId}:
349
+ get:
350
+ operationId: readAttachment
351
+ summary: Fetch one session attachment's bytes
352
+ description: |
353
+ Returns the raw bytes of an image attachment referenced by a session
354
+ event. The attachment store is content-addressed, so `attachmentId`
355
+ contains a colon (typically `sha256:<hex>`) and **must be
356
+ percent-encoded in the URL** (`sha256%3A<hex>`). `sessionId` is a
357
+ required query parameter: the host verifies the session actually
358
+ references this attachment and validates bytes/media type against the
359
+ real reference stored in the session event. Requires `files.read` plus
360
+ ownership of the session. Bytes are immutable and may be cached
361
+ privately for a year.
362
+ parameters:
363
+ - name: attachmentId
364
+ in: path
365
+ required: true
366
+ description: Content-addressed attachment id, e.g. `sha256%3A<hex>` (the raw id is `sha256:<hex>` and must be percent-encoded).
367
+ schema: { type: string }
368
+ - name: sessionId
369
+ in: query
370
+ required: true
371
+ schema: { type: string }
372
+ responses:
373
+ '200':
374
+ description: Attachment bytes; immutable and privately cacheable.
375
+ headers:
376
+ Cache-Control: { schema: { type: string }, description: private, max-age=31536000, immutable }
377
+ content:
378
+ application/octet-stream:
379
+ schema: { type: string, contentEncoding: binary }
380
+ '400': { $ref: '#/components/responses/Error' }
381
+ '404': { $ref: '#/components/responses/Error' }
274
382
  /chat/workspaces:
275
383
  get:
276
384
  operationId: listChatWorkspaces
@@ -471,6 +579,68 @@ paths:
471
579
  content:
472
580
  application/json:
473
581
  schema: { $ref: '#/components/schemas/ModelCatalog' }
582
+ /settings/namespaces:
583
+ get:
584
+ operationId: listSettingsNamespaces
585
+ summary: Read every DSH plugin settings namespace
586
+ description: |
587
+ Returns the host's per-plugin settings document as one entry per namespace, so a
588
+ mobile client can build a form from `schema` without a dedicated endpoint per
589
+ plugin. Requires `settings.read`.
590
+
591
+ The top-level `writable` mirrors the host settings document. Each entry's
592
+ `writable` is that value AND the bridge's write policy: namespaces starting with
593
+ `ui-` or ending in `locale` are writable by default; high-risk ones (`llm-*`,
594
+ `permission`, `agent-default-model`, `subagent-*`, …) are read-only unless the
595
+ deployment explicitly allows them through `settingsWriteNamespaces`. When an
596
+ entry is not writable, `reason` says why: `HOST_READONLY` (the whole settings
597
+ document is read-only) or `HIGH_RISK_NAMESPACE` (write policy).
598
+
599
+ Secret values never leave the host: the kernel already redacts fields declared as
600
+ secrets, and the bridge additionally masks string values whose key matches
601
+ `apiKey|secret|token|password|credential|apiKeyEnv` with `***`.
602
+ responses:
603
+ '200':
604
+ description: All namespaces, with secret values redacted and per-namespace writability.
605
+ content:
606
+ application/json:
607
+ schema: { $ref: '#/components/schemas/SettingsNamespaces' }
608
+ '403': { $ref: '#/components/responses/Error' }
609
+ /settings/namespaces/{ns}:
610
+ parameters:
611
+ - name: ns
612
+ in: path
613
+ required: true
614
+ schema: { type: string }
615
+ description: Namespace id (a plugin name such as `ui-chat`).
616
+ patch:
617
+ operationId: mutateSettingsNamespace
618
+ description: |
619
+ Applies path-addressed `set` / `unset` operations to one namespace, mirroring the
620
+ kernel's `settings.mutate`. Requires `settings.write`; at most 32 operations per
621
+ request. A successful mutation is audited as `settings.namespace.mutate` with the
622
+ touched field paths only — never the values.
623
+
624
+ Only namespaces allowed by the write policy can be changed; others answer
625
+ 403 `SETTINGS_NAMESPACE_READONLY`. `expectedRevision` is optimistic concurrency:
626
+ a mismatch answers 409 `SETTINGS_CONFLICT`; an invalid ops body answers
627
+ 400 `SETTINGS_OPS_INVALID`; a missing host settings service answers
628
+ 503 `SETTINGS_UNAVAILABLE`.
629
+ requestBody:
630
+ required: true
631
+ content:
632
+ application/json:
633
+ schema: { $ref: '#/components/schemas/SettingsNamespaceOps' }
634
+ responses:
635
+ '200':
636
+ description: The mutated namespace, in the same shape as one entry of `SettingsNamespaces.namespaces`.
637
+ content:
638
+ application/json:
639
+ schema: { $ref: '#/components/schemas/SettingsNamespaceSummary' }
640
+ '400': { $ref: '#/components/responses/Error' }
641
+ '403': { $ref: '#/components/responses/Error' }
642
+ '409': { $ref: '#/components/responses/Error' }
643
+ '503': { $ref: '#/components/responses/Error' }
474
644
  /settings/providers/{providerId}:
475
645
  parameters:
476
646
  - name: providerId
@@ -784,6 +954,45 @@ paths:
784
954
  responses:
785
955
  '202': { description: Prompt accepted by DSH. }
786
956
  '409': { $ref: '#/components/responses/Error' }
957
+ /chat/sessions/{sessionId}/attachments:
958
+ parameters:
959
+ - name: sessionId
960
+ in: path
961
+ required: true
962
+ schema: { type: string }
963
+ post:
964
+ operationId: uploadSessionAttachment
965
+ summary: Upload one attachment and receive a receipt
966
+ description: |
967
+ Uploads one attachment as base64 text and returns the receipt a later
968
+ message can reference. Kernel file-type content parts require this
969
+ `receiptId` (obtained through an upload); only image content parts may
970
+ be inlined as base64 directly in a message. Requires `chat.write` plus
971
+ ownership of the session. The request body is limited to 24 MiB.
972
+ requestBody:
973
+ required: true
974
+ content:
975
+ application/json:
976
+ schema:
977
+ type: object
978
+ required: [data]
979
+ properties:
980
+ data:
981
+ type: string
982
+ contentEncoding: base64
983
+ minLength: 1
984
+ description: Base64-encoded attachment bytes.
985
+ name:
986
+ type: string
987
+ description: Optional original file name; trimmed by the server.
988
+ responses:
989
+ '201':
990
+ description: Stored; the receipt id is referenced by later message file parts.
991
+ content:
992
+ application/json:
993
+ schema: { $ref: '#/components/schemas/AttachmentReceipt' }
994
+ '400': { $ref: '#/components/responses/Error' }
995
+ '404': { $ref: '#/components/responses/Error' }
787
996
  /chat/sessions/{sessionId}/approvals:
788
997
  get:
789
998
  operationId: listPendingApprovals
@@ -994,6 +1203,24 @@ paths:
994
1203
  schema: { $ref: '#/components/schemas/SessionModels' }
995
1204
  '404': { $ref: '#/components/responses/Error' }
996
1205
  /chat/sessions/{sessionId}/model:
1206
+ get:
1207
+ operationId: getSessionModelAlias
1208
+ description: |
1209
+ Alias of `GET /chat/sessions/{sessionId}/models`; the server route
1210
+ `model(?:s)?$` answers GET on both spellings. New clients should call
1211
+ `/models`.
1212
+ parameters:
1213
+ - name: sessionId
1214
+ in: path
1215
+ required: true
1216
+ schema: { type: string }
1217
+ responses:
1218
+ '200':
1219
+ description: Current selection and models routable for this DSH session.
1220
+ content:
1221
+ application/json:
1222
+ schema: { $ref: '#/components/schemas/SessionModels' }
1223
+ '404': { $ref: '#/components/responses/Error' }
997
1224
  put:
998
1225
  operationId: selectSessionModel
999
1226
  parameters:
@@ -1155,6 +1382,24 @@ components:
1155
1382
  id: { type: string, format: uuid }
1156
1383
  label: { type: string }
1157
1384
  createdAt: { type: integer }
1385
+ ResolvedPath:
1386
+ type: object
1387
+ description: Result of mapping an absolute host path into an authorized root; the root's absolute path itself is never included.
1388
+ required: [rootId, path, kind, size, modifiedAt, contentType]
1389
+ properties:
1390
+ rootId: { type: string, format: uuid }
1391
+ path: { $ref: '#/components/schemas/WirePath' }
1392
+ kind: { type: string, enum: [file, directory] }
1393
+ size: { type: integer, nullable: true }
1394
+ modifiedAt: { type: integer, nullable: true }
1395
+ contentType: { type: string, nullable: true }
1396
+ AttachmentReceipt:
1397
+ type: object
1398
+ description: Upload receipt referenced later as a file-type content part in a message.
1399
+ required: [receiptId, name]
1400
+ properties:
1401
+ receiptId: { type: string }
1402
+ name: { type: string, nullable: true }
1158
1403
  Device:
1159
1404
  type: object
1160
1405
  required: [id, name, scopes, rootIds]
@@ -1441,6 +1686,83 @@ components:
1441
1686
  providers:
1442
1687
  type: array
1443
1688
  items: { $ref: '#/components/schemas/Provider' }
1689
+ SettingsNamespaceSecret:
1690
+ type: object
1691
+ description: One secret slot reported by the host. Only its path and whether it is set are exposed, never the value.
1692
+ required: [path, set]
1693
+ properties:
1694
+ path:
1695
+ type: array
1696
+ items: { type: string }
1697
+ set: { type: boolean }
1698
+ SettingsNamespaceSummary:
1699
+ type: object
1700
+ description: One DSH settings namespace as seen by a remote client, with secret values redacted.
1701
+ required: [ns, revision, value, writable]
1702
+ properties:
1703
+ ns: { type: string, examples: [ui-chat, ui-theme] }
1704
+ revision: { type: integer, minimum: 0 }
1705
+ schema:
1706
+ type: object
1707
+ additionalProperties: true
1708
+ description: Kernel schema tree for form generation; its shape is kernel-defined.
1709
+ value:
1710
+ description: Effective value (base merged with user overrides), recursively redacted.
1711
+ base:
1712
+ description: Built-in default value, recursively redacted.
1713
+ user:
1714
+ description: User overrides only, recursively redacted.
1715
+ autoGenerate:
1716
+ type: object
1717
+ additionalProperties: true
1718
+ description: Kernel-reported fields that are generated automatically.
1719
+ applies:
1720
+ type: object
1721
+ additionalProperties: true
1722
+ description: Kernel-reported applicability conditions for this namespace.
1723
+ secrets:
1724
+ type: array
1725
+ items: { $ref: '#/components/schemas/SettingsNamespaceSecret' }
1726
+ writable:
1727
+ type: boolean
1728
+ description: The host document is writable AND this namespace passes the bridge's write policy.
1729
+ reason:
1730
+ type: string
1731
+ enum: [HOST_READONLY, HIGH_RISK_NAMESPACE]
1732
+ description: Present only when `writable` is false.
1733
+ SettingsNamespaces:
1734
+ type: object
1735
+ required: [writable, namespaces]
1736
+ properties:
1737
+ writable:
1738
+ type: boolean
1739
+ description: Whether the host settings document itself is writable.
1740
+ namespaces:
1741
+ type: array
1742
+ items: { $ref: '#/components/schemas/SettingsNamespaceSummary' }
1743
+ SettingsNamespaceOps:
1744
+ type: object
1745
+ description: Path-addressed operations and optional optimistic-concurrency revision.
1746
+ required: [ops]
1747
+ properties:
1748
+ ops:
1749
+ type: array
1750
+ minItems: 1
1751
+ maxItems: 32
1752
+ items:
1753
+ oneOf:
1754
+ - type: object
1755
+ required: [op, path, value]
1756
+ properties:
1757
+ op: { type: string, const: set }
1758
+ path: { type: array, items: { type: string } }
1759
+ value: {}
1760
+ - type: object
1761
+ required: [op, path]
1762
+ properties:
1763
+ op: { type: string, const: unset }
1764
+ path: { type: array, items: { type: string } }
1765
+ expectedRevision: { type: integer, minimum: 0 }
1444
1766
  ProviderPatch:
1445
1767
  type: object
1446
1768
  properties: