@deepseek-ai/dsh-client-connection 0.0.1-rc.1 → 0.0.1-rc.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 +2 -2
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/lib/client.js +88 -25
- package/lib/types/client/api.d.ts +1 -1
- package/lib/types/client/connection.d.ts +4 -2
- package/lib/types/client/index.d.ts +12 -3
- 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/connection/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: a82bb55ab65df2732ad16248d2cc9aa15b60e94d
|
|
6
|
+
README.zh.md: d4c19db8e74e3562ec55b5ef60675aef79b82008
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
English | [中文](README.zh.md)
|
|
4
4
|
|
|
5
|
-
Wire consumer layer: the client plugin's apply mounts `ctx.connection` (shared api client + current-page loopback state + single-consumer stream-loop starter); the export face carries the wire contract types, the `AbstractApiClient` abstraction, and the loop's sink/config types. The browser carrier uses HTTP POST for unary and respond operations and opens one downlink-only WebSocket each for `events.mux` and `events.host`; the in-process carrier satisfies the same two-stream abstraction. The Host half owns the single `/api` route and its Fetch bridge; a registered TypeRT interceptor claims its Remote endpoints before the API Proxy fallback. Loopback hostname classification stays package-internal: the `/api` Host fence and WebSocket upgrades use it directly, while other client plugins consume the derived `ctx.connection.isLoopback` state. The node half's `/api` route pins the privileged method set (`host.pickDirectory`, `host.openPath`, and the whole configuration plane — `settings.describe`/`openDocument`/`update`/`replace`/`mutate` and `credentials.describe`/`set`/`unset`; reads and native actions included, since describing returns the exposed configuration, opening acts on the Host desktop, and probing an arbitrary reference reports where a credential comes from — and the agent-preset authoring plane, `agentPreset.read`/`copy`/`openDocument`/`remove`, since a composition names the plugins a session runs, so reading one is reconnaissance, and copy/remove/openDocument manage the roster and drive the host desktop (authoring is copy-only, so none of them accepts composition text or a path); `agentPreset.list` and `agentPreset.select` stay out — the roster carries only ids and trust, and choosing a preset grants nothing `session.create`'s own `agentPreset` did not, over a default that already carries bash) to loopback by passing the trust fence with an empty trust list — a declared `trustedHosts` authority reaches every other method, while these stay loopback-local until a real authentication layer exists. The platform carriers and ConnectionController loop are package-internal; apply selects and drives them. The downlink boundary is documented in the [WebSocket downlink carrier Agent Note](../../../.agents/notes/implemented/architecture/2026-08-04-websocket-downlink-carrier.md).
|
|
5
|
+
Wire consumer layer: the client plugin's apply mounts `ctx.connection` (shared api client + current-page loopback state + observable generation-scoped `hostDescription` + single-consumer stream-loop starter); the export face carries the wire contract types, the `AbstractApiClient` abstraction, and the loop's sink/config types. Each successful readiness handshake publishes the exact `host.describe` value before `onConnected`; generation loss and explicit stop clear it, so native-capability consumers never retain a disconnected answer. The browser carrier uses HTTP POST for unary and respond operations and opens one downlink-only WebSocket each for `events.mux` and `events.host`; the in-process carrier satisfies the same two-stream abstraction. The Host half owns the single `/api` route and its Fetch bridge; a registered TypeRT interceptor claims its Remote endpoints before the API Proxy fallback. Loopback hostname classification stays package-internal: the `/api` Host fence and WebSocket upgrades use it directly, while other client plugins consume the derived `ctx.connection.isLoopback` state. The node half's `/api` route pins the privileged method set (`host.pickDirectory`, `host.openPath`, and the whole configuration plane — `settings.describe`/`openDocument`/`update`/`replace`/`mutate` and `credentials.describe`/`set`/`unset`; reads and native actions included, since describing returns the exposed configuration, opening acts on the Host desktop, and probing an arbitrary reference reports where a credential comes from — and the agent-preset authoring plane, `agentPreset.read`/`copy`/`openDocument`/`remove`, since a composition names the plugins a session runs, so reading one is reconnaissance, and copy/remove/openDocument manage the roster and drive the host desktop (authoring is copy-only, so none of them accepts composition text or a path); `agentPreset.list` and `agentPreset.select` stay out — the roster carries only ids and trust, and choosing a preset grants nothing `session.create`'s own `agentPreset` did not, over a default that already carries bash) to loopback by passing the trust fence with an empty trust list — a declared `trustedHosts` authority reaches every other method, while these stay loopback-local until a real authentication layer exists. The platform carriers and ConnectionController loop are package-internal; apply selects and drives them. The downlink boundary is documented in the [WebSocket downlink carrier Agent Note](../../../.agents/notes/implemented/architecture/2026-08-04-websocket-downlink-carrier.md).
|
|
6
6
|
|
|
7
7
|
## /api browser-trust fence
|
|
8
8
|
|
package/README.zh.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 中文
|
|
4
4
|
|
|
5
|
-
协议消费层:客户端插件的 apply 会挂载 `ctx.connection`(共享 API 客户端 + 当前页面的 loopback 状态 + 单消费方流循环启动器);导出表层携带协议约定类型、`AbstractApiClient` 抽象,以及循环的 sink
|
|
5
|
+
协议消费层:客户端插件的 apply 会挂载 `ctx.connection`(共享 API 客户端 + 当前页面的 loopback 状态 + 可观察且按 generation 生效的 `hostDescription` + 单消费方流循环启动器);导出表层携带协议约定类型、`AbstractApiClient` 抽象,以及循环的 sink/配置类型。每次就绪握手成功后,都会在 `onConnected` 之前发布完整的 `host.describe` 值;generation 失效或显式 stop 会清空它,因此原生能力消费者不会保留已经断线的判断。浏览器载体以 HTTP POST 发送 unary/respond,并为 `events.mux` 与 `events.host` 各开一条只下行的 WebSocket;进程内载体满足同一双流抽象。Host half 持有唯一 `/api` route 及其 Fetch bridge;已注册的 TypeRT interceptor 会先认领自己的 Remote endpoint,未认领请求再回退 API Proxy。Loopback hostname 判定逻辑留在包内部:`/api` Host fence 与 WebSocket upgrade 会直接使用它,其他客户端插件则消费派生的 `ctx.connection.isLoopback` 状态。node 半侧的 `/api` 路由让特权方法集(`host.pickDirectory`、`host.openPath`,以及整个配置面——`settings.describe`/`openDocument`/`update`/`replace`/`mutate` 与 `credentials.describe`/`set`/`unset`;读取与原生操作也在内,因为 describe 会返回已暴露的配置、打开操作会作用于 Host 桌面,而探测任意引用会报出某条凭据来自何处——以及 agent preset 的创作面 `agentPreset.read`/`copy`/`openDocument`/`remove`,因为组装指明了一个会话所运行的插件,读取它是侦察,而 copy/remove/openDocument 管理名单并驱动宿主桌面(创作只有复制一种写入,因此这些方法都不接收组装文本或路径);`agentPreset.list` 与 `agentPreset.select` 不在其中——名单只携带 id 与信任级别,而选择一个 preset 并不比 `session.create` 自带的 `agentPreset` 多给任何能力,何况默认 preset 本就带着 bash)以空信任表过信任 fence,从而钉在回环——已声明的 `trustedHosts` 授权可达其余全部方法,而这些方法在真正的认证层出现之前仍只限回环本机。平台载体与 ConnectionController 循环属于包内部;apply 负责选择并驱动它们。下行边界见 [WebSocket 下行载体 Agent Note](../../../.agents/notes/implemented/architecture/2026-08-04-websocket-downlink-carrier.md)。
|
|
6
6
|
|
|
7
7
|
## /api 浏览器信任栅栏
|
|
8
8
|
|
package/lib/client.js
CHANGED
|
@@ -67,6 +67,10 @@ window.__ModuleLoader__.load({
|
|
|
67
67
|
isRunning() {
|
|
68
68
|
return this.running;
|
|
69
69
|
}
|
|
70
|
+
/** Re-read both mutable liveness guards after a potentially reentrant sink. */
|
|
71
|
+
isGenerationActive(controller) {
|
|
72
|
+
return this.isRunning() && !controller.signal.aborted;
|
|
73
|
+
}
|
|
70
74
|
async loop() {
|
|
71
75
|
while (this.running) {
|
|
72
76
|
const gen = ++this.generation;
|
|
@@ -99,7 +103,9 @@ window.__ModuleLoader__.load({
|
|
|
99
103
|
if (ac.signal.aborted) throw new Error("generation aborted during readiness handshake");
|
|
100
104
|
this.attempt = 0;
|
|
101
105
|
this.emitState("connected");
|
|
102
|
-
this.
|
|
106
|
+
if (this.isGenerationActive(ac)) this.callSink(() => {
|
|
107
|
+
this.sinks.onConnected?.(descriptionResult.value);
|
|
108
|
+
});
|
|
103
109
|
} catch {
|
|
104
110
|
if (!ac.signal.aborted) ac.abort();
|
|
105
111
|
}
|
|
@@ -131,7 +137,6 @@ window.__ModuleLoader__.load({
|
|
|
131
137
|
}
|
|
132
138
|
/** Sink exception isolation: a business-layer throw is logged only, never affecting pump or reconnect semantics. */
|
|
133
139
|
callSink(fn) {
|
|
134
|
-
if (fn === void 0) return;
|
|
135
140
|
try {
|
|
136
141
|
fn();
|
|
137
142
|
} catch (error) {
|
|
@@ -492,7 +497,7 @@ window.__ModuleLoader__.load({
|
|
|
492
497
|
}
|
|
493
498
|
/**
|
|
494
499
|
* Fold a transport exception into the RpcResult error branch (unified error
|
|
495
|
-
*
|
|
500
|
+
* API; 'internal' as the catch-all code). Lives with RpcResult so every
|
|
496
501
|
* carrier consumer folds the same way.
|
|
497
502
|
* @param error - the thrown value from the carrier.
|
|
498
503
|
* @returns the error branch of an RpcResult.
|
|
@@ -4880,6 +4885,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4880
4885
|
existingCwd: string().optional()
|
|
4881
4886
|
})
|
|
4882
4887
|
}),
|
|
4888
|
+
object({
|
|
4889
|
+
code: literal("invalid-time-zone"),
|
|
4890
|
+
message: string(),
|
|
4891
|
+
details: object({ value: string() })
|
|
4892
|
+
}),
|
|
4883
4893
|
object({
|
|
4884
4894
|
code: literal("workspace-attach-failed"),
|
|
4885
4895
|
message: string(),
|
|
@@ -5164,7 +5174,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5164
5174
|
/**
|
|
5165
5175
|
* sessions domain zod schemas (names derived from map keys: sessionListRequestSchema /
|
|
5166
5176
|
* sessionListValueSchema). SessionEvent passthrough = strict envelope (type/seq/time) + wide
|
|
5167
|
-
* data: the merge-extensible event
|
|
5177
|
+
* data: the merge-extensible event API keeps an unknown-type branch at the union level,
|
|
5168
5178
|
* with no field-level passthrough. SessionId brand cast point: sessionIdSchema, and only there.
|
|
5169
5179
|
*/
|
|
5170
5180
|
/** SessionId: one brand cast after schema validation (the only cast point in this domain). */
|
|
@@ -5185,7 +5195,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5185
5195
|
time: number(),
|
|
5186
5196
|
data: unknown(),
|
|
5187
5197
|
sourceEventSeqs: array(number()).optional(),
|
|
5188
|
-
surfaceOp: unknown().optional()
|
|
5198
|
+
surfaceOp: unknown().optional(),
|
|
5199
|
+
ignorable: literal(true).optional()
|
|
5189
5200
|
});
|
|
5190
5201
|
/** SessionSummary row of session.list (`projections` reuses the history block's shape and schema). */
|
|
5191
5202
|
const sessionSummarySchema = object({
|
|
@@ -5347,7 +5358,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5347
5358
|
object({
|
|
5348
5359
|
sessionId: sessionIdSchema,
|
|
5349
5360
|
mode: union([literal("queue"), literal("steer")]),
|
|
5350
|
-
content: array(promptContentPartSchema)
|
|
5361
|
+
content: array(promptContentPartSchema),
|
|
5362
|
+
clientTimeZone: string().optional()
|
|
5351
5363
|
});
|
|
5352
5364
|
/** session.prompt response value (the command slot appears only when the prompt dispatched a slash command). */
|
|
5353
5365
|
const sessionPromptValueSchema = object({
|
|
@@ -5408,6 +5420,26 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5408
5420
|
approvalId: approvalRequestIdSchema,
|
|
5409
5421
|
outcome: union([literal("allowed-once"), literal("rejected")])
|
|
5410
5422
|
});
|
|
5423
|
+
/**
|
|
5424
|
+
* One wire task view. `kind` stays an open string because producer plugins
|
|
5425
|
+
* extend the registry's kind map by declaration merging, so the closed set is
|
|
5426
|
+
* not knowable at this boundary.
|
|
5427
|
+
*/
|
|
5428
|
+
const taskViewSchema = object({
|
|
5429
|
+
id: string().min(1),
|
|
5430
|
+
kind: string().min(1),
|
|
5431
|
+
label: string().min(1),
|
|
5432
|
+
status: union([
|
|
5433
|
+
literal("running"),
|
|
5434
|
+
literal("stopping"),
|
|
5435
|
+
literal("completed"),
|
|
5436
|
+
literal("killed"),
|
|
5437
|
+
literal("failed")
|
|
5438
|
+
]),
|
|
5439
|
+
detail: string().optional(),
|
|
5440
|
+
startedAt: number().int().nonnegative(),
|
|
5441
|
+
finishedAt: number().int().nonnegative().optional()
|
|
5442
|
+
});
|
|
5411
5443
|
//#endregion
|
|
5412
5444
|
//#region ../../host/apiproxy/src/api/workspace.schema.ts
|
|
5413
5445
|
/**
|
|
@@ -5545,6 +5577,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5545
5577
|
message: messageSchema
|
|
5546
5578
|
}))
|
|
5547
5579
|
}),
|
|
5580
|
+
object({
|
|
5581
|
+
type: literal("session/tasks"),
|
|
5582
|
+
sessionId: sessionIdSchema,
|
|
5583
|
+
tasks: array(taskViewSchema)
|
|
5584
|
+
}),
|
|
5548
5585
|
object({
|
|
5549
5586
|
type: literal("session/projection"),
|
|
5550
5587
|
sessionId: sessionIdSchema,
|
|
@@ -5594,21 +5631,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5594
5631
|
type: literal("host/archived-sessions-changed"),
|
|
5595
5632
|
archivedSessionIds: array(sessionIdSchema)
|
|
5596
5633
|
}),
|
|
5597
|
-
object({ type: literal("host/commands-changed") }),
|
|
5598
|
-
object({
|
|
5599
|
-
type: literal("host/session-preset-changed"),
|
|
5600
|
-
sessionId: sessionIdSchema,
|
|
5601
|
-
agentPreset: string()
|
|
5602
|
-
}),
|
|
5603
|
-
object({
|
|
5604
|
-
type: literal("host/settings-changed"),
|
|
5605
|
-
ns: string()
|
|
5606
|
-
}),
|
|
5607
5634
|
object({
|
|
5608
|
-
type: literal("host/
|
|
5609
|
-
|
|
5635
|
+
type: literal("host/remote-event"),
|
|
5636
|
+
event: string().min(1),
|
|
5637
|
+
args: array(unknown())
|
|
5610
5638
|
}),
|
|
5611
|
-
object({ type: literal("host/models-changed") }),
|
|
5612
5639
|
object({
|
|
5613
5640
|
type: literal("stream/error"),
|
|
5614
5641
|
error: rpcErrorSchema
|
|
@@ -5621,7 +5648,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5621
5648
|
cwd: string(),
|
|
5622
5649
|
provider: string().optional(),
|
|
5623
5650
|
model: string().optional(),
|
|
5624
|
-
attachedSessions: number().int().nonnegative()
|
|
5651
|
+
attachedSessions: number().int().nonnegative(),
|
|
5652
|
+
canOpenPath: boolean()
|
|
5625
5653
|
});
|
|
5626
5654
|
object({});
|
|
5627
5655
|
/** host.pickDirectory response value; null means the user cancelled. */
|
|
@@ -5983,7 +6011,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5983
6011
|
parentSessionId: sessionIdSchema,
|
|
5984
6012
|
childSessionId: sessionIdSchema,
|
|
5985
6013
|
mode: literal("continuable"),
|
|
5986
|
-
content: array(contentBlockSchema)
|
|
6014
|
+
content: array(contentBlockSchema),
|
|
6015
|
+
clientTimeZone: string().optional()
|
|
5987
6016
|
});
|
|
5988
6017
|
object({
|
|
5989
6018
|
parentSessionId: sessionIdSchema,
|
|
@@ -6354,7 +6383,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6354
6383
|
"- first item",
|
|
6355
6384
|
" - nested item",
|
|
6356
6385
|
"",
|
|
6357
|
-
"|
|
|
6386
|
+
"| Area | State |",
|
|
6358
6387
|
"| --- | --- |",
|
|
6359
6388
|
"| history | rendered |",
|
|
6360
6389
|
"| streaming | stable |",
|
|
@@ -8936,7 +8965,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8936
8965
|
describe: (request) => ok(request, {
|
|
8937
8966
|
version: "0.0.0-fixture",
|
|
8938
8967
|
cwd: "/tmp/fixture",
|
|
8939
|
-
attachedSessions
|
|
8968
|
+
attachedSessions,
|
|
8969
|
+
canOpenPath: true
|
|
8940
8970
|
}),
|
|
8941
8971
|
pickDirectory: (request) => ok(request, { path: `${FIXTURE_HOME}/Documents/project` }),
|
|
8942
8972
|
listDirectory: (request) => {
|
|
@@ -9594,7 +9624,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9594
9624
|
outcome: message.result.ok ? "answered" : "cancelled"
|
|
9595
9625
|
});
|
|
9596
9626
|
return Promise.resolve({ accepted: true });
|
|
9597
|
-
}
|
|
9627
|
+
},
|
|
9628
|
+
downloads: { sessionLog: () => Promise.resolve(new Response("fixture mode does not serve session export", { status: 404 })) }
|
|
9598
9629
|
},
|
|
9599
9630
|
rpc: { call(channel, endpoint, payload) {
|
|
9600
9631
|
if (channel !== "/api") return Promise.reject(/* @__PURE__ */ new Error(`fixture connection RPC channel ${JSON.stringify(channel)} is unavailable`));
|
|
@@ -9916,17 +9947,49 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9916
9947
|
const api = fixtureClient ?? new WebApiClient();
|
|
9917
9948
|
const rpc = fixtureClient?.rpc ?? createWebConnectionRpc();
|
|
9918
9949
|
let started = false;
|
|
9950
|
+
let description;
|
|
9951
|
+
const descriptionListeners = /* @__PURE__ */ new Set();
|
|
9952
|
+
const publishDescription = (next) => {
|
|
9953
|
+
if (Object.is(description, next)) return;
|
|
9954
|
+
description = next;
|
|
9955
|
+
for (const listener of [...descriptionListeners]) try {
|
|
9956
|
+
listener();
|
|
9957
|
+
} catch (error) {
|
|
9958
|
+
console.error("[web-runtime] host-description listener threw:", error);
|
|
9959
|
+
}
|
|
9960
|
+
};
|
|
9919
9961
|
const handle = {
|
|
9920
9962
|
api,
|
|
9921
9963
|
isLoopback: pageLocation === void 0 || isLoopbackHostname(pageLocation.hostname),
|
|
9964
|
+
hostDescription: {
|
|
9965
|
+
getSnapshot: () => description,
|
|
9966
|
+
subscribe: (listener) => {
|
|
9967
|
+
descriptionListeners.add(listener);
|
|
9968
|
+
return () => {
|
|
9969
|
+
descriptionListeners.delete(listener);
|
|
9970
|
+
};
|
|
9971
|
+
}
|
|
9972
|
+
},
|
|
9922
9973
|
rpc,
|
|
9923
9974
|
start(sinks, config) {
|
|
9924
9975
|
if (started) throw new Error("connection: the stream loop is already owned by another consumer");
|
|
9925
9976
|
started = true;
|
|
9926
|
-
const controller = new ConnectionController(api,
|
|
9977
|
+
const controller = new ConnectionController(api, {
|
|
9978
|
+
...sinks,
|
|
9979
|
+
onConnected: (next) => {
|
|
9980
|
+
publishDescription(next);
|
|
9981
|
+
if (!Object.is(description, next)) return;
|
|
9982
|
+
sinks.onConnected?.(next);
|
|
9983
|
+
},
|
|
9984
|
+
onStateChange: (state) => {
|
|
9985
|
+
if (state === "reconnecting") publishDescription(void 0);
|
|
9986
|
+
sinks.onStateChange?.(state);
|
|
9987
|
+
}
|
|
9988
|
+
}, config ?? {});
|
|
9927
9989
|
controller.start();
|
|
9928
9990
|
return { stop: () => {
|
|
9929
9991
|
controller.stop();
|
|
9992
|
+
publishDescription(void 0);
|
|
9930
9993
|
} };
|
|
9931
9994
|
}
|
|
9932
9995
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { ApiProxy, SessionsApi, SessionSearchItem, SessionSummary, PromptContentPart, HostApi, EventsApi, MuxFrame, HostFrame, ApprovalResponsePayload, QuestionResponsePayload, HistoryEntry, ToolEventView, DirectoryEntry, DirectoryListing, ResponseValue, WorkspaceApi, WorkspaceId, WorkspaceView, CommandsApi, CommandDescriptor, SkillsApi, SkillEntry, ModelCatalogFailure, ModelCatalogModel, ModelProviderGroup, ModelReasoning, ModelReasoningEffort, ModelSelection, QueueAction, QueuedInboxItem, SessionModels, GoalsApi, GoalRef, SettingsApi, SettingsNamespaceView, SettingsPathOpView, SettingsSecretView, CredentialsApi, CredentialView, ConfigurableProviderView, DiscoveredModelView, LlmApi, SubagentsApi, SubagentAddress, SubagentCatalog, SubagentListEntry, SubagentPromptReceipt, } from '@deepseek-ai/dsh-host-apiproxy/api';
|
|
1
|
+
export type { ApiProxy, SessionsApi, SessionSearchItem, SessionSummary, PromptContentPart, HostApi, EventsApi, MuxFrame, HostFrame, ApprovalResponsePayload, QuestionResponsePayload, HistoryEntry, ToolEventView, DirectoryEntry, DirectoryListing, ResponseValue, WorkspaceApi, WorkspaceId, WorkspaceView, CommandsApi, CommandDescriptor, SkillsApi, SkillEntry, ModelCatalogFailure, ModelCatalogModel, ModelProviderGroup, ModelReasoning, ModelReasoningEffort, ModelSelection, QueueAction, QueuedInboxItem, SessionModels, GoalsApi, GoalRef, SettingsApi, SettingsNamespaceView, SettingsPathOpView, SettingsSecretView, CredentialsApi, CredentialView, ConfigurableProviderView, DiscoveredModelView, LlmApi, SubagentsApi, SubagentAddress, SubagentCatalog, SubagentListEntry, SubagentPromptReceipt, TaskView, } from '@deepseek-ai/dsh-host-apiproxy/api';
|
|
2
2
|
export type { ToolCallView, ToolResultView } from '@deepseek-ai/dsh-tools/presentation';
|
|
3
3
|
export type { RpcRequest, RpcResponse, RpcResult, RpcError, RpcErrorCode, ClientRequest, ServerResponse, ServerRequest, ClientResponse, RpcMessage, RpcReceipt, } from '@deepseek-ai/dsh-host-apiproxy/api';
|
|
4
4
|
export { RpcId, SESSION_SEARCH_RESULT_LIMIT, transportError, } from '@deepseek-ai/dsh-host-apiproxy/api';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IApiClient, HostFrame, MuxFrame, RpcRequest } from './api.ts';
|
|
1
|
+
import type { HostDescription, IApiClient, HostFrame, MuxFrame, RpcRequest } from './api.ts';
|
|
2
2
|
/** Reconnect/backoff tunables (deployment-varying — no hardcoded tunables; these become the
|
|
3
3
|
* future `ctx.connection` plugin's Config). All fields optional; defaults below. */
|
|
4
4
|
export interface ConnectionConfig {
|
|
@@ -23,7 +23,7 @@ export interface ConnectionSinks {
|
|
|
23
23
|
onMuxEnvelope?: (envelope: RpcRequest<MuxFrame>) => void;
|
|
24
24
|
onHostEnvelope?: (envelope: RpcRequest<HostFrame>) => void;
|
|
25
25
|
/** After each connection generation is established (both streams open + describe succeeded), first connect included. */
|
|
26
|
-
onConnected?: () => void;
|
|
26
|
+
onConnected?: (description: HostDescription) => void;
|
|
27
27
|
/** Coarse state transitions (deduplicated: fires only on change). The initial pre-connect
|
|
28
28
|
* span reports nothing — the UI treats "no state yet" as connecting, not as an outage. */
|
|
29
29
|
onStateChange?: (state: ConnectionState) => void;
|
|
@@ -52,6 +52,8 @@ export declare class ConnectionController {
|
|
|
52
52
|
private backoffDelay;
|
|
53
53
|
/** Read through a method: stop() flips the flag across awaits, so narrowing from the loop condition must not stick. */
|
|
54
54
|
private isRunning;
|
|
55
|
+
/** Re-read both mutable liveness guards after a potentially reentrant sink. */
|
|
56
|
+
private isGenerationActive;
|
|
55
57
|
private loop;
|
|
56
58
|
/** Deduplicated state emission (sink isolation applies). */
|
|
57
59
|
private emitState;
|
|
@@ -4,17 +4,24 @@
|
|
|
4
4
|
* controller with its sinks.
|
|
5
5
|
*/
|
|
6
6
|
import type { Context } from '@deepseek-ai/cordis';
|
|
7
|
-
import type { IApiClient } from './api.ts';
|
|
7
|
+
import type { HostDescription, IApiClient } from './api.ts';
|
|
8
8
|
import { type ConnectionConfig, type ConnectionSinks, type ConnectionState } from './connection.ts';
|
|
9
9
|
import type { ClientConnectionRpc } from '../rpc.ts';
|
|
10
|
-
export type { ApiProxy, SessionsApi, SessionSearchItem, SessionSummary, PromptContentPart, HostApi, EventsApi, MuxFrame, HostFrame, ApprovalResponsePayload, QuestionResponsePayload, HistoryEntry, ToolEventView, DirectoryEntry, DirectoryListing, ToolCallView, ToolResultView, WorkspaceApi, WorkspaceId, WorkspaceView, CommandsApi, CommandDescriptor, SkillsApi, SkillEntry, ModelCatalogFailure, ModelCatalogModel, ModelProviderGroup, ModelReasoning, MessageId, ModelReasoningEffort, ModelSelection, QueueAction, QueuedInboxItem, SessionModels, SubagentsApi, SubagentAddress, SubagentCatalog, SubagentListEntry, SubagentPromptReceipt, RpcRequest, RpcResponse, RpcResult, RpcError, RpcErrorCode, ClientRequest, ServerResponse, ServerRequest, ClientResponse, RpcMessage, RpcReceipt, HostDescription, IApiClient, SessionId, SessionEvent, ContentBlock, StreamChunk, GoalsApi, GoalRef, SettingsApi, SettingsNamespaceView, SettingsPathOpView, SettingsSecretView, CredentialsApi, CredentialView, ConfigurableProviderView, DiscoveredModelView, LlmApi, } from './api.ts';
|
|
10
|
+
export type { ApiProxy, SessionsApi, SessionSearchItem, SessionSummary, PromptContentPart, HostApi, EventsApi, MuxFrame, HostFrame, ApprovalResponsePayload, QuestionResponsePayload, HistoryEntry, ToolEventView, DirectoryEntry, DirectoryListing, ToolCallView, ToolResultView, WorkspaceApi, WorkspaceId, WorkspaceView, CommandsApi, CommandDescriptor, SkillsApi, SkillEntry, ModelCatalogFailure, ModelCatalogModel, ModelProviderGroup, ModelReasoning, MessageId, ModelReasoningEffort, ModelSelection, QueueAction, QueuedInboxItem, SessionModels, SubagentsApi, SubagentAddress, SubagentCatalog, SubagentListEntry, SubagentPromptReceipt, TaskView, RpcRequest, RpcResponse, RpcResult, RpcError, RpcErrorCode, ClientRequest, ServerResponse, ServerRequest, ClientResponse, RpcMessage, RpcReceipt, HostDescription, IApiClient, SessionId, SessionEvent, ContentBlock, StreamChunk, GoalsApi, GoalRef, SettingsApi, SettingsNamespaceView, SettingsPathOpView, SettingsSecretView, CredentialsApi, CredentialView, ConfigurableProviderView, DiscoveredModelView, LlmApi, } from './api.ts';
|
|
11
11
|
export { RpcId, AbstractApiClient, transportError, } from './api.ts';
|
|
12
12
|
export type { ConnectionConfig, ConnectionSinks, ConnectionState };
|
|
13
13
|
export type { ClientConnectionRpc } from '../rpc.ts';
|
|
14
|
+
/** Observable Host description published by each completed connection handshake. */
|
|
15
|
+
export interface HostDescriptionSource {
|
|
16
|
+
/** Latest connected-generation description; absent before connect and while reconnecting. */
|
|
17
|
+
getSnapshot(): HostDescription | undefined;
|
|
18
|
+
/** Subscribe to description replacement and connection loss. */
|
|
19
|
+
subscribe(listener: () => void): () => void;
|
|
20
|
+
}
|
|
14
21
|
/** Required services (none — this is the wire root). */
|
|
15
22
|
export declare const inject: string[];
|
|
16
23
|
/**
|
|
17
|
-
* The ctx.connection service
|
|
24
|
+
* The ctx.connection service API: the API client plus a one-shot
|
|
18
25
|
* controller starter (the runtime plugin supplies sinks when its object layer
|
|
19
26
|
* is ready — connection stays consumer-agnostic).
|
|
20
27
|
*/
|
|
@@ -23,6 +30,8 @@ export interface ConnectionHandle {
|
|
|
23
30
|
readonly api: IApiClient;
|
|
24
31
|
/** Whether the current page authority is loopback; non-browser contexts default to true. */
|
|
25
32
|
readonly isLoopback: boolean;
|
|
33
|
+
/** Generation-scoped Host facts, including native path-open capability. */
|
|
34
|
+
readonly hostDescription: HostDescriptionSource;
|
|
26
35
|
/** Generic logical RPC channels over the same Connection transport. */
|
|
27
36
|
readonly rpc: ClientConnectionRpc;
|
|
28
37
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-connection",
|
|
3
3
|
"description": "Wire consumer layer: HTTP-up/WebSocket-down client, ConnectionController dual streams with reconnect, and fixture api",
|
|
4
|
-
"version": "0.0.1-rc.
|
|
4
|
+
"version": "0.0.1-rc.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "restricted"
|
|
7
7
|
},
|
|
@@ -39,13 +39,13 @@
|
|
|
39
39
|
"license": "BSD-3-Clause",
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"ws": "^8.21.0",
|
|
42
|
-
"@deepseek-ai/dsh-
|
|
43
|
-
"@deepseek-ai/dsh-
|
|
44
|
-
"@deepseek-ai/dsh-
|
|
45
|
-
"@deepseek-ai/dsh-session": "^0.0.1-rc.
|
|
46
|
-
"@deepseek-ai/dsh-tools": "^0.0.1-rc.
|
|
42
|
+
"@deepseek-ai/dsh-commands": "^0.0.1-rc.2",
|
|
43
|
+
"@deepseek-ai/dsh-attachment": "^0.0.1-rc.2",
|
|
44
|
+
"@deepseek-ai/dsh-host-apiproxy": "^0.0.1-rc.2",
|
|
45
|
+
"@deepseek-ai/dsh-session": "^0.0.1-rc.2",
|
|
46
|
+
"@deepseek-ai/dsh-tools": "^0.0.1-rc.2",
|
|
47
47
|
"@deepseek-ai/schemastery": "^3.18.1-rc.1",
|
|
48
|
-
"@deepseek-ai/dsh-llm": "^0.0.1-rc.
|
|
48
|
+
"@deepseek-ai/dsh-llm": "^0.0.1-rc.2"
|
|
49
49
|
},
|
|
50
50
|
"files": [
|
|
51
51
|
"lib/index.js",
|
|
@@ -54,14 +54,14 @@
|
|
|
54
54
|
"lib/types/**/*.d.ts"
|
|
55
55
|
],
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@deepseek-ai/dsh-
|
|
58
|
-
"@deepseek-ai/dsh-
|
|
57
|
+
"@deepseek-ai/dsh-host-webserver": "^0.0.1-rc.2",
|
|
58
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.2",
|
|
59
59
|
"@deepseek-ai/cordis": "^4.0.1-rc.1"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/ws": "^8.18.1",
|
|
63
|
-
"@deepseek-ai/dsh-host-webserver": "^0.0.1-rc.
|
|
64
|
-
"@deepseek-ai/
|
|
65
|
-
"@deepseek-ai/
|
|
63
|
+
"@deepseek-ai/dsh-host-webserver": "^0.0.1-rc.2",
|
|
64
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.1",
|
|
65
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.2"
|
|
66
66
|
}
|
|
67
67
|
}
|