@deepseek-ai/dsh-client-file-upload 0.1.5-rc.2 → 0.1.6-alpha.2

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 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: 2c19e1a564d7b58226e6ccd3cb5b7c81e1b1c14e
6
- README.zh.md: d1553d40d953dd6378a954c43a9b7a36b7334953
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. The standalone `?fixture` page uses the generated Remote for replayable Blob and exact-byte inputs.
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 and fixture Blob inputs use the generated Remote.
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 fallback for replayable inputs. The provider reads the optional pre-Cordis `__DSH_FILE_UPLOAD__` hook once. Without a hook, each non-fixture raw 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.
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 边界时会转移所有权。独立的 `?fixture` 页面通过生成的 Remote 处理可重放的 Blob 与精确字节输入。
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,或传递给页面自己提供的载体。精确字节与 fixture Blob 输入使用生成的 Remote。
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 标识,组装原始路由请求,并为可重放输入调用生成的 Remote 兜底。提供方只读取一次可选的 Cordis 启动前 `__DSH_FILE_UPLOAD__` 钩子。没有该钩子时,每个非 fixture 原始请求拥有一个短期 Worker,并在完成、失败或取消后释放。存在该钩子时,服务通过页面自己提供的 Fetch 载体发送请求体;Web Worker runtime 会通过请求帧转移 stream 请求体,再以带背压的分片形式交给 Host HTTP bridge。
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) && this.available) {
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(bytes),
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");
@@ -1,19 +1,22 @@
1
1
  /* Generated by @deepseek-ai/dsh-typert-generator from FaceModel — do not edit. */
2
2
  import { z } from 'zod'
3
3
 
4
- const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_0$schema = z.intersection(z.string(), z.unknown())
5
- const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_1$schema = z.object({
4
+ let _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_0$schema$value
5
+ const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_0$schema = () => (_deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_0$schema$value ??= z.intersection(z.string(), z.unknown()))
6
+ let _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_1$schema$value
7
+ const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_1$schema = () => (_deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_1$schema$value ??= z.object({
6
8
  'data': z.string().readonly(),
7
9
  'name': z.string().readonly().optional(),
8
- })
9
- const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_result$schema = z.object({
10
+ }))
11
+ let _deepseek_ai_dsh_client_file_upload_fileUploads_upload_result$schema$value
12
+ const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_result$schema = () => (_deepseek_ai_dsh_client_file_upload_fileUploads_upload_result$schema$value ??= z.object({
10
13
  'receiptId': z.intersection(z.string(), z.unknown()).readonly(),
11
14
  'file': z.object({
12
15
  'attachmentId': z.intersection(z.string(), z.unknown()),
13
16
  'name': z.string(),
14
17
  'bytes': z.number(),
15
18
  }).readonly(),
16
- })
19
+ }))
17
20
 
18
21
  export const TYPERT = {
19
22
  package: '@deepseek-ai/dsh-client-file-upload',
@@ -40,7 +43,7 @@ export const TYPERT = {
40
43
  codec: {
41
44
  mode: 'strict',
42
45
  typeSymbol: '@deepseek-ai/dsh-session/types#SessionId',
43
- schema: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_0$schema,
46
+ create: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_0$schema,
44
47
  },
45
48
  },
46
49
  {
@@ -50,7 +53,7 @@ export const TYPERT = {
50
53
  codec: {
51
54
  mode: 'strict',
52
55
  typeSymbol: '@deepseek-ai/dsh-client-file-upload/types#EncodedFileUploadRequest',
53
- schema: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_1$schema,
56
+ create: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_1$schema,
54
57
  },
55
58
  },
56
59
  ],
@@ -58,7 +61,7 @@ export const TYPERT = {
58
61
  result: {
59
62
  mode: 'strict',
60
63
  typeSymbol: '@deepseek-ai/dsh-client-file-upload/types#FileUploadValue',
61
- schema: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_result$schema,
64
+ create: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_result$schema,
62
65
  },
63
66
  sourceLocation: {"file":"packages/client/file-upload/src/index.ts","line":106,"column":3},
64
67
  },
@@ -158,8 +161,8 @@ export const TYPERT = {
158
161
  "declaration": "export interface AssistantMessage extends Message {\n readonly role: 'assistant';\n readonly source: ModelMessageSource;\n}"
159
162
  },
160
163
  {
161
- "name": "AssistantProvenance",
162
- "declaration": "export interface AssistantProvenance {\n provider: string;\n model: string;\n replayState?: unknown;\n}"
164
+ "name": "AssistantProviderMetadata",
165
+ "declaration": "export interface AssistantProviderMetadata {\n provider: string;\n model: string;\n replayState?: unknown;\n}"
163
166
  },
164
167
  {
165
168
  "name": "AssistantStreamRecord",
@@ -307,12 +310,16 @@ export const TYPERT = {
307
310
  },
308
311
  {
309
312
  "name": "ImageBlock",
310
- "declaration": "export interface ImageBlock {\n type: 'image';\n attachment: ImageAttachmentRef;\n}"
313
+ "declaration": "export interface ImageBlock {\n type: 'image';\n attachment: ImageAttachmentRef;\n offloaded?: true;\n}"
311
314
  },
312
315
  {
313
316
  "name": "ImageMediaType",
314
317
  "declaration": "export type ImageMediaType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif';"
315
318
  },
319
+ {
320
+ "name": "ImageOffloadTarget",
321
+ "declaration": "export interface ImageOffloadTarget {\n seq: SessionSeq;\n imageIndexes: number[];\n}"
322
+ },
316
323
  {
317
324
  "name": "Inbox",
318
325
  "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 +342,7 @@ export const TYPERT = {
335
342
  },
336
343
  {
337
344
  "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}"
345
+ "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
346
  },
340
347
  {
341
348
  "name": "Message",
@@ -375,7 +382,7 @@ export const TYPERT = {
375
382
  },
376
383
  {
377
384
  "name": "ModelMessageSource",
378
- "declaration": "export interface ModelMessageSource extends AssistantProvenance {\n kind: 'model';\n}"
385
+ "declaration": "export interface ModelMessageSource extends AssistantProviderMetadata {\n kind: 'model';\n}"
379
386
  },
380
387
  {
381
388
  "name": "ModelSelection",
@@ -399,7 +406,7 @@ export const TYPERT = {
399
406
  },
400
407
  {
401
408
  "name": "PtcDispatchEventData",
402
- "declaration": "export interface PtcDispatchEventData extends PtcDispatchStartEventData {\n isError: boolean;\n content: ContentBlock[];\n}"
409
+ "declaration": "export interface PtcDispatchEventData extends PtcDispatchStartEventData {\n isError: boolean;\n content: ContentBlock[];\n error?: { name: string; code: string; reason?: string; };\n}"
403
410
  },
404
411
  {
405
412
  "name": "PtcDispatchStartEventData",
@@ -439,7 +446,7 @@ export const TYPERT = {
439
446
  },
440
447
  {
441
448
  "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 'sandbox/mode': { mode: SandboxMode; source?: 'delegation'; };\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}"
449
+ "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
450
  },
444
451
  {
445
452
  "name": "SessionEventType",
@@ -471,15 +478,15 @@ export const TYPERT = {
471
478
  },
472
479
  {
473
480
  "name": "SessionSurface",
474
- "declaration": "export interface SessionSurface {\n readonly nodes: readonly SessionSeq[];\n readonly replaceGeneration: number;\n}"
481
+ "declaration": "export interface SessionSurface {\n readonly nodes: readonly SessionSeq[];\n readonly replaceGeneration: number;\n readonly contentGeneration: number;\n}"
475
482
  },
476
483
  {
477
484
  "name": "SessionTitleEventData",
478
485
  "declaration": "export interface SessionTitleEventData {\n readonly title: string;\n readonly messageSeqs: SessionSeq[];\n readonly source: SessionTitleSource;\n}"
479
486
  },
480
487
  {
481
- "name": "SessionTitleModelProvenance",
482
- "declaration": "export interface SessionTitleModelProvenance {\n readonly provider: string;\n readonly model: string;\n}"
488
+ "name": "SessionTitleModelIdentity",
489
+ "declaration": "export interface SessionTitleModelIdentity {\n readonly provider: string;\n readonly model: string;\n}"
483
490
  },
484
491
  {
485
492
  "name": "SessionTitleProviderId",
@@ -487,7 +494,7 @@ export const TYPERT = {
487
494
  },
488
495
  {
489
496
  "name": "SessionTitleSource",
490
- "declaration": "export type SessionTitleSource = { readonly kind: 'fallback'; } | { readonly kind: 'provider'; readonly provider: SessionTitleProviderId; readonly model?: SessionTitleModelProvenance; } | { readonly kind: 'user'; };"
497
+ "declaration": "export type SessionTitleSource = { readonly kind: 'fallback'; } | { readonly kind: 'provider'; readonly provider: SessionTitleProviderId; readonly model?: SessionTitleModelIdentity; } | { readonly kind: 'user'; };"
491
498
  },
492
499
  {
493
500
  "name": "SkillInvocationSource",
@@ -1,19 +1,22 @@
1
1
  /* Generated by @deepseek-ai/dsh-typert-generator from the Host FaceModel — do not edit. */
2
2
  import { z } from 'zod'
3
3
 
4
- const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_0$schema = z.intersection(z.string(), z.unknown())
5
- const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_1$schema = z.object({
4
+ let _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_0$schema$value
5
+ const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_0$schema = () => (_deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_0$schema$value ??= z.intersection(z.string(), z.unknown()))
6
+ let _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_1$schema$value
7
+ const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_1$schema = () => (_deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_1$schema$value ??= z.object({
6
8
  'data': z.string().readonly(),
7
9
  'name': z.string().readonly().optional(),
8
- })
9
- const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_result$schema = z.object({
10
+ }))
11
+ let _deepseek_ai_dsh_client_file_upload_fileUploads_upload_result$schema$value
12
+ const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_result$schema = () => (_deepseek_ai_dsh_client_file_upload_fileUploads_upload_result$schema$value ??= z.object({
10
13
  'receiptId': z.intersection(z.string(), z.unknown()).readonly(),
11
14
  'file': z.object({
12
15
  'attachmentId': z.intersection(z.string(), z.unknown()),
13
16
  'name': z.string(),
14
17
  'bytes': z.number(),
15
18
  }).readonly(),
16
- })
19
+ }))
17
20
 
18
21
  export const TYPERT_REMOTE = {
19
22
  package: '@deepseek-ai/dsh-client-file-upload',
@@ -37,7 +40,7 @@ export const TYPERT_REMOTE = {
37
40
  codec: {
38
41
  mode: 'strict',
39
42
  typeSymbol: '@deepseek-ai/dsh-session/types#SessionId',
40
- schema: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_0$schema,
43
+ create: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_0$schema,
41
44
  },
42
45
  },
43
46
  {
@@ -47,7 +50,7 @@ export const TYPERT_REMOTE = {
47
50
  codec: {
48
51
  mode: 'strict',
49
52
  typeSymbol: '@deepseek-ai/dsh-client-file-upload/types#EncodedFileUploadRequest',
50
- schema: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_1$schema,
53
+ create: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_1$schema,
51
54
  },
52
55
  },
53
56
  ],
@@ -55,7 +58,7 @@ export const TYPERT_REMOTE = {
55
58
  result: {
56
59
  mode: 'strict',
57
60
  typeSymbol: '@deepseek-ai/dsh-client-file-upload/types#FileUploadValue',
58
- schema: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_result$schema,
61
+ create: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_result$schema,
59
62
  },
60
63
  sourceLocation: {"file":"packages/client/file-upload/src/index.ts","line":106,"column":3},
61
64
  },
@@ -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 and fixture fallbacks use Remote.
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) && this.available) {
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(bytes),
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.5-rc.2",
4
+ "version": "0.1.6-alpha.2",
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.5-rc.2",
63
- "@deepseek-ai/dsh-brand": "^0.1.5-rc.2"
62
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.2",
63
+ "@deepseek-ai/dsh-brand": "^0.1.6-alpha.2"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "@deepseek-ai/cordis": "^4.0.2",
67
- "@deepseek-ai/dsh-scope": "^0.1.5-rc.2"
67
+ "@deepseek-ai/dsh-scope": "^0.1.6-alpha.2"
68
68
  },
69
69
  "devDependencies": {
70
70
  "@deepseek-ai/cordis": "^4.0.2",
71
- "@deepseek-ai/dsh-agent": "^0.1.5-rc.2",
72
- "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.2",
73
- "@deepseek-ai/dsh-commands": "^0.1.5-rc.2",
74
- "@deepseek-ai/dsh-scope": "^0.1.5-rc.2",
75
- "@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.2",
76
- "@deepseek-ai/dsh-session": "^0.1.5-rc.2",
77
- "@deepseek-ai/dsh-util-crypto": "^0.1.5-rc.2",
78
- "@deepseek-ai/dsh-attachment": "^0.1.5-rc.2"
71
+ "@deepseek-ai/dsh-client-connection": "^0.1.6-alpha.2",
72
+ "@deepseek-ai/dsh-agent": "^0.1.6-alpha.2",
73
+ "@deepseek-ai/dsh-commands": "^0.1.6-alpha.2",
74
+ "@deepseek-ai/dsh-util-crypto": "^0.1.6-alpha.2",
75
+ "@deepseek-ai/dsh-scope": "^0.1.6-alpha.2",
76
+ "@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.2",
77
+ "@deepseek-ai/dsh-attachment": "^0.1.6-alpha.2",
78
+ "@deepseek-ai/dsh-session": "^0.1.6-alpha.2"
79
79
  },
80
80
  "scripts": {
81
81
  "bundle": "tsdown",