dsh-deeppilot 0.9.1 → 0.9.3

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/lib/index.d.ts CHANGED
@@ -1,39 +1,238 @@
1
- import { Context } from "@deepseek-ai/cordis";
2
1
  import z from "@deepseek-ai/schemastery";
3
- //#region src/prompt-delivery.d.ts
4
- type DeliveryStatus = 'accepted' | 'rejected' | 'unknown' | 'notFound' | 'expired';
5
- interface DeliveryReceipt {
6
- clientSendId: string;
7
- status: DeliveryStatus;
8
- userSeq?: number;
9
- code?: string;
2
+ import { Context } from "@deepseek-ai/cordis";
3
+ //#region src/wire-errors.d.ts
4
+ /**
5
+ * 错误词表(error vocabulary)的唯一属主。
6
+ *
7
+ * 一条失败信息在系统里要过三种词汇:Host 控制器返回的错误码(`session-not-found`、
8
+ * `schedule_conflict`……)、Bridge 结果里的状态、以及 wire 上的 `E_*` 码。
9
+ * 此前这三层各有映射函数(host-bridge 里 5 个、connection-policy 里 2 个、
10
+ * connection.ts 的 switch 里 5 处内联三元链),一处漂移就会让客户端对
11
+ * 「该不该重试」的判断失真。
12
+ *
13
+ * 本 module 只做一次翻译:每个域一张「Host code -> wire code」表,
14
+ * Bridge 的结果直接携带 wire 码。`E_BUSY` 与 `E_PROTOCOL` 决定客户端是否
15
+ * 重试,因此这张表是 wire 行为的一部分,逐码都有测试把守。
16
+ *
17
+ * 规范见 PROTOCOL.md 错误码表(`E_PROTOCOL` = 未知类型或非法 payload,
18
+ * `E_FORBIDDEN` = 权限不足,`E_UNSUPPORTED` = 能力缺失)。
19
+ */
20
+ /** wire 错误码及其规范描述;PROTOCOL.md 错误码表的 TS 镜像。 */
21
+ declare const ERROR_CODES: {
22
+ readonly E_AUTH: 'device proof missing or invalid';
23
+ readonly E_FORBIDDEN: 'device scope does not allow this operation';
24
+ readonly E_PROTOCOL: 'unknown type or malformed payload';
25
+ readonly E_NOT_FOUND: 'session or request not found';
26
+ readonly E_BUSY: 'session is busy';
27
+ readonly E_UNSUPPORTED: 'protocol version or capability unsupported';
28
+ readonly E_INTERNAL: 'internal error';
29
+ };
30
+ type WireErrorCode = keyof typeof ERROR_CODES;
31
+ //#endregion
32
+ //#region src/dispatch-journal.d.ts
33
+ /** 落盘条目:两个通用字段 + codec 私有字段。 */
34
+ interface JournalEntry {
35
+ fingerprint: string;
36
+ createdAt: number;
37
+ [field: string]: unknown;
38
+ }
39
+ /** operation 的结果:成功带值,失败带 wire 错误码。 */
40
+ type OperationResult<T = unknown> = {
41
+ ok: true;
42
+ value: T;
43
+ } | {
44
+ ok: false;
45
+ code: WireErrorCode;
46
+ message?: string;
47
+ };
48
+ /**
49
+ * 一切「不看条目就能定」的结果类别。codec 用一个方法把它们翻译成自己的方言,
50
+ * 而不是让核心知道任何一方的形状。
51
+ */
52
+ type DispatchOutcome =
53
+ /** 命中既有条目(重放)。 */
54
+ {
55
+ kind: 'replay';
56
+ entry: JournalEntry;
57
+ } |
58
+ /** 同一 id 搭不同内容。 */
59
+ {
60
+ kind: 'mismatch';
61
+ } |
62
+ /** 构造期解析失败:journal 已永久降级,本次进程不再投递。 */
63
+ {
64
+ kind: 'unavailable';
65
+ } |
66
+ /** id 的纪元前缀超出保留窗口。 */
67
+ {
68
+ kind: 'expired';
69
+ } |
70
+ /** 条目数已达容量上限。 */
71
+ {
72
+ kind: 'full';
73
+ } |
74
+ /** 刚跑完的那一次:operation 的结果原样带出(不标 replayed)。 */
75
+ {
76
+ kind: 'ran';
77
+ entry: JournalEntry;
78
+ result: OperationResult;
79
+ } |
80
+ /** operation 抛错:条目保持 unknown,且不得自动重试。 */
81
+ {
82
+ kind: 'crashed';
83
+ } |
84
+ /**
85
+ * 预留条目时落盘失败——上游**没有**被调用,但无从判断 rename 是否已经
86
+ * 生效(save() 可能在 rename 之后才抛),因此条目留在内存里按 unknown 处理。
87
+ */
88
+ {
89
+ kind: 'reserve-failed';
90
+ } |
91
+ /** id 形状不合法(仅 mutation 变体开启这道守卫)。 */
92
+ {
93
+ kind: 'invalid-id';
94
+ };
95
+ interface DispatchCodec<TRequest = unknown, TResult = unknown> {
96
+ /** 实例身份:opener 单例键的一部分(含 persistValues 等变体差异)。 */
97
+ readonly identity: string;
98
+ /** 请求指纹。prompt 覆盖 [sessionId, content];mutation 只覆盖 content。 */
99
+ fingerprint(request: TRequest): string;
100
+ /** 预留条目时写入的 codec 字段(fingerprint/createdAt 由核心补)。 */
101
+ reserve(request: TRequest, id: string): Record<string, unknown>;
102
+ /** operation 成功后就地更新条目。 */
103
+ accepted(entry: JournalEntry, id: string, value: unknown): void;
104
+ /** operation 失败后就地更新条目。 */
105
+ rejected(entry: JournalEntry, id: string, code: WireErrorCode): void;
106
+ /** 把结果类别翻译成调用方言。 */
107
+ resultOf(outcome: DispatchOutcome, id: string): TResult;
108
+ /** id 非法时是否先拒掉(mutation 是;prompt 由 wire 层保证,故否)。 */
109
+ readonly requiresValidId?: boolean;
110
+ /** lookup 的匹配规则;缺省表示该实例不提供 lookup。 */
111
+ matches?(entry: JournalEntry, request: unknown): boolean;
112
+ /** lookup 未命中时回什么(prompt 要区分 notFound / expired)。 */
113
+ miss?(id: string, expired: boolean): TResult;
114
+ /** 落盘容器 → 内存条目表;任何不合法都抛错(核心转成 healthy=false)。 */
115
+ parse(raw: unknown): Record<string, JournalEntry>;
116
+ /** 内存条目表 → 落盘容器。 */
117
+ serialize(entries: Record<string, JournalEntry>): unknown;
118
+ }
119
+ interface DispatchJournalOptions<TRequest = unknown, TResult = unknown> {
120
+ codec: DispatchCodec<TRequest, TResult>;
121
+ path?: string;
122
+ /** 落盘体积上限:prompt 8MB、mutation 4MB(由条目实际大小决定,不统一)。 */
123
+ maxFileBytes?: number;
124
+ capacity?: number;
125
+ retentionMs?: number;
126
+ /**
127
+ * 并发重复是否搭同在途那次。prompt 是(重复方拿到同一个最终回执);
128
+ * mutation 否(重复方立刻拿到 unknown → E_INTERNAL)。两者都由对等快照钉住。
129
+ */
130
+ joinInFlight?: boolean;
131
+ /**
132
+ * 落盘失败的诊断输出。由调用方注入它已有的 logger;缺省即静默。
133
+ * 只打阶段与 errno,绝不打条目内容或消息体(plugin 的日志禁令)。
134
+ */
135
+ log?: (message: string) => void;
136
+ /**
137
+ * 目录 fsync 里「对 fd 调用 fsync」这一步的覆盖点,缺省即 `fsyncSync`。
138
+ *
139
+ * 存在的唯一理由是可测:Linux CI 上复现不出 Windows 的 EPERM。注入点刻意
140
+ * 停在 fd 这一层而不是替换整个目录 fsync——容忍 EPERM 的判断必须留在真实
141
+ * 路径里被测到,换掉整函数就等于把待测逻辑一起换掉了。
142
+ */
143
+ fsyncFileDescriptor?: (fd: number) => void;
10
144
  }
11
145
  /** Durable at-most-once dispatch. Unknown outcomes are never automatically retried. */
12
- declare class PromptDeliveryJournal {
13
- private readonly path?;
14
- private readonly capacity;
146
+ declare class DispatchJournal<TRequest = unknown, TResult = unknown> {
147
+ private readonly options;
15
148
  private entries;
16
149
  private inFlight;
150
+ /** 只由构造期的解析失败置位,见下方 catch;运行期落盘失败不碰它。 */
17
151
  private healthy;
18
- constructor(path?: string | undefined, capacity?: number);
152
+ private readonly maxFileBytes;
153
+ private readonly capacity;
154
+ private readonly retentionMs;
155
+ private readonly joinInFlight;
156
+ constructor(options: DispatchJournalOptions<TRequest, TResult>);
19
157
  private key;
158
+ /** id 的纪元前缀是否还在保留窗口内(未来 5 分钟以上同样算非法)。 */
20
159
  private expired;
21
160
  private save;
22
- lookup(deviceId: string, sessionId: string, id: string): DeliveryReceipt;
23
- dispatch(deviceId: string, sessionId: string, id: string, content: unknown, operation: () => Promise<{
24
- ok: true;
25
- value: number;
26
- } | {
27
- ok: false;
28
- kind: string;
29
- }>): Promise<DeliveryReceipt>;
161
+ /**
162
+ * 落盘一次,失败只报不降级。
163
+ *
164
+ * 与 `healthy` 的分工:`healthy` 只由构造期的解析失败置位,运行期的磁盘错误
165
+ * 不再拉黑整个进程——此前一次 save() 失败就让本进程余下的所有投递永久返回
166
+ * unknown,直到 DSH 重启(issue #20 / #26)。现在只影响当次投递,下一次
167
+ * dispatch 会重新尝试。
168
+ */
169
+ private persist;
170
+ /** 查询既有回执;`request` 交给 codec 的 matches 判定(缺省即不提供 lookup)。 */
171
+ lookup(deviceId: string, id: string, request?: unknown): TResult | undefined;
172
+ /**
173
+ * 幂等投递。未知结果绝不自动重试:条目保持 unknown,重放时由 codec 决定
174
+ * 调用方看到什么。
175
+ */
176
+ dispatch(deviceId: string, id: string, request: TRequest, operation: () => Promise<OperationResult>): Promise<TResult>;
177
+ /** 等所有在途投递结束;供进程收尾。 */
178
+ settled(): Promise<void>;
179
+ }
180
+ /** prompt 投递的回执:手机侧按 clientSendId 对账。 */
181
+ type DeliveryStatus = 'accepted' | 'rejected' | 'unknown' | 'notFound' | 'expired';
182
+ interface DeliveryReceipt {
183
+ clientSendId: string;
184
+ status: DeliveryStatus;
185
+ userSeq?: number;
186
+ code?: string;
30
187
  }
188
+ /** mutation 的结果:replayed 让调用方知道这是重放而非新执行。 */
189
+ type MutationErrorCode = WireErrorCode;
190
+ type MutationDispatchResult<T> = {
191
+ ok: true;
192
+ value?: T;
193
+ replayed?: boolean;
194
+ } | {
195
+ ok: false;
196
+ code: MutationErrorCode;
197
+ message?: string;
198
+ replayed?: boolean;
199
+ };
31
200
  //#endregion
32
201
  //#region src/device-auth.d.ts
33
202
  declare const DEVICE_SCOPES: readonly ['sessions.read', 'prompt.send', 'sessions.manage', 'interactions.respond', 'notifications.register', 'schedule.manage'];
34
203
  type DeviceScope = (typeof DEVICE_SCOPES)[number];
35
204
  //#endregion
36
205
  //#region src/protocol.d.ts
206
+ interface WelcomeCapabilities {
207
+ historyPaging: boolean;
208
+ replay: boolean;
209
+ approvals: boolean;
210
+ questions: boolean;
211
+ /** Client can request the complete currently-pending approval/question set. */
212
+ pendingSnapshot?: boolean;
213
+ promptDelivery?: boolean;
214
+ /** Bridge emits s2c.notify for all four notification categories. */
215
+ notifyAllCategories?: boolean;
216
+ models: boolean;
217
+ sessionManagement: boolean;
218
+ /** Bridge can fork a session at an exact event boundary. */
219
+ sessionFork?: boolean;
220
+ /** Bridge can list archived sessions and restore them
221
+ * (c2s.sessions.archived / c2s.session.unarchive). Absent on hosts whose
222
+ * workspace controller predates unarchiveSession. */
223
+ sessionRestore?: boolean;
224
+ projectSelection: boolean;
225
+ /** Host exposes the optional DSH Schedule service. */
226
+ schedules?: boolean;
227
+ /** Bridge has APNs configured; clients may send c2s.push.register. */
228
+ push?: boolean;
229
+ widgetPush?: boolean;
230
+ liveActivityPush?: boolean;
231
+ /** Bridge accepts c2s.device.revoke, letting a client unbind itself before
232
+ * deleting its local credentials. Absent on older bridges; older clients
233
+ * ignore the extra field. */
234
+ deviceRevoke?: boolean;
235
+ }
37
236
  type SessionStatus = "running" | "idle" | "error" | "unknown";
38
237
  /**
39
238
  * Cumulative model/token statistics for one session, mirrored from the
@@ -435,12 +634,17 @@ interface HostSessionModels {
435
634
  message: string;
436
635
  }>;
437
636
  }
637
+ /**
638
+ * Bridge 方法的结果。失败直接携带 wire 错误码(error vocabulary 见
639
+ * wire-errors.ts):调用方(wire-registry 的行)原样透给手机,不再经过
640
+ * kind 中间层翻译。
641
+ */
438
642
  type ModelBridgeResult<T> = {
439
643
  ok: true;
440
644
  value: T;
441
645
  } | {
442
646
  ok: false;
443
- kind: 'unsupported' | 'not-found' | 'busy' | 'unavailable' | 'internal';
647
+ code: WireErrorCode;
444
648
  message: string;
445
649
  };
446
650
  type SessionManagementResult<T> = {
@@ -448,7 +652,7 @@ type SessionManagementResult<T> = {
448
652
  value: T;
449
653
  } | {
450
654
  ok: false;
451
- kind: 'unsupported' | 'not-found' | 'busy' | 'invalid' | 'internal';
655
+ code: WireErrorCode;
452
656
  message: string;
453
657
  };
454
658
  interface PromptArgs {
@@ -597,62 +801,30 @@ interface PromptDocument {
597
801
  truncated?: boolean;
598
802
  }
599
803
  //#endregion
600
- //#region src/mutation-journal.d.ts
601
- type MutationErrorCode = 'E_PROTOCOL' | 'E_NOT_FOUND' | 'E_BUSY' | 'E_UNSUPPORTED' | 'E_INTERNAL';
602
- type MutationOperationResult<T> = {
603
- ok: true;
604
- value: T;
605
- } | {
606
- ok: false;
607
- code: MutationErrorCode;
608
- message?: string;
609
- };
610
- type MutationDispatchResult<T> = {
611
- ok: true;
612
- value?: T;
613
- replayed?: boolean;
614
- } | {
615
- ok: false;
616
- code: MutationErrorCode;
617
- message?: string;
618
- replayed?: boolean;
619
- };
804
+ //#region src/host-bridge.d.ts
620
805
  /**
621
- * Small durable at-most-once journal for non-prompt mutations.
806
+ * 失败结果直接携带 wire 错误码(error vocabulary 见 wire-errors.ts)。
622
807
  *
623
- * Only a hash of the request content is persisted. The successful value is
624
- * kept in memory for the current process so a concurrent retry can receive the
625
- * same response; after a restart a replay is acknowledged and the client
626
- * refetches the authoritative resource. This avoids duplicating reminder
627
- * prompts or conversation data in a second journal.
808
+ * 迁移前这里有 kind 中间层('not-found' | 'busy' | 'conflict' | 'invalid'…),
809
+ * 由 5 个映射函数在「Host code ↔ kind ↔ E_*」之间来回翻译,其中
810
+ * E_BUSY→conflict→E_BUSY 的往返已证明有损。现在每个域查一次表,调用方拿到的
811
+ * 就是手机上会看到的错误码。
628
812
  */
629
- declare class MutationJournal {
630
- private readonly path?;
631
- private readonly persistValues;
632
- private readonly entries;
633
- private readonly inFlight;
634
- private healthy;
635
- constructor(path?: string | undefined, persistValues?: boolean);
636
- private key;
637
- private expired;
638
- private save;
639
- dispatch<T>(deviceId: string, id: string, content: unknown, operation: () => Promise<MutationOperationResult<T>>): Promise<MutationDispatchResult<T>>;
640
- }
641
- //#endregion
642
- //#region src/host-bridge.d.ts
643
813
  type ScheduleBridgeResult<T> = {
644
814
  ok: true;
645
815
  value: T;
646
816
  replayed?: boolean;
647
817
  } | {
648
818
  ok: false;
649
- kind: 'unsupported' | 'not-found' | 'invalid' | 'conflict' | 'busy' | 'internal';
819
+ code: WireErrorCode;
650
820
  message: string;
651
821
  replayed?: boolean;
652
822
  };
653
823
  declare class HostBridge {
654
824
  private readonly apiProxy;
655
825
  private readonly historyBufferMax;
826
+ /** 三个 journal 的落盘失败诊断输出;缺省即静默。不打任何消息体。 */
827
+ private readonly log?;
656
828
  readonly id: number;
657
829
  private summaries;
658
830
  private activeTools;
@@ -670,10 +842,15 @@ declare class HostBridge {
670
842
  private abort;
671
843
  private started;
672
844
  private disposed;
673
- readonly promptDeliveries: PromptDeliveryJournal;
674
- readonly scheduleMutations: MutationJournal;
675
- readonly forkMutations: MutationJournal;
676
- constructor(apiProxy: ApiProxyLike, historyBufferMax?: number, deliveryJournalPath?: string, scheduleJournalPath?: string, forkJournalPath?: string);
845
+ readonly promptDeliveries: DispatchJournal<{
846
+ sessionId: string;
847
+ content: unknown;
848
+ }, DeliveryReceipt>;
849
+ readonly scheduleMutations: DispatchJournal<unknown, MutationDispatchResult<unknown>>;
850
+ readonly forkMutations: DispatchJournal<unknown, MutationDispatchResult<unknown>>;
851
+ constructor(apiProxy: ApiProxyLike, historyBufferMax?: number, deliveryJournalPath?: string, scheduleJournalPath?: string, forkJournalPath?: string,
852
+ /** 三个 journal 的落盘失败诊断输出;缺省即静默。不打任何消息体。 */
853
+ log?: ((message: string) => void) | undefined);
677
854
  private pushOutlet;
678
855
  private widgetFingerprint;
679
856
  /**
@@ -681,25 +858,12 @@ declare class HostBridge {
681
858
  * capability and notify-worthy events are mirrored to APNs.
682
859
  */
683
860
  setPushOutlet(outlet: PushOutlet | undefined): void;
684
- get capabilities(): {
685
- historyPaging: boolean;
686
- replay: boolean;
687
- approvals: boolean;
688
- questions: boolean;
689
- pendingSnapshot: boolean;
690
- promptDelivery: boolean;
691
- notifyAllCategories: boolean;
692
- models: boolean;
693
- sessionManagement: boolean;
694
- sessionFork: boolean;
695
- sessionRestore: boolean;
696
- projectSelection: boolean;
697
- schedules: boolean;
698
- push: boolean;
699
- widgetPush: boolean;
700
- liveActivityPush: boolean;
701
- deviceRevoke: boolean;
702
- };
861
+ /**
862
+ * welcome 能力位。委托 host-capabilities.ts 的探测表:此前同一条事实在这里
863
+ * 和各方法体内各存一份,已经漂移过两次(models 位过严、schedules 位与
864
+ * schedule 方法的探测不同源)。
865
+ */
866
+ get capabilities(): WelcomeCapabilities;
703
867
  diagnostic(message: string): void;
704
868
  currentCursor(): number;
705
869
  addSink(sink: BridgeSink): void;