@deepseek-ai/dsh-client-file-upload 0.1.5-rc.2 → 0.1.6-alpha.1
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.i18n.yaml +2 -2
- package/README.md +3 -3
- package/README.zh.md +3 -3
- package/lib/client.js +2 -11
- package/lib/typert.host.js +15 -11
- package/lib/types/client/contract.d.ts +1 -3
- package/lib/types/client/runtime.d.ts +0 -1
- package/lib/types/client/runtime.js +2 -16
- package/package.json +12 -12
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/client/file-upload/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: e0c0d7637da0b426ebb5b4a0c6cb47d12784e0ed
|
|
6
|
+
README.zh.md: abdf965694f7ae62dd05a0192ffbc16e8678ec7d
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
|
|
9
9
|
|
|
10
10
|
## Summary
|
|
11
11
|
|
|
12
|
-
This package lets browser features store a `Blob`, exact bytes, or a `ReadableStream<Uint8Array>` for one Session and receive an opaque receipt for a later prompt. Served pages send Blob and stream bodies without aggregating their bytes on the page thread; pages whose Host runs in another execution context supply a Fetch-shaped carrier before Cordis boots. Callers can observe consumed bytes and cancel an active operation. A stream body is consumed once and transfers ownership when it crosses a Worker boundary.
|
|
12
|
+
This package lets browser features store a `Blob`, exact bytes, or a `ReadableStream<Uint8Array>` for one Session and receive an opaque receipt for a later prompt. Served pages send Blob and stream bodies without aggregating their bytes on the page thread; pages whose Host runs in another execution context supply a Fetch-shaped carrier before Cordis boots. Callers can observe consumed bytes and cancel an active operation. A stream body is consumed once and transfers ownership when it crosses a Worker boundary. Exact bytes use the generated Remote.
|
|
13
13
|
|
|
14
14
|
## Table of Contents
|
|
15
15
|
|
|
@@ -32,7 +32,7 @@ Mount the package before a consumer that injects `fileUpload`, then call `ctx.fi
|
|
|
32
32
|
name: '@deepseek-ai/dsh-client-file-upload'
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
The package has no Cordis configuration fields. A `Blob` uses XMLHttpRequest inside a dedicated Worker so the service can report browser upload progress, including the total when the browser provides it. A `ReadableStream` transfers to that Worker and feeds Fetch incrementally; progress reports consumed bytes without a total. An `AbortSignal` terminates the dedicated Worker or reaches a page-owned carrier. Exact bytes
|
|
35
|
+
The package has no Cordis configuration fields. A `Blob` uses XMLHttpRequest inside a dedicated Worker so the service can report browser upload progress, including the total when the browser provides it. A `ReadableStream` transfers to that Worker and feeds Fetch incrementally; progress reports consumed bytes without a total. An `AbortSignal` terminates the dedicated Worker or reaches a page-owned carrier. Exact bytes use the generated Remote.
|
|
36
36
|
|
|
37
37
|
-----
|
|
38
38
|
|
|
@@ -42,7 +42,7 @@ The package has no Cordis configuration fields. A `Blob` uses XMLHttpRequest ins
|
|
|
42
42
|
<details>
|
|
43
43
|
<summary>Implementation internals — click to expand</summary>
|
|
44
44
|
|
|
45
|
-
The Client plugin provides `ctx.fileUpload`. Its `upload()` method receives the owning Session identity, assembles the raw route request, and invokes the generated Remote
|
|
45
|
+
The Client plugin provides `ctx.fileUpload`. Its `upload()` method receives the owning Session identity, assembles the raw route request, and invokes the generated Remote for exact bytes. The provider reads the optional pre-Cordis `__DSH_FILE_UPLOAD__` hook once. Without a hook, each Blob or stream request owns a short-lived Worker and releases it after completion, failure, or cancellation. With the hook, the service sends the body through the page-owned Fetch carrier; the Web Worker runtime transfers stream bodies through its request frame and exposes them to the Host HTTP bridge as backpressured chunks.
|
|
46
46
|
|
|
47
47
|
The Host plugin provides `ctx.fileUploads`. It owns the authenticated streaming route, encoded Remote fallback, command receipt resolver, and staged-receipt lifecycle; encoded admission, attachment-error recognition, and byte storage stay behind `ctx.attachments`. Receipt tables use the receiving Agent's Session object as their key. The Session Controller registers the resolver that can resume a cold ordinary Agent and consumes receipts during prompt admission. Prompt delivery holds each receipt binding in a disposable transaction: disposal restores the previous binding until successful delivery commits it, and queue or history observation then retires the committed receipt.
|
|
48
48
|
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@ kind: "package-reference"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
本包让浏览器功能为一个 Session 存储 `Blob`、精确字节或 `ReadableStream<Uint8Array>`,并取得供后续 prompt 使用的不透明凭证。普通服务页面发送 Blob 和 stream 请求体时,不会在页面线程聚合全部字节;Host 位于其他执行上下文中的页面会在 Cordis 启动前提供 Fetch 形式的载体。调用方可以观察已消费字节并取消活动操作。stream 请求体只能消费一次,跨 Worker
|
|
12
|
+
本包让浏览器功能为一个 Session 存储 `Blob`、精确字节或 `ReadableStream<Uint8Array>`,并取得供后续 prompt 使用的不透明凭证。普通服务页面发送 Blob 和 stream 请求体时,不会在页面线程聚合全部字节;Host 位于其他执行上下文中的页面会在 Cordis 启动前提供 Fetch 形式的载体。调用方可以观察已消费字节并取消活动操作。stream 请求体只能消费一次,跨 Worker 边界时会转移所有权。精确字节使用生成的 Remote。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -32,7 +32,7 @@ kind: "package-reference"
|
|
|
32
32
|
name: '@deepseek-ai/dsh-client-file-upload'
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
本包没有 Cordis 配置字段。`Blob` 在专用 Worker 内通过 XMLHttpRequest 发送,因此服务可以报告浏览器上传进度,并在浏览器提供总量时一并报告。`ReadableStream` 会转移给该 Worker,再增量传入 Fetch;进度只报告已消费字节,不包含总量。`AbortSignal` 会终止专用 Worker
|
|
35
|
+
本包没有 Cordis 配置字段。`Blob` 在专用 Worker 内通过 XMLHttpRequest 发送,因此服务可以报告浏览器上传进度,并在浏览器提供总量时一并报告。`ReadableStream` 会转移给该 Worker,再增量传入 Fetch;进度只报告已消费字节,不包含总量。`AbortSignal` 会终止专用 Worker,或传递给页面自己提供的载体。精确字节使用生成的 Remote。
|
|
36
36
|
|
|
37
37
|
-----
|
|
38
38
|
|
|
@@ -42,7 +42,7 @@ kind: "package-reference"
|
|
|
42
42
|
<details>
|
|
43
43
|
<summary>实现细节——点击展开</summary>
|
|
44
44
|
|
|
45
|
-
Client 插件提供 `ctx.fileUpload`。其 `upload()` 方法接收所属 Session
|
|
45
|
+
Client 插件提供 `ctx.fileUpload`。其 `upload()` 方法接收所属 Session 标识,组装原始路由请求,并为精确字节调用生成的 Remote。提供方只读取一次可选的 Cordis 启动前 `__DSH_FILE_UPLOAD__` 钩子。没有该钩子时,每个 Blob 或 stream 请求拥有一个短期 Worker,并在完成、失败或取消后释放。存在该钩子时,服务通过页面自己提供的 Fetch 载体发送请求体;Web Worker runtime 会通过请求帧转移 stream 请求体,再以带背压的分片形式交给 Host HTTP bridge。
|
|
46
46
|
|
|
47
47
|
Host 插件提供 `ctx.fileUploads`。它拥有经过认证的流式路由、编码 Remote 兜底、命令凭证解析器与暂存凭证生命周期;编码准入、附件错误识别与字节存储仍由 `ctx.attachments` 提供。凭证表以接收方 Agent 的 Session 对象为键。Session Controller 注册可恢复休眠普通 Agent 的解析器,并在 prompt 准入时消费凭证。Prompt 投递通过可释放事务持有每个凭证绑定。成功投递提交事务前,释放会恢复原绑定;提交后,队列或历史观察会退休该凭证。
|
|
48
48
|
|
package/lib/client.js
CHANGED
|
@@ -151,13 +151,11 @@ window.__ModuleLoader__.load({
|
|
|
151
151
|
}
|
|
152
152
|
/** Cordis service that owns one background carrier per upload operation. */
|
|
153
153
|
var FileUploadRuntime = class extends _deepseek_ai_cordis.Service {
|
|
154
|
-
available;
|
|
155
154
|
transport;
|
|
156
155
|
/** @param ctx - providing Client context. */
|
|
157
156
|
constructor(ctx) {
|
|
158
157
|
super(ctx, "fileUpload");
|
|
159
158
|
const hook = globalThis.__DSH_FILE_UPLOAD__;
|
|
160
|
-
this.available = hook !== void 0 || !isFixturePage();
|
|
161
159
|
this.transport = hook === void 0 ? workerTransport() : customTransport(hook.fetch);
|
|
162
160
|
}
|
|
163
161
|
/**
|
|
@@ -166,7 +164,6 @@ window.__ModuleLoader__.load({
|
|
|
166
164
|
* @returns the response status and text body.
|
|
167
165
|
*/
|
|
168
166
|
post(request) {
|
|
169
|
-
if (!this.available) return Promise.reject(/* @__PURE__ */ new Error("background upload is unavailable in fixture mode"));
|
|
170
167
|
return this.transport.post(request);
|
|
171
168
|
}
|
|
172
169
|
/**
|
|
@@ -179,7 +176,7 @@ window.__ModuleLoader__.load({
|
|
|
179
176
|
* @returns the staged receipt and durable file reference, or a business error.
|
|
180
177
|
*/
|
|
181
178
|
async upload(sessionId, data, name, signal, onProgress) {
|
|
182
|
-
if (!(data instanceof Uint8Array)
|
|
179
|
+
if (!(data instanceof Uint8Array)) {
|
|
183
180
|
const query = new URLSearchParams({ sessionId });
|
|
184
181
|
if (name !== void 0) query.set("name", name);
|
|
185
182
|
const response = await this.post({
|
|
@@ -192,10 +189,8 @@ window.__ModuleLoader__.load({
|
|
|
192
189
|
if (response.status !== 200) throw new Error(`file upload transport failed with HTTP ${String(response.status)}`);
|
|
193
190
|
return parseFileUploadResult(response.body);
|
|
194
191
|
}
|
|
195
|
-
if (!(data instanceof Uint8Array) && !(data instanceof Blob)) throw new Error("stream file upload requires a background carrier");
|
|
196
|
-
const bytes = data instanceof Uint8Array ? data : new Uint8Array(await data.arrayBuffer());
|
|
197
192
|
return this.ctx.remote.fileUploads.upload(sessionId, {
|
|
198
|
-
data: bytesToBase64(
|
|
193
|
+
data: bytesToBase64(data),
|
|
199
194
|
...name === void 0 ? {} : { name }
|
|
200
195
|
}, signal);
|
|
201
196
|
}
|
|
@@ -278,10 +273,6 @@ window.__ModuleLoader__.load({
|
|
|
278
273
|
const origin = typeof pageLocation === "object" && pageLocation !== null && "origin" in pageLocation && typeof pageLocation.origin === "string" ? pageLocation.origin : void 0;
|
|
279
274
|
return new URL(path, origin === void 0 || origin === "null" ? "http://dsh.internal" : origin);
|
|
280
275
|
}
|
|
281
|
-
function isFixturePage() {
|
|
282
|
-
const pageLocation = Reflect.get(globalThis, "location");
|
|
283
|
-
return typeof pageLocation === "object" && pageLocation !== null && "search" in pageLocation && typeof pageLocation.search === "string" && new URLSearchParams(pageLocation.search).has("fixture");
|
|
284
|
-
}
|
|
285
276
|
function parseFileUploadResult(body) {
|
|
286
277
|
const value = JSON.parse(body);
|
|
287
278
|
if (!isRecord(value) || typeof value.ok !== "boolean") throw new TypeError("file upload transport returned an invalid result");
|
package/lib/typert.host.js
CHANGED
|
@@ -158,8 +158,8 @@ export const TYPERT = {
|
|
|
158
158
|
"declaration": "export interface AssistantMessage extends Message {\n readonly role: 'assistant';\n readonly source: ModelMessageSource;\n}"
|
|
159
159
|
},
|
|
160
160
|
{
|
|
161
|
-
"name": "
|
|
162
|
-
"declaration": "export interface
|
|
161
|
+
"name": "AssistantProviderMetadata",
|
|
162
|
+
"declaration": "export interface AssistantProviderMetadata {\n provider: string;\n model: string;\n replayState?: unknown;\n}"
|
|
163
163
|
},
|
|
164
164
|
{
|
|
165
165
|
"name": "AssistantStreamRecord",
|
|
@@ -307,12 +307,16 @@ export const TYPERT = {
|
|
|
307
307
|
},
|
|
308
308
|
{
|
|
309
309
|
"name": "ImageBlock",
|
|
310
|
-
"declaration": "export interface ImageBlock {\n type: 'image';\n attachment: ImageAttachmentRef;\n}"
|
|
310
|
+
"declaration": "export interface ImageBlock {\n type: 'image';\n attachment: ImageAttachmentRef;\n offloaded?: true;\n}"
|
|
311
311
|
},
|
|
312
312
|
{
|
|
313
313
|
"name": "ImageMediaType",
|
|
314
314
|
"declaration": "export type ImageMediaType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif';"
|
|
315
315
|
},
|
|
316
|
+
{
|
|
317
|
+
"name": "ImageOffloadTarget",
|
|
318
|
+
"declaration": "export interface ImageOffloadTarget {\n seq: SessionSeq;\n imageIndexes: number[];\n}"
|
|
319
|
+
},
|
|
316
320
|
{
|
|
317
321
|
"name": "Inbox",
|
|
318
322
|
"declaration": "export interface Inbox {\n readonly nextTurn: readonly UserMessage[];\n readonly nextStep: readonly UserMessage[];\n clear(): void;\n append(target: InboxTarget, message: UserMessage): void;\n prepend(target: InboxTarget, message: UserMessage): void;\n replace(messageId: MessageId, newMessage: UserMessage): boolean;\n remove(messageId: MessageId): boolean;\n splice(target: InboxTarget, start: number, deleteCount: number, inserted: UserMessage[]): UserMessage[];\n}"
|
|
@@ -335,7 +339,7 @@ export const TYPERT = {
|
|
|
335
339
|
},
|
|
336
340
|
{
|
|
337
341
|
"name": "LlmFailure",
|
|
338
|
-
"declaration": "export interface LlmFailure {\n readonly message: string;\n readonly code: string;\n readonly status?: number;\n readonly providerRetryAfterMs?: number;\n readonly requestId?: ProviderRequestId;\n}"
|
|
342
|
+
"declaration": "export interface LlmFailure {\n readonly message: string;\n readonly code: string;\n readonly status?: number;\n readonly providerRetryAfterMs?: number;\n readonly requestId?: ProviderRequestId;\n readonly offloadImages?: number;\n}"
|
|
339
343
|
},
|
|
340
344
|
{
|
|
341
345
|
"name": "Message",
|
|
@@ -375,7 +379,7 @@ export const TYPERT = {
|
|
|
375
379
|
},
|
|
376
380
|
{
|
|
377
381
|
"name": "ModelMessageSource",
|
|
378
|
-
"declaration": "export interface ModelMessageSource extends
|
|
382
|
+
"declaration": "export interface ModelMessageSource extends AssistantProviderMetadata {\n kind: 'model';\n}"
|
|
379
383
|
},
|
|
380
384
|
{
|
|
381
385
|
"name": "ModelSelection",
|
|
@@ -399,7 +403,7 @@ export const TYPERT = {
|
|
|
399
403
|
},
|
|
400
404
|
{
|
|
401
405
|
"name": "PtcDispatchEventData",
|
|
402
|
-
"declaration": "export interface PtcDispatchEventData extends PtcDispatchStartEventData {\n isError: boolean;\n content: ContentBlock[];\n}"
|
|
406
|
+
"declaration": "export interface PtcDispatchEventData extends PtcDispatchStartEventData {\n isError: boolean;\n content: ContentBlock[];\n error?: { name: string; code: string; reason?: string; };\n}"
|
|
403
407
|
},
|
|
404
408
|
{
|
|
405
409
|
"name": "PtcDispatchStartEventData",
|
|
@@ -439,7 +443,7 @@ export const TYPERT = {
|
|
|
439
443
|
},
|
|
440
444
|
{
|
|
441
445
|
"name": "SessionEventMap",
|
|
442
|
-
"declaration": "export interface SessionEventMap {\n 'turn/start': { turn: number; };\n 'turn/end': { turn: number; reason: TurnEndReason; };\n 'step/start': { turn: number; step: number; };\n 'step/end': { turn: number; step: number; };\n 'user/message': UserMessage;\n 'system/message': { turn: number; step: number; message: SystemMessage; };\n 'assistant/message': { turn: number; step: number; message: AssistantMessage; stream: AssistantStreamRecord[]; usage?: TokenUsage; interrupted?: true; };\n 'assistant/attempt': { turn: number; step: number; stream: AssistantStreamRecord[]; };\n 'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string; };\n 'tool/result': { turn: number; step: number; message: ToolResultMessage; error?: { name: string; code: string; }; meta?: JsonValue; };\n 'request/header': { header: EpochHeader; reason: RequestHeaderReason; startsSeries?: true; };\n 'request/context': RequestContext;\n 'session/end-seed': { inherited?: true; };\n 'agent/inbox/spliced': { target: InboxTarget; start: number; removedCount?: number; inserted: UserMessage[]; outcome?: 'canceled'; };\n 'approval/asked': { id: ApprovalRequestId; toolName: string; callId?: ToolCallId; reason?: string; };\n 'approval/decided': { id: ApprovalRequestId; outcome: ApprovalOutcome; };\n 'approval/policy': { policy: ApprovalPolicy; source?: 'delegation'; };\n 'tool/ptc-dispatch-start': PtcDispatchStartEventData;\n 'tool/ptc-dispatch': PtcDispatchEventData;\n 'agent-preset/selected': { agentPreset: string; };\n 'command/run': { commandId: CommandId; name: string; args?: string; source: CommandSource; };\n 'command/done': { commandId: CommandId; kind: 'success' | 'error'; text?: string; sourceEventSeq?: import('@deepseek-ai/dsh-session/types').SessionSeq; };\n 'session/title': SessionTitleEventData;\n 'todo/write': { todos: TodoItem[]; };\n 'model/selection': ModelSelection;\n 'subagent/descriptor': SubagentDescriptorData;\n '
|
|
446
|
+
"declaration": "export interface SessionEventMap {\n 'turn/start': { turn: number; };\n 'turn/end': { turn: number; reason: TurnEndReason; };\n 'step/start': { turn: number; step: number; };\n 'step/end': { turn: number; step: number; };\n 'user/message': UserMessage;\n 'system/message': { turn: number; step: number; message: SystemMessage; };\n 'assistant/message': { turn: number; step: number; message: AssistantMessage; stream: AssistantStreamRecord[]; usage?: TokenUsage; interrupted?: true; };\n 'assistant/attempt': { turn: number; step: number; stream: AssistantStreamRecord[]; };\n 'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string; };\n 'tool/result': { turn: number; step: number; message: ToolResultMessage; error?: { name: string; code: string; reason?: string; }; meta?: JsonValue; };\n 'request/header': { header: EpochHeader; reason: RequestHeaderReason; startsSeries?: true; };\n 'request/context': RequestContext;\n 'session/end-seed': { inherited?: true; };\n 'agent/inbox/spliced': { target: InboxTarget; start: number; removedCount?: number; inserted: UserMessage[]; outcome?: 'canceled'; };\n 'sandbox/mode': { mode: SandboxMode; source?: 'delegation'; };\n 'approval/asked': { id: ApprovalRequestId; toolName: string; callId?: ToolCallId; reason?: string; };\n 'approval/decided': { id: ApprovalRequestId; outcome: ApprovalOutcome; };\n 'approval/policy': { policy: ApprovalPolicy; source?: 'delegation'; };\n 'tool/ptc-dispatch-start': PtcDispatchStartEventData;\n 'tool/ptc-dispatch': PtcDispatchEventData;\n 'agent-preset/selected': { agentPreset: string; };\n 'command/run': { commandId: CommandId; name: string; args?: string; source: CommandSource; };\n 'command/done': { commandId: CommandId; kind: 'success' | 'error'; text?: string; sourceEventSeq?: import('@deepseek-ai/dsh-session/types').SessionSeq; };\n 'image/offload': { targets: ImageOffloadTarget[]; };\n 'session/title': SessionTitleEventData;\n 'todo/write': { todos: TodoItem[]; };\n 'model/selection': ModelSelection;\n 'subagent/descriptor': SubagentDescriptorData;\n 'permission/preset': { preset: string; };\n 'subagent/catalog': SubagentCatalogEvent;\n 'feedback/record': FeedbackRecord;\n 'team/member': { version: 2; teamId: TeamId; member: TeamMemberSnapshot; };\n 'team/task': { version: 2; teamId: TeamId; task: TeamTaskSnapshot; };\n 'team/message/queued': { version: 2; teamId: TeamId; message: TeamMessageSnapshot; };\n 'team/message/delivered': { version: 2; teamId: TeamId; messageId: TeamMessageId; targetId: SessionId; };\n 'goal/change': GoalChangeMeta;\n 'feedback/message-put': MessageFeedbackPut;\n 'feedback/message-delete': MessageFeedbackDelete;\n 'compaction/start': { compactionId: CompactionId; sourceCommandId?: CommandId; turn: number | null; };\n 'compaction/summary': { compactionId: CompactionId; sourceCommandId?: CommandId; summary: ContentBlock[]; shadowedRange: { start: SessionSeq; end: SessionSeq; }; shadowedSeqs: SessionSeq[]; shadowedTokenCount: number; provider: string; model: string; maxTokens?: number; usage?: TokenUsage; } & ({ rawOutput: ContentBlock[]; llmStreamCall: true; } | { rawOutput?: ContentBlock[]; llmStreamCall?: never; });\n 'compaction/end': { compactionId: CompactionId; sourceCommandId?: CommandId; turn: number | null; error?: string; };\n 'compaction/prune': { shadowedRange: { start: SessionSeq; end: SessionSeq; }; shadowedSeqs: SessionSeq[]; shadowedTokenCount: number; };\n}"
|
|
443
447
|
},
|
|
444
448
|
{
|
|
445
449
|
"name": "SessionEventType",
|
|
@@ -471,15 +475,15 @@ export const TYPERT = {
|
|
|
471
475
|
},
|
|
472
476
|
{
|
|
473
477
|
"name": "SessionSurface",
|
|
474
|
-
"declaration": "export interface SessionSurface {\n readonly nodes: readonly SessionSeq[];\n readonly replaceGeneration: number;\n}"
|
|
478
|
+
"declaration": "export interface SessionSurface {\n readonly nodes: readonly SessionSeq[];\n readonly replaceGeneration: number;\n readonly contentGeneration: number;\n}"
|
|
475
479
|
},
|
|
476
480
|
{
|
|
477
481
|
"name": "SessionTitleEventData",
|
|
478
482
|
"declaration": "export interface SessionTitleEventData {\n readonly title: string;\n readonly messageSeqs: SessionSeq[];\n readonly source: SessionTitleSource;\n}"
|
|
479
483
|
},
|
|
480
484
|
{
|
|
481
|
-
"name": "
|
|
482
|
-
"declaration": "export interface
|
|
485
|
+
"name": "SessionTitleModelIdentity",
|
|
486
|
+
"declaration": "export interface SessionTitleModelIdentity {\n readonly provider: string;\n readonly model: string;\n}"
|
|
483
487
|
},
|
|
484
488
|
{
|
|
485
489
|
"name": "SessionTitleProviderId",
|
|
@@ -487,7 +491,7 @@ export const TYPERT = {
|
|
|
487
491
|
},
|
|
488
492
|
{
|
|
489
493
|
"name": "SessionTitleSource",
|
|
490
|
-
"declaration": "export type SessionTitleSource = { readonly kind: 'fallback'; } | { readonly kind: 'provider'; readonly provider: SessionTitleProviderId; readonly model?:
|
|
494
|
+
"declaration": "export type SessionTitleSource = { readonly kind: 'fallback'; } | { readonly kind: 'provider'; readonly provider: SessionTitleProviderId; readonly model?: SessionTitleModelIdentity; } | { readonly kind: 'user'; };"
|
|
491
495
|
},
|
|
492
496
|
{
|
|
493
497
|
"name": "SkillInvocationSource",
|
|
@@ -10,11 +10,9 @@ export interface FileUploadProgress {
|
|
|
10
10
|
}
|
|
11
11
|
/** Browser upload service addressed by one Session identity. */
|
|
12
12
|
export interface FileUploadService {
|
|
13
|
-
/** Whether this page has a Host-backed background upload carrier. */
|
|
14
|
-
readonly available: boolean;
|
|
15
13
|
/**
|
|
16
14
|
* Store one file for a Session. Blob and stream bodies use
|
|
17
|
-
* the background carrier; exact bytes
|
|
15
|
+
* the background carrier; exact bytes use Remote.
|
|
18
16
|
* @param sessionId - Session that owns the staged receipt.
|
|
19
17
|
* @param data - browser Blob, exact bytes, or a one-shot byte stream.
|
|
20
18
|
* @param name - optional display name.
|
|
@@ -64,7 +64,6 @@ type UploadWorkerFetch = (input: string, init: RequestInit & {
|
|
|
64
64
|
export declare function fileUploadWorker(scope?: UploadWorkerScope, createXhr?: () => UploadXhr, doFetch?: UploadWorkerFetch): void;
|
|
65
65
|
/** Cordis service that owns one background carrier per upload operation. */
|
|
66
66
|
export declare class FileUploadRuntime extends Service implements FileUploadService {
|
|
67
|
-
readonly available: boolean;
|
|
68
67
|
private readonly transport;
|
|
69
68
|
/** @param ctx - providing Client context. */
|
|
70
69
|
constructor(ctx: Context);
|
|
@@ -82,13 +82,11 @@ export function fileUploadWorker(scope = self, createXhr = () => new XMLHttpRequ
|
|
|
82
82
|
}
|
|
83
83
|
/** Cordis service that owns one background carrier per upload operation. */
|
|
84
84
|
export class FileUploadRuntime extends Service {
|
|
85
|
-
available;
|
|
86
85
|
transport;
|
|
87
86
|
/** @param ctx - providing Client context. */
|
|
88
87
|
constructor(ctx) {
|
|
89
88
|
super(ctx, 'fileUpload');
|
|
90
89
|
const hook = globalThis.__DSH_FILE_UPLOAD__;
|
|
91
|
-
this.available = hook !== undefined || !isFixturePage();
|
|
92
90
|
this.transport = hook === undefined ? workerTransport() : customTransport(hook.fetch);
|
|
93
91
|
}
|
|
94
92
|
/**
|
|
@@ -97,8 +95,6 @@ export class FileUploadRuntime extends Service {
|
|
|
97
95
|
* @returns the response status and text body.
|
|
98
96
|
*/
|
|
99
97
|
post(request) {
|
|
100
|
-
if (!this.available)
|
|
101
|
-
return Promise.reject(new Error('background upload is unavailable in fixture mode'));
|
|
102
98
|
return this.transport.post(request);
|
|
103
99
|
}
|
|
104
100
|
/**
|
|
@@ -111,7 +107,7 @@ export class FileUploadRuntime extends Service {
|
|
|
111
107
|
* @returns the staged receipt and durable file reference, or a business error.
|
|
112
108
|
*/
|
|
113
109
|
async upload(sessionId, data, name, signal, onProgress) {
|
|
114
|
-
if (!(data instanceof Uint8Array)
|
|
110
|
+
if (!(data instanceof Uint8Array)) {
|
|
115
111
|
const query = new URLSearchParams({ sessionId });
|
|
116
112
|
if (name !== undefined)
|
|
117
113
|
query.set('name', name);
|
|
@@ -127,12 +123,8 @@ export class FileUploadRuntime extends Service {
|
|
|
127
123
|
}
|
|
128
124
|
return parseFileUploadResult(response.body);
|
|
129
125
|
}
|
|
130
|
-
if (!(data instanceof Uint8Array) && !(data instanceof Blob)) {
|
|
131
|
-
throw new Error('stream file upload requires a background carrier');
|
|
132
|
-
}
|
|
133
|
-
const bytes = data instanceof Uint8Array ? data : new Uint8Array(await data.arrayBuffer());
|
|
134
126
|
return this.ctx.remote.fileUploads.upload(sessionId, {
|
|
135
|
-
data: bytesToBase64(
|
|
127
|
+
data: bytesToBase64(data),
|
|
136
128
|
...(name === undefined ? {} : { name }),
|
|
137
129
|
}, signal);
|
|
138
130
|
}
|
|
@@ -223,12 +215,6 @@ function resolveUrl(path) {
|
|
|
223
215
|
: undefined;
|
|
224
216
|
return new URL(path, origin === undefined || origin === 'null' ? 'http://dsh.internal' : origin);
|
|
225
217
|
}
|
|
226
|
-
function isFixturePage() {
|
|
227
|
-
const pageLocation = Reflect.get(globalThis, 'location');
|
|
228
|
-
return typeof pageLocation === 'object' && pageLocation !== null
|
|
229
|
-
&& 'search' in pageLocation && typeof pageLocation.search === 'string'
|
|
230
|
-
&& new URLSearchParams(pageLocation.search).has('fixture');
|
|
231
|
-
}
|
|
232
218
|
function parseFileUploadResult(body) {
|
|
233
219
|
const value = JSON.parse(body);
|
|
234
220
|
if (!isRecord(value) || typeof value.ok !== 'boolean') {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-file-upload",
|
|
3
3
|
"description": "Agent-scoped browser file upload, streaming intake, and staged receipt service",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.6-alpha.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -59,23 +59,23 @@
|
|
|
59
59
|
],
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"zod": "^4.4.3",
|
|
62
|
-
"@deepseek-ai/dsh-typert-protocol": "^0.1.
|
|
63
|
-
"@deepseek-ai/dsh-brand": "^0.1.
|
|
62
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.1",
|
|
63
|
+
"@deepseek-ai/dsh-brand": "^0.1.6-alpha.1"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
66
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
67
|
-
"@deepseek-ai/dsh-scope": "^0.1.
|
|
67
|
+
"@deepseek-ai/dsh-scope": "^0.1.6-alpha.1"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
71
|
-
"@deepseek-ai/dsh-agent": "^0.1.
|
|
72
|
-
"@deepseek-ai/dsh-client-connection": "^0.1.
|
|
73
|
-
"@deepseek-ai/dsh-commands": "^0.1.
|
|
74
|
-
"@deepseek-ai/dsh-scope": "^0.1.
|
|
75
|
-
"@deepseek-ai/dsh-
|
|
76
|
-
"@deepseek-ai/dsh-
|
|
77
|
-
"@deepseek-ai/dsh-
|
|
78
|
-
"@deepseek-ai/dsh-
|
|
71
|
+
"@deepseek-ai/dsh-agent": "^0.1.6-alpha.1",
|
|
72
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.6-alpha.1",
|
|
73
|
+
"@deepseek-ai/dsh-commands": "^0.1.6-alpha.1",
|
|
74
|
+
"@deepseek-ai/dsh-scope": "^0.1.6-alpha.1",
|
|
75
|
+
"@deepseek-ai/dsh-util-crypto": "^0.1.6-alpha.1",
|
|
76
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.1",
|
|
77
|
+
"@deepseek-ai/dsh-attachment": "^0.1.6-alpha.1",
|
|
78
|
+
"@deepseek-ai/dsh-session": "^0.1.6-alpha.1"
|
|
79
79
|
},
|
|
80
80
|
"scripts": {
|
|
81
81
|
"bundle": "tsdown",
|