dsh-remote-bridge 2.7.1 → 2.8.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.
- package/README.en.md +2 -2
- package/README.md +2 -2
- package/docs/api/asyncapi.yaml +1 -1
- package/docs/api/openapi.yaml +484 -2
- package/lib/index.d.ts +219 -3
- package/lib/index.js +5798 -1210
- package/package.json +2 -2
package/README.en.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<img alt="Version" src="https://img.shields.io/badge/version-v2.
|
|
12
|
+
<img alt="Version" src="https://img.shields.io/badge/version-v2.8.0-087f8c">
|
|
13
13
|
<img alt="DSH plugin" src="https://img.shields.io/badge/DeepSeek_Harness-plugin-1f2328">
|
|
14
14
|
<img alt="Platforms" src="https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-586069">
|
|
15
15
|
<img alt="License" src="https://img.shields.io/badge/license-AGPL--3.0-2da44e">
|
|
@@ -402,7 +402,7 @@ inside the zero-change set, and 4 slots retained with 0 removed.
|
|
|
402
402
|
|
|
403
403
|
## Project
|
|
404
404
|
|
|
405
|
-
- Version: `v2.
|
|
405
|
+
- Version: `v2.8.0`
|
|
406
406
|
- **v2.7.1 is a defect-fix release**: the kernel's `sessionController.resolveAgent()` returns an
|
|
407
407
|
`{ agent }` / `{ error }` **envelope**, not the Agent itself (`dsh-api-session-controller/lib/index.js:365-368`
|
|
408
408
|
`liveAgent()` and `:221-246` `resolve()` both have that shape). The bridge used to hand the envelope
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<img alt="Version" src="https://img.shields.io/badge/version-v2.
|
|
12
|
+
<img alt="Version" src="https://img.shields.io/badge/version-v2.8.0-087f8c">
|
|
13
13
|
<img alt="DSH plugin" src="https://img.shields.io/badge/DeepSeek_Harness-plugin-1f2328">
|
|
14
14
|
<img alt="Platforms" src="https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-586069">
|
|
15
15
|
<img alt="License" src="https://img.shields.io/badge/license-AGPL--3.0-2da44e">
|
|
@@ -433,7 +433,7 @@ DSH **0.2.1-rc.3** 隔离实例上**实跑验过**,不是静态推断:插件
|
|
|
433
433
|
|
|
434
434
|
## 项目信息
|
|
435
435
|
|
|
436
|
-
- 当前版本:`v2.
|
|
436
|
+
- 当前版本:`v2.8.0`
|
|
437
437
|
- **v2.7.1 是缺陷修复版**:内核 `sessionController.resolveAgent()` 返回的是 `{ agent }` / `{ error }`
|
|
438
438
|
**包装对象**而非 Agent 本体(`dsh-api-session-controller/lib/index.js:365-368` 的 `liveAgent()` 与
|
|
439
439
|
`:221-246` 的 `resolve()` 都是这个形状),桥此前把包装对象直接当 Agent 传给 `fileUploads.upload()`,
|
package/docs/api/asyncapi.yaml
CHANGED
package/docs/api/openapi.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
openapi: 3.1.0
|
|
2
2
|
info:
|
|
3
3
|
title: DeepSeek Harness Mobile API
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.8.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.
|
|
27
|
+
pluginVersion: { type: string, const: 2.8.0 }
|
|
28
28
|
/debug/host-services:
|
|
29
29
|
get:
|
|
30
30
|
operationId: hostServices
|
|
@@ -1319,6 +1319,332 @@ paths:
|
|
|
1319
1319
|
schema: { type: string }
|
|
1320
1320
|
responses:
|
|
1321
1321
|
'202': { description: Cancellation accepted. }
|
|
1322
|
+
/chat/sessions/{sessionId}/attachments/uploads:
|
|
1323
|
+
post:
|
|
1324
|
+
operationId: openChunkedAttachmentUpload
|
|
1325
|
+
description: |
|
|
1326
|
+
Opens — or resumes — a chunked attachment upload for one DSH session
|
|
1327
|
+
(`docs/project/BFF-REST-CONTRACT.md` §1).
|
|
1328
|
+
|
|
1329
|
+
`chunkSize` is chosen by the server and must be obeyed by the client.
|
|
1330
|
+
Re-posting the same `name` + `size` while an upload is still alive returns the
|
|
1331
|
+
**same** `uploadId` plus `alreadyReceived` (the chunk indexes the server already
|
|
1332
|
+
holds), which is how a dropped connection resumes without re-sending bytes.
|
|
1333
|
+
Tickets expire one hour after opening; an expired or unknown id answers 404.
|
|
1334
|
+
|
|
1335
|
+
Errors: `400 BODY_INVALID` (bad name/size/contentType), `404 NOT_FOUND` (unknown
|
|
1336
|
+
session), `413 PAYLOAD_TOO_LARGE` (`size` above the 256 MiB ceiling).
|
|
1337
|
+
parameters:
|
|
1338
|
+
- name: sessionId
|
|
1339
|
+
in: path
|
|
1340
|
+
required: true
|
|
1341
|
+
schema: { type: string }
|
|
1342
|
+
requestBody:
|
|
1343
|
+
required: true
|
|
1344
|
+
content:
|
|
1345
|
+
application/json:
|
|
1346
|
+
schema: { $ref: '#/components/schemas/ChunkedUploadOpenRequest' }
|
|
1347
|
+
responses:
|
|
1348
|
+
'201':
|
|
1349
|
+
description: Upload ticket opened (or reused).
|
|
1350
|
+
content:
|
|
1351
|
+
application/json:
|
|
1352
|
+
schema: { $ref: '#/components/schemas/ChunkedUploadTicket' }
|
|
1353
|
+
'400': { $ref: '#/components/responses/Error' }
|
|
1354
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
1355
|
+
'413': { $ref: '#/components/responses/Error' }
|
|
1356
|
+
get:
|
|
1357
|
+
operationId: listChunkedAttachmentUploads
|
|
1358
|
+
description: |
|
|
1359
|
+
Lists the session's unfinished upload tickets so a cold-started client can
|
|
1360
|
+
resume without re-opening. Expired tickets are omitted; `missing` on each item
|
|
1361
|
+
is the same chunk-index list `complete` would report.
|
|
1362
|
+
parameters:
|
|
1363
|
+
- name: sessionId
|
|
1364
|
+
in: path
|
|
1365
|
+
required: true
|
|
1366
|
+
schema: { type: string }
|
|
1367
|
+
responses:
|
|
1368
|
+
'200':
|
|
1369
|
+
description: Unfinished uploads for this session, soonest expiry first.
|
|
1370
|
+
content:
|
|
1371
|
+
application/json:
|
|
1372
|
+
schema:
|
|
1373
|
+
type: object
|
|
1374
|
+
required: [items]
|
|
1375
|
+
properties:
|
|
1376
|
+
items:
|
|
1377
|
+
type: array
|
|
1378
|
+
items: { $ref: '#/components/schemas/ChunkedUploadStatus' }
|
|
1379
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
1380
|
+
/chat/sessions/{sessionId}/attachments/uploads/{uploadId}:
|
|
1381
|
+
get:
|
|
1382
|
+
operationId: getChunkedAttachmentUpload
|
|
1383
|
+
description: |
|
|
1384
|
+
Upload progress. `missing` is the authoritative retry list — the same array
|
|
1385
|
+
`POST …/complete` returns in `details.missing` when chunks are absent.
|
|
1386
|
+
Unknown, expired, or other-session ids all answer `404 NOT_FOUND`
|
|
1387
|
+
(indistinguishable on purpose).
|
|
1388
|
+
parameters:
|
|
1389
|
+
- name: sessionId
|
|
1390
|
+
in: path
|
|
1391
|
+
required: true
|
|
1392
|
+
schema: { type: string }
|
|
1393
|
+
- name: uploadId
|
|
1394
|
+
in: path
|
|
1395
|
+
required: true
|
|
1396
|
+
schema: { type: string }
|
|
1397
|
+
responses:
|
|
1398
|
+
'200':
|
|
1399
|
+
description: Current ticket state.
|
|
1400
|
+
content:
|
|
1401
|
+
application/json:
|
|
1402
|
+
schema: { $ref: '#/components/schemas/ChunkedUploadStatus' }
|
|
1403
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
1404
|
+
delete:
|
|
1405
|
+
operationId: cancelChunkedAttachmentUpload
|
|
1406
|
+
description: Discards the ticket and its spooled chunks. The id is dead afterwards (404).
|
|
1407
|
+
parameters:
|
|
1408
|
+
- name: sessionId
|
|
1409
|
+
in: path
|
|
1410
|
+
required: true
|
|
1411
|
+
schema: { type: string }
|
|
1412
|
+
- name: uploadId
|
|
1413
|
+
in: path
|
|
1414
|
+
required: true
|
|
1415
|
+
schema: { type: string }
|
|
1416
|
+
responses:
|
|
1417
|
+
'204': { description: 'Ticket discarded; no body.' }
|
|
1418
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
1419
|
+
/chat/sessions/{sessionId}/attachments/uploads/{uploadId}/chunks/{index}:
|
|
1420
|
+
put:
|
|
1421
|
+
operationId: putChunkedAttachmentChunk
|
|
1422
|
+
description: |
|
|
1423
|
+
Stores one chunk. `index` is zero-based; repeating a `PUT` on the same index is
|
|
1424
|
+
idempotent (the chunk is overwritten), which makes retries safe.
|
|
1425
|
+
|
|
1426
|
+
Byte length must be **exact**: `chunkSize` for every chunk but the last, and
|
|
1427
|
+
`size - index * chunkSize` for the last one. A body longer than `chunkSize`
|
|
1428
|
+
answers `413 PAYLOAD_TOO_LARGE`; a wrong-but-not-oversized length answers
|
|
1429
|
+
`400 BODY_INVALID`; an index outside `[0, count)` answers `400 BODY_INVALID`.
|
|
1430
|
+
The body is `application/octet-stream`, not JSON and not base64.
|
|
1431
|
+
parameters:
|
|
1432
|
+
- name: sessionId
|
|
1433
|
+
in: path
|
|
1434
|
+
required: true
|
|
1435
|
+
schema: { type: string }
|
|
1436
|
+
- name: uploadId
|
|
1437
|
+
in: path
|
|
1438
|
+
required: true
|
|
1439
|
+
schema: { type: string }
|
|
1440
|
+
- name: index
|
|
1441
|
+
in: path
|
|
1442
|
+
required: true
|
|
1443
|
+
schema: { type: integer, minimum: 0 }
|
|
1444
|
+
requestBody:
|
|
1445
|
+
required: true
|
|
1446
|
+
content:
|
|
1447
|
+
application/octet-stream:
|
|
1448
|
+
schema: { type: string, format: binary }
|
|
1449
|
+
responses:
|
|
1450
|
+
'200':
|
|
1451
|
+
description: Chunk stored.
|
|
1452
|
+
content:
|
|
1453
|
+
application/json:
|
|
1454
|
+
schema: { $ref: '#/components/schemas/ChunkedUploadChunkResult' }
|
|
1455
|
+
'400': { $ref: '#/components/responses/Error' }
|
|
1456
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
1457
|
+
'413': { $ref: '#/components/responses/Error' }
|
|
1458
|
+
/chat/sessions/{sessionId}/attachments/uploads/{uploadId}/complete:
|
|
1459
|
+
post:
|
|
1460
|
+
operationId: completeChunkedAttachmentUpload
|
|
1461
|
+
description: |
|
|
1462
|
+
Assembles the chunks and hands the bytes to the same kernel attachment path the
|
|
1463
|
+
single-shot `POST /chat/sessions/{sessionId}/attachments` uses, so the resulting
|
|
1464
|
+
receipt has the same meaning. On success the ticket is void (a second `complete`
|
|
1465
|
+
answers 404).
|
|
1466
|
+
|
|
1467
|
+
Missing chunks answer `409 CONFLICT` with `details.missing` = the chunk indexes
|
|
1468
|
+
still owed, so the client can send exactly those and retry.
|
|
1469
|
+
parameters:
|
|
1470
|
+
- name: sessionId
|
|
1471
|
+
in: path
|
|
1472
|
+
required: true
|
|
1473
|
+
schema: { type: string }
|
|
1474
|
+
- name: uploadId
|
|
1475
|
+
in: path
|
|
1476
|
+
required: true
|
|
1477
|
+
schema: { type: string }
|
|
1478
|
+
responses:
|
|
1479
|
+
'201':
|
|
1480
|
+
description: Attachment stored in the session.
|
|
1481
|
+
content:
|
|
1482
|
+
application/json:
|
|
1483
|
+
schema: { $ref: '#/components/schemas/ChunkedUploadReceipt' }
|
|
1484
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
1485
|
+
'409': { $ref: '#/components/responses/Error' }
|
|
1486
|
+
/chat/schedules:
|
|
1487
|
+
get:
|
|
1488
|
+
operationId: listSchedules
|
|
1489
|
+
description: |
|
|
1490
|
+
Lists cron-shaped schedules (`docs/project/BFF-REST-CONTRACT.md` §2).
|
|
1491
|
+
|
|
1492
|
+
Every item is a **cron projection** of the kernel record: `daily`/`weekly`
|
|
1493
|
+
records are converted to their equivalent five-field cron; one-shot (`at`) and
|
|
1494
|
+
fixed-interval (`every`) records cannot be expressed that way and are therefore
|
|
1495
|
+
omitted rather than faked. `createdAt` is always `null` because the kernel record
|
|
1496
|
+
carries no creation timestamp.
|
|
1497
|
+
parameters:
|
|
1498
|
+
- name: sessionId
|
|
1499
|
+
in: query
|
|
1500
|
+
required: false
|
|
1501
|
+
schema: { type: string }
|
|
1502
|
+
responses:
|
|
1503
|
+
'200':
|
|
1504
|
+
description: Schedules, optionally filtered to one session.
|
|
1505
|
+
content:
|
|
1506
|
+
application/json:
|
|
1507
|
+
schema:
|
|
1508
|
+
type: object
|
|
1509
|
+
required: [items]
|
|
1510
|
+
properties:
|
|
1511
|
+
items:
|
|
1512
|
+
type: array
|
|
1513
|
+
items: { $ref: '#/components/schemas/Schedule' }
|
|
1514
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
1515
|
+
'503': { $ref: '#/components/responses/Error' }
|
|
1516
|
+
post:
|
|
1517
|
+
operationId: createSchedule
|
|
1518
|
+
description: |
|
|
1519
|
+
Creates a cron schedule for one DSH session.
|
|
1520
|
+
|
|
1521
|
+
`cron` must be a five-field Vixie expression; `timezone` must be `UTC` or a real
|
|
1522
|
+
IANA `Area/Location` name — anything else is `400 BODY_INVALID` before the kernel
|
|
1523
|
+
is involved. An unknown session is `404 NOT_FOUND`.
|
|
1524
|
+
|
|
1525
|
+
`503 UPSTREAM_UNAVAILABLE` (with `details.reason`) means the kernel does not
|
|
1526
|
+
expose schedule creation to the bridge at all — the client must hide the entry
|
|
1527
|
+
point then, not retry.
|
|
1528
|
+
requestBody:
|
|
1529
|
+
required: true
|
|
1530
|
+
content:
|
|
1531
|
+
application/json:
|
|
1532
|
+
schema: { $ref: '#/components/schemas/ScheduleCreateRequest' }
|
|
1533
|
+
responses:
|
|
1534
|
+
'201':
|
|
1535
|
+
description: Schedule created.
|
|
1536
|
+
content:
|
|
1537
|
+
application/json:
|
|
1538
|
+
schema: { $ref: '#/components/schemas/Schedule' }
|
|
1539
|
+
'400': { $ref: '#/components/responses/Error' }
|
|
1540
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
1541
|
+
'503': { $ref: '#/components/responses/Error' }
|
|
1542
|
+
/chat/schedules/{id}:
|
|
1543
|
+
patch:
|
|
1544
|
+
operationId: updateSchedule
|
|
1545
|
+
description: |
|
|
1546
|
+
Updates only the fields present in the body.
|
|
1547
|
+
|
|
1548
|
+
`enabled: false` (pause) answers `400 BODY_INVALID` with
|
|
1549
|
+
`details.reason: ENABLED_UNSUPPORTED` — this kernel has no pause operation, and a
|
|
1550
|
+
silent success would show a paused reminder that keeps firing. Deleting is the
|
|
1551
|
+
supported way to stop one. `enabled: true` on an already-ended schedule answers
|
|
1552
|
+
`409 CONFLICT` (`details.reason: SCHEDULE_ENDED`).
|
|
1553
|
+
parameters:
|
|
1554
|
+
- name: id
|
|
1555
|
+
in: path
|
|
1556
|
+
required: true
|
|
1557
|
+
schema: { type: string }
|
|
1558
|
+
requestBody:
|
|
1559
|
+
required: true
|
|
1560
|
+
content:
|
|
1561
|
+
application/json:
|
|
1562
|
+
schema: { $ref: '#/components/schemas/SchedulePatch' }
|
|
1563
|
+
responses:
|
|
1564
|
+
'200':
|
|
1565
|
+
description: Updated schedule.
|
|
1566
|
+
content:
|
|
1567
|
+
application/json:
|
|
1568
|
+
schema: { $ref: '#/components/schemas/Schedule' }
|
|
1569
|
+
'400': { $ref: '#/components/responses/Error' }
|
|
1570
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
1571
|
+
'409': { $ref: '#/components/responses/Error' }
|
|
1572
|
+
'503': { $ref: '#/components/responses/Error' }
|
|
1573
|
+
delete:
|
|
1574
|
+
operationId: deleteSchedule
|
|
1575
|
+
description: Removes the schedule. Unknown id answers 404.
|
|
1576
|
+
parameters:
|
|
1577
|
+
- name: id
|
|
1578
|
+
in: path
|
|
1579
|
+
required: true
|
|
1580
|
+
schema: { type: string }
|
|
1581
|
+
responses:
|
|
1582
|
+
'204': { description: 'Deleted; no body.' }
|
|
1583
|
+
'404': { $ref: '#/components/responses/Error' }
|
|
1584
|
+
'503': { $ref: '#/components/responses/Error' }
|
|
1585
|
+
/chat/speech/catalog:
|
|
1586
|
+
get:
|
|
1587
|
+
operationId: getSpeechCatalog
|
|
1588
|
+
description: |
|
|
1589
|
+
Official speech input catalog (`docs/project/BFF-REST-CONTRACT.md` §3).
|
|
1590
|
+
|
|
1591
|
+
When the kernel has no speech service this is **not** a 404: it answers `200`
|
|
1592
|
+
with `available: false`, an empty `providers` and a `reason`. That is the same
|
|
1593
|
+
judgement the `/ws/mobile` `speech-catalog` frame makes, and it lets an old
|
|
1594
|
+
client hide the entry point without treating a missing feature as a failure.
|
|
1595
|
+
responses:
|
|
1596
|
+
'200':
|
|
1597
|
+
description: Catalog, or the unavailable view.
|
|
1598
|
+
content:
|
|
1599
|
+
application/json:
|
|
1600
|
+
schema: { $ref: '#/components/schemas/SpeechCatalog' }
|
|
1601
|
+
/chat/speech/transcriptions:
|
|
1602
|
+
post:
|
|
1603
|
+
operationId: createSpeechTranscription
|
|
1604
|
+
description: |
|
|
1605
|
+
Transcribes one base64 WAV clip.
|
|
1606
|
+
|
|
1607
|
+
Decoded audio must be ≤ 4 MiB — the same ceiling `/ws/mobile` enforces, so a clip
|
|
1608
|
+
does not become acceptable merely by taking the REST path; over the limit answers
|
|
1609
|
+
`413 PAYLOAD_TOO_LARGE`. Transcription gives up after 180 s. An empty transcript
|
|
1610
|
+
is a **success** (`text: ""`), not an error: silence is a legal answer.
|
|
1611
|
+
|
|
1612
|
+
Upstream failures answer `503 UPSTREAM_UNAVAILABLE` with `details.reason`; the
|
|
1613
|
+
kernel's raw message is never echoed (it can contain host paths).
|
|
1614
|
+
requestBody:
|
|
1615
|
+
required: true
|
|
1616
|
+
content:
|
|
1617
|
+
application/json:
|
|
1618
|
+
schema: { $ref: '#/components/schemas/SpeechTranscriptionRequest' }
|
|
1619
|
+
responses:
|
|
1620
|
+
'200':
|
|
1621
|
+
description: Transcript (possibly empty).
|
|
1622
|
+
content:
|
|
1623
|
+
application/json:
|
|
1624
|
+
schema: { $ref: '#/components/schemas/SpeechTranscription' }
|
|
1625
|
+
'400': { $ref: '#/components/responses/Error' }
|
|
1626
|
+
'413': { $ref: '#/components/responses/Error' }
|
|
1627
|
+
'503': { $ref: '#/components/responses/Error' }
|
|
1628
|
+
/chat/speech/config:
|
|
1629
|
+
put:
|
|
1630
|
+
operationId: configureSpeech
|
|
1631
|
+
description: |
|
|
1632
|
+
Changes the selected provider and/or language and returns the catalog view
|
|
1633
|
+
afterwards. At least one of `providerId`/`language` is required
|
|
1634
|
+
(`400 BODY_INVALID` otherwise).
|
|
1635
|
+
requestBody:
|
|
1636
|
+
required: true
|
|
1637
|
+
content:
|
|
1638
|
+
application/json:
|
|
1639
|
+
schema: { $ref: '#/components/schemas/SpeechConfigPatch' }
|
|
1640
|
+
responses:
|
|
1641
|
+
'200':
|
|
1642
|
+
description: Catalog after the change.
|
|
1643
|
+
content:
|
|
1644
|
+
application/json:
|
|
1645
|
+
schema: { $ref: '#/components/schemas/SpeechCatalog' }
|
|
1646
|
+
'400': { $ref: '#/components/responses/Error' }
|
|
1647
|
+
'503': { $ref: '#/components/responses/Error' }
|
|
1322
1648
|
components:
|
|
1323
1649
|
securitySchemes:
|
|
1324
1650
|
bearerAuth:
|
|
@@ -1889,3 +2215,159 @@ components:
|
|
|
1889
2215
|
detail: { type: string, description: 'Terminal reason (`exit code: 3`); a recorded kill reason is merged in.' }
|
|
1890
2216
|
startedAt: { type: integer, format: epoch-ms }
|
|
1891
2217
|
finishedAt: { type: integer, format: epoch-ms, description: Absent while live. }
|
|
2218
|
+
ChunkedUploadOpenRequest:
|
|
2219
|
+
type: object
|
|
2220
|
+
description: Opens or resumes a chunked upload. `size` is the exact byte count the client intends to send.
|
|
2221
|
+
required: [name, size]
|
|
2222
|
+
properties:
|
|
2223
|
+
name:
|
|
2224
|
+
type: string
|
|
2225
|
+
minLength: 1
|
|
2226
|
+
maxLength: 255
|
|
2227
|
+
description: 'Leaf file name only — path separators and control characters are refused.'
|
|
2228
|
+
size:
|
|
2229
|
+
type: integer
|
|
2230
|
+
minimum: 1
|
|
2231
|
+
maximum: 268435456
|
|
2232
|
+
description: 'Declared byte count, 1..256 MiB; above the ceiling answers 413.'
|
|
2233
|
+
contentType:
|
|
2234
|
+
type: [string, 'null']
|
|
2235
|
+
description: 'Optional MIME hint. Omitted → derived from the file extension at `complete`.'
|
|
2236
|
+
ChunkedUploadTicket:
|
|
2237
|
+
type: object
|
|
2238
|
+
description: Server-decided ticket for one chunked upload.
|
|
2239
|
+
required: [uploadId, chunkSize, expiresAt, alreadyReceived]
|
|
2240
|
+
properties:
|
|
2241
|
+
uploadId: { type: string, description: 'Opaque id, e.g. `up_9f3a…`; valid for one hour.' }
|
|
2242
|
+
chunkSize: { type: integer, description: 'Fixed server-side slice size (1048576 today); clients must obey it.' }
|
|
2243
|
+
expiresAt: { type: integer, format: epoch-ms }
|
|
2244
|
+
alreadyReceived:
|
|
2245
|
+
type: array
|
|
2246
|
+
description: 'Chunks the server already holds — empty on a fresh open, populated when a resume matched.'
|
|
2247
|
+
items: { type: integer, minimum: 0 }
|
|
2248
|
+
ChunkedUploadStatus:
|
|
2249
|
+
type: object
|
|
2250
|
+
required: [uploadId, name, size, chunkSize, receivedCount, count, expiresAt, missing]
|
|
2251
|
+
properties:
|
|
2252
|
+
uploadId: { type: string }
|
|
2253
|
+
name: { type: string }
|
|
2254
|
+
size: { type: integer }
|
|
2255
|
+
chunkSize: { type: integer }
|
|
2256
|
+
receivedCount: { type: integer, description: 'Distinct chunks held so far.' }
|
|
2257
|
+
count: { type: integer, description: 'Total chunks the declared size needs.' }
|
|
2258
|
+
expiresAt: { type: integer, format: epoch-ms }
|
|
2259
|
+
missing:
|
|
2260
|
+
type: array
|
|
2261
|
+
description: 'Chunk indexes still owed, ascending. Same list `complete` reports in `details.missing`.'
|
|
2262
|
+
items: { type: integer, minimum: 0 }
|
|
2263
|
+
ChunkedUploadChunkResult:
|
|
2264
|
+
type: object
|
|
2265
|
+
required: [uploadId, received, count]
|
|
2266
|
+
properties:
|
|
2267
|
+
uploadId: { type: string }
|
|
2268
|
+
received: { type: integer, description: 'Distinct chunks held after this write.' }
|
|
2269
|
+
count: { type: integer, description: 'Total chunks the declared size needs.' }
|
|
2270
|
+
ChunkedUploadReceipt:
|
|
2271
|
+
type: object
|
|
2272
|
+
description: 'Stored attachment. Same meaning as the single-shot upload receipt; `name`/`contentType` may be server-derived.'
|
|
2273
|
+
required: [id, receiptId, name, size, contentType, uploadedAt]
|
|
2274
|
+
properties:
|
|
2275
|
+
id: { type: string }
|
|
2276
|
+
receiptId: { type: string }
|
|
2277
|
+
name: { type: string }
|
|
2278
|
+
size: { type: integer }
|
|
2279
|
+
contentType: { type: string }
|
|
2280
|
+
uploadedAt: { type: integer, format: epoch-ms }
|
|
2281
|
+
Schedule:
|
|
2282
|
+
type: object
|
|
2283
|
+
description: |
|
|
2284
|
+
Cron projection of one DSH reminder. `createdAt` is always null — the kernel
|
|
2285
|
+
record has no creation timestamp; clients must not sort on it.
|
|
2286
|
+
required: [id, sessionId, text, cron, timezone, enabled, createdAt, lastRunAt, nextRunAt]
|
|
2287
|
+
properties:
|
|
2288
|
+
id: { type: string, description: 'Opaque id, e.g. `sch_…`.' }
|
|
2289
|
+
sessionId: { type: string }
|
|
2290
|
+
text: { type: string, maxLength: 8000, description: 'The reminder body (kernel `prompt`).' }
|
|
2291
|
+
cron: { type: string, description: 'Five-field Vixie expression, `minute hour day-of-month month day-of-week`.' }
|
|
2292
|
+
timezone: { type: string, description: 'IANA zone the expression is evaluated in.' }
|
|
2293
|
+
enabled: { type: boolean, description: 'True while the kernel still schedules it.' }
|
|
2294
|
+
createdAt: { type: [integer, 'null'], format: epoch-ms, description: 'Always null in this kernel generation.' }
|
|
2295
|
+
lastRunAt: { type: [integer, 'null'], format: epoch-ms, description: 'Last delivered occurrence, when the kernel reports one.' }
|
|
2296
|
+
nextRunAt: { type: [integer, 'null'], format: epoch-ms, description: 'Next planned occurrence.' }
|
|
2297
|
+
ScheduleCreateRequest:
|
|
2298
|
+
type: object
|
|
2299
|
+
required: [sessionId, text, cron, timezone]
|
|
2300
|
+
properties:
|
|
2301
|
+
sessionId: { type: string }
|
|
2302
|
+
text: { type: string, minLength: 1, maxLength: 8000 }
|
|
2303
|
+
cron: { type: string, description: 'Five-field Vixie expression; seconds and `@daily`-style macros are refused.' }
|
|
2304
|
+
timezone: { type: string, description: '`UTC` or a real IANA `Area/Location` name.' }
|
|
2305
|
+
SchedulePatch:
|
|
2306
|
+
type: object
|
|
2307
|
+
description: Send only the fields to change; omitted fields keep their stored value.
|
|
2308
|
+
minProperties: 1
|
|
2309
|
+
properties:
|
|
2310
|
+
enabled:
|
|
2311
|
+
type: boolean
|
|
2312
|
+
description: '`false` is refused (400, `details.reason: ENABLED_UNSUPPORTED`) — this kernel cannot pause; delete instead.'
|
|
2313
|
+
text: { type: string, minLength: 1, maxLength: 8000 }
|
|
2314
|
+
cron: { type: string }
|
|
2315
|
+
timezone: { type: string }
|
|
2316
|
+
SpeechCatalog:
|
|
2317
|
+
type: object
|
|
2318
|
+
description: 'Official speech input state. `available: false` is a 200 answer, never a 404 — hide the entry point, do not error.'
|
|
2319
|
+
required: [available, providers, selected, reason, maxAudioBytes, maxDurationSeconds]
|
|
2320
|
+
properties:
|
|
2321
|
+
available: { type: boolean }
|
|
2322
|
+
providers:
|
|
2323
|
+
type: array
|
|
2324
|
+
items: { $ref: '#/components/schemas/SpeechProvider' }
|
|
2325
|
+
selected:
|
|
2326
|
+
oneOf:
|
|
2327
|
+
- $ref: '#/components/schemas/SpeechSelection'
|
|
2328
|
+
- type: 'null'
|
|
2329
|
+
reason: { type: [string, 'null'], description: 'Why the feature is unavailable; null when it is available.' }
|
|
2330
|
+
maxAudioBytes: { type: integer, description: 'Hard ceiling for one clip on this device (4 MiB today).' }
|
|
2331
|
+
maxDurationSeconds: { type: [integer, 'null'], description: 'Kernel-declared clip length ceiling, when it reports one.' }
|
|
2332
|
+
SpeechProvider:
|
|
2333
|
+
type: object
|
|
2334
|
+
required: [id, name, languages, location]
|
|
2335
|
+
properties:
|
|
2336
|
+
id: { type: string, examples: [builtin] }
|
|
2337
|
+
name: { type: string, examples: [内置语音] }
|
|
2338
|
+
languages:
|
|
2339
|
+
type: array
|
|
2340
|
+
items: { type: string }
|
|
2341
|
+
examples: [[zh-CN, en-US]]
|
|
2342
|
+
location: { type: string, enum: [host-local, cloud], description: 'Whether inference runs on the host or in a provider cloud.' }
|
|
2343
|
+
SpeechSelection:
|
|
2344
|
+
type: object
|
|
2345
|
+
required: [providerId, language]
|
|
2346
|
+
properties:
|
|
2347
|
+
providerId: { type: string }
|
|
2348
|
+
language: { type: string }
|
|
2349
|
+
SpeechTranscriptionRequest:
|
|
2350
|
+
type: object
|
|
2351
|
+
required: [audio]
|
|
2352
|
+
properties:
|
|
2353
|
+
audio:
|
|
2354
|
+
type: string
|
|
2355
|
+
format: byte
|
|
2356
|
+
description: 'Base64 WAV (16 kHz PCM16 recommended). Decoded bytes must be ≤ 4 MiB; non-canonical base64 is refused.'
|
|
2357
|
+
providerId: { type: string, description: 'Defaults to the configured provider.' }
|
|
2358
|
+
language: { type: string, description: 'Defaults to the configured language.' }
|
|
2359
|
+
SpeechTranscription:
|
|
2360
|
+
type: object
|
|
2361
|
+
required: [text, providerId, language, durationMs]
|
|
2362
|
+
properties:
|
|
2363
|
+
text: { type: string, description: 'Empty string means "no speech recognised" — a success, not an error.' }
|
|
2364
|
+
providerId: { type: string }
|
|
2365
|
+
language: { type: string }
|
|
2366
|
+
durationMs: { type: integer, description: 'Audio duration reported by the kernel, in milliseconds.' }
|
|
2367
|
+
SpeechConfigPatch:
|
|
2368
|
+
type: object
|
|
2369
|
+
description: At least one field is required.
|
|
2370
|
+
minProperties: 1
|
|
2371
|
+
properties:
|
|
2372
|
+
providerId: { type: string }
|
|
2373
|
+
language: { type: string }
|