@deepseek-ai/dsh-api-remotes 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 +11 -3
- package/README.zh.md +12 -2
- package/lib/index.js +25 -1
- package/lib/types/agent-lookup.js +166 -0
- package/lib/types/client/index.d.ts +12 -0
- package/lib/types/client/index.js +13 -0
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.js +16 -0
- package/lib/types/invariant.js +16 -0
- package/lib/types/remote-events.d.ts +17 -0
- package/lib/types/remote-events.js +23 -0
- package/lib/types/types.d.ts +18 -0
- package/lib/types/types.js +12 -0
- package/package.json +33 -16
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/api/remotes/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: cc903af7204ca715c6c7931cfe44823d4d5fc71e
|
|
6
|
+
README.zh.md: fe34b8774c9864cef442ff8a58f22f541d40768a
|
package/README.md
CHANGED
|
@@ -6,21 +6,29 @@ Two-sided BFF for Host Remote capabilities selected by this application. The Hos
|
|
|
6
6
|
|
|
7
7
|
`createApiRemoteAgentResolver()` reuses live Agents, resumes ordinary cold sessions, deduplicates concurrent resumes, preserves the subagent ownership fence, and configures the same resolver for TypeRT `agent` and `session` lookups. The standard Web API Proxy supplies its Agent defaults and scope setup, then uses the returned resolver for legacy methods, so migrated and unmigrated methods share one policy implementation.
|
|
8
8
|
|
|
9
|
-
The current Client assembly mounts only the Goal Remote contribution. Cordis effect ownership withdraws every contribution when this assembly unloads, while `@deepseek-ai/dsh-api-gateway/client` owns descriptor validation, traced namespace Services, direct and scoped methods, invocation, and cancellation. The Client entry consumes the shared `TypeRTClientRemote` interface through Cordis and does not import the concrete Gateway.
|
|
9
|
+
The current Client assembly mounts only the Goal Remote contribution. Cordis effect ownership withdraws every contribution when this assembly unloads, while `@deepseek-ai/dsh-api-gateway/client` owns descriptor validation, traced namespace Services, direct and scoped methods, invocation, and cancellation. The Client entry consumes the shared `TypeRTClientRemote` interface through Cordis and does not import the concrete Gateway. It re-exports the Gateway Client face's declaration merges type-only, so a consumer reaching the forwarded-event vocabulary through this facade gains no runtime edge to the Gateway implementation.
|
|
10
10
|
|
|
11
11
|
This package contains no transport or Host service discovery logic. Its Client face can be reused by Web or a future TUI that provides the same React-free `ctx.remote` contract.
|
|
12
12
|
|
|
13
|
+
## Forwarded Host events
|
|
14
|
+
|
|
15
|
+
`src/remote-events.ts` holds `API_REMOTE_FORWARDED_EVENTS`, the allowlist of Host cordis events this application forwards to consumers verbatim — no projection, no redaction, no renaming — and therefore the legal key set of `ctx.remote.$on`; the type-only `src/types.ts` derives its selection face. Forwarding one more event is an entry in that array and nothing else: the type projection, the consumer key face, and the Host forwarding loop all derive from it.
|
|
16
|
+
|
|
17
|
+
The listener signature is not restated here. Each allowlisted event's cordis `Events` declaration lives in its owner package's client-safe `./types` export (`dsh-agent-presets`, `dsh-commands`, `dsh-credentials`, `dsh-llm`, `dsh-settings`), and both faces of this package pull those declarations in, so "forwarded verbatim" holds by construction rather than by proof. The Host face additionally asserts the list against `TypeRTForwardableEvent`, which rejects a name that is not a declared event, one that binds an AgentScope, and one whose shape is not one-way.
|
|
18
|
+
|
|
13
19
|
## Build boundary
|
|
14
20
|
|
|
15
21
|
An ordinary repository package belongs to one TypeScript face: Host packages are registered in the root `tsconfig.host.json`, and Client packages in the root `tsconfig.client.json`. `api-remotes` is the only deliberate exception because its Host entry must participate in the Host TypeRT graph, while `src/client/index.ts` cannot compile until Host tsdown has generated the business packages' `/remote` declarations.
|
|
16
22
|
|
|
17
|
-
This package's root `tsconfig.json` is only a solution that references `tsconfig.host.json` and `tsconfig.client.json`. The Host aggregate and direct Host consumers reference the former, while the Client aggregate and direct Client consumers reference the latter; the package-root solution must not enter either aggregate's dependency graph. The two projects own disjoint source files and `.tsbuildinfo` files but share the `lib/types` output directory.
|
|
23
|
+
This package's root `tsconfig.json` is only a solution that references `tsconfig.host.json` and `tsconfig.client.json`. The Host aggregate and direct Host consumers reference the former, while the Client aggregate and direct Client consumers reference the latter; the package-root solution must not enter either aggregate's dependency graph. The two projects own disjoint source files and `.tsbuildinfo` files but share the `lib/types` output directory, with one deliberate exception: `src/remote-events.ts` and `src/types.ts` are listed in BOTH faces' `files`, because the forwarded-event allowlist is the single control point over what a consumer can receive, and the Host forwarding loop and the Client `ctx.remote.$on` key face must read one declaration rather than two that could drift.
|
|
24
|
+
|
|
25
|
+
That exception is not just a `files` entry. The root `tsconfig.base.json` maps `@deepseek-ai/dsh-api-remotes/types` to `src/types.ts` — the source plane, like every other workspace subpath and unlike the generated `/remote` artifacts, which have no `paths` entry and resolve through `exports` to built output. Both faces therefore admit the same allowlist and type projection into their own programs and emit byte-identical `remote-events` and `types` outputs into `lib/types`; the `.tsbuildinfo` files stay independent. No gate enforces the faces' source-file disjointness — `scripts/project-reference-faces.ts` only checks that a reference into a split project names the matching face — so this paragraph records why the double listing is intentional.
|
|
18
26
|
|
|
19
27
|
The package-local `clientBundle(..., { hostPhase: true })` makes Host tsdown bundle the Host entry and the later Client tsdown bundle only the browser entry. Ordinary Client plugins remain single Client projects and produce both their Node loader entry and browser bundle during Client tsdown; do not copy this package's split merely because a package has both `src/index.ts` and `src/client/index.ts`.
|
|
20
28
|
|
|
21
29
|
## Model Experience
|
|
22
30
|
|
|
23
|
-
None, as this BFF selects Remote application methods and identity policy but registers
|
|
31
|
+
None, as this BFF selects Remote application methods and identity policy but registers nothing model-facing.
|
|
24
32
|
|
|
25
33
|
#### KV Cache effect
|
|
26
34
|
|
package/README.zh.md
CHANGED
|
@@ -6,15 +6,25 @@
|
|
|
6
6
|
|
|
7
7
|
`createApiRemoteAgentResolver()` 会复用 live Agent、恢复普通冷会话、对并发恢复去重、保留 subagent ownership fence,并为 TypeRT `agent` 和 `session` lookup 配置同一个 resolver。标准 Web API Proxy 提供 Agent 默认值和 scope 设置,再将返回的 resolver 用于旧方法,使已迁移与未迁移方法共用同一份策略实现。
|
|
8
8
|
|
|
9
|
-
当前 Client 组合仅挂载 Goal Remote 贡献。该组合卸载时,Cordis effect 的所有权机制会撤回所有贡献;`@deepseek-ai/dsh-api-gateway/client` 负责描述符校验、可追踪 namespace Service、直接与作用域方法、调用与取消。Client 入口通过 Cordis 消费共享的 `TypeRTClientRemote` 接口,不导入具体 Gateway
|
|
9
|
+
当前 Client 组合仅挂载 Goal Remote 贡献。该组合卸载时,Cordis effect 的所有权机制会撤回所有贡献;`@deepseek-ai/dsh-api-gateway/client` 负责描述符校验、可追踪 namespace Service、直接与作用域方法、调用与取消。Client 入口通过 Cordis 消费共享的 `TypeRTClientRemote` 接口,不导入具体 Gateway;它只以 type-only 形式重新导出 Gateway Client face 的声明合并,因此消费端经由本外观取到转发事件词汇时,运行时不会多出一条通往 Gateway 实现的边。
|
|
10
|
+
|
|
10
11
|
|
|
11
12
|
本包不包含传输逻辑或 Host 服务发现逻辑。Web 或未来的 TUI 只要提供同一份不依赖 React 的 `ctx.remote` 约定,均可复用其 Client face。
|
|
12
13
|
|
|
14
|
+
## 转发的 Host 事件
|
|
15
|
+
|
|
16
|
+
`src/remote-events.ts` 持有 `API_REMOTE_FORWARDED_EVENTS`——本应用原样转发给消费端的 Host cordis 事件名单(无投影、无脱敏、无改名),它同时就是 `ctx.remote.$on` 的合法键集;只含类型的 `src/types.ts` 派生其选择面。多转发一个事件只需在该数组里加一行:类型投影、消费端键面与 Host 转发循环全部由它派生。
|
|
17
|
+
|
|
18
|
+
监听器签名不在此处重写。名单内每条事件的 cordis `Events` 声明都住在其 owner 包 client-safe 的 `./types` 出口(`dsh-agent-presets`、`dsh-commands`、`dsh-credentials`、`dsh-llm`、`dsh-settings`),本包两个 face 都把那些声明纳入编译面,因此「原样转发」是构造性成立的,不需要另立证明。Host face 还额外把名单断言给 `TypeRTForwardableEvent`:未声明的事件名、绑定 AgentScope 的事件、以及形状不是单向的事件都会在此被拒绝。
|
|
19
|
+
|
|
13
20
|
## 构建边界
|
|
14
21
|
|
|
15
22
|
仓库中的普通包只属于一个 TypeScript face:Host 包登记在根 `tsconfig.host.json`,Client 包登记在根 `tsconfig.client.json`。`api-remotes` 是唯一刻意拆分的特例,因为它的 Host 入口要参与 Host TypeRT 图,而 `src/client/index.ts` 必须等 Host tsdown 生成业务包的 `/remote` 声明后才能编译。
|
|
16
23
|
|
|
17
|
-
本包根 `tsconfig.json` 只是引用 `tsconfig.host.json` 与 `tsconfig.client.json` 的 solution。Host aggregate 和 Host 直接消费方引用前者,Client aggregate 和 Client 直接消费方引用后者;禁止把包根 solution 放进任一 aggregate 的依赖图。两个 project 拥有互不重叠的源码和 `.tsbuildinfo`,但共享 `lib/types`
|
|
24
|
+
本包根 `tsconfig.json` 只是引用 `tsconfig.host.json` 与 `tsconfig.client.json` 的 solution。Host aggregate 和 Host 直接消费方引用前者,Client aggregate 和 Client 直接消费方引用后者;禁止把包根 solution 放进任一 aggregate 的依赖图。两个 project 拥有互不重叠的源码和 `.tsbuildinfo`,但共享 `lib/types` 输出目录——只有一处刻意的例外:`src/remote-events.ts` 与 `src/types.ts` **同时**列进两个 face 的 `files`,因为转发事件名单是「消费端能收到什么」的唯一控制点,Host 转发循环与 Client 的 `ctx.remote.$on` 键面必须读同一份声明,而不是两份可能彼此漂移的声明。
|
|
25
|
+
|
|
26
|
+
这条例外不止是一行 `files`。根 `tsconfig.base.json` 把 `@deepseek-ai/dsh-api-remotes/types` 映射到 `src/types.ts`——**源平面**,与其余所有 workspace 子路径一致,也与生成的 `/remote` 产物相反(后者没有 `paths` 条目,靠 `exports` 命中构建产物)。于是两个 face 都把同一份名单与类型投影收进各自的 program,并向 `lib/types` 发射逐字相同的 `remote-events` 与 `types` 输出;`.tsbuildinfo` 仍各自独立。没有任何门禁强制两个 face 的源文件互不重叠——`scripts/project-reference-faces.ts` 只校验「引用一个 split project 必须指到对应 face」——因此本段记录这次双列为何是有意的。
|
|
27
|
+
|
|
18
28
|
|
|
19
29
|
包内 `clientBundle(..., { hostPhase: true })` 让 Host tsdown 打包 Host 入口,让后续 Client tsdown 只打包 browser 入口。普通 Client 插件仍使用单一 Client project,并在 Client tsdown 阶段一起生成 Node loader 入口和 browser bundle;不得因一个包同时存在 `src/index.ts` 与 `src/client/index.ts` 就复制本包的拆分。
|
|
20
30
|
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
import { TypeRTLookupFailure } from "@deepseek-ai/dsh-type-meta";
|
|
2
|
+
//#region lib/types/remote-events.js
|
|
3
|
+
/**
|
|
4
|
+
* The one home of this application's forwarded-Host-event allowlist. Both
|
|
5
|
+
* compiler faces list this file, so the Host forwarding loop and the consumer
|
|
6
|
+
* `ctx.remote.$on` key face read one declaration instead of two copies that
|
|
7
|
+
* could drift; `./types.ts` derives the type projection from it and stays
|
|
8
|
+
* type-only.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Host events this application forwards to consumers verbatim: no projection,
|
|
12
|
+
* no redaction, no renaming. The wire name is the Host cordis event name and
|
|
13
|
+
* the payload is its argument list, so this array is simultaneously the whole
|
|
14
|
+
* control point over what a consumer can receive and the legal key set of
|
|
15
|
+
* `ctx.remote.$on`. Forwarding one more event is an entry here and nothing
|
|
16
|
+
* else.
|
|
17
|
+
*/
|
|
18
|
+
const API_REMOTE_FORWARDED_EVENTS = [
|
|
19
|
+
"agent-preset/selected",
|
|
20
|
+
"commands/change",
|
|
21
|
+
"credentials/updated",
|
|
22
|
+
"llm/adapters-updated",
|
|
23
|
+
"settings/document-updated"
|
|
24
|
+
];
|
|
25
|
+
//#endregion
|
|
2
26
|
//#region lib/types/agent-lookup.js
|
|
3
27
|
/** Host BFF policy for resolving Remote Agent and Session identities. */
|
|
4
28
|
/** Cold identity absent from the durable session store. */
|
|
@@ -139,4 +163,4 @@ function createApiRemoteAgentResolver(ctx, options) {
|
|
|
139
163
|
/** Host plugin body; the selected contributions mount only in Client environments. */
|
|
140
164
|
function apply() {}
|
|
141
165
|
//#endregion
|
|
142
|
-
export { ApiRemoteSessionNotFound, ApiRemoteSubagentSessionOwnership, apiRemoteSubagentOwnershipError, apply, createApiRemoteAgentResolver, hasApiRemoteSubagentOwner, inspectApiRemoteSession };
|
|
166
|
+
export { API_REMOTE_FORWARDED_EVENTS, ApiRemoteSessionNotFound, ApiRemoteSubagentSessionOwnership, apiRemoteSubagentOwnershipError, apply, createApiRemoteAgentResolver, hasApiRemoteSubagentOwner, inspectApiRemoteSession };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/** Host BFF policy for resolving Remote Agent and Session identities. */
|
|
2
|
+
import { TypeRTLookupFailure } from '@deepseek-ai/dsh-type-meta';
|
|
3
|
+
/** Cold identity absent from the durable session store. */
|
|
4
|
+
export class ApiRemoteSessionNotFound extends Error {
|
|
5
|
+
}
|
|
6
|
+
/** Session identity whose lifecycle belongs to subagent routing. */
|
|
7
|
+
export class ApiRemoteSubagentSessionOwnership extends Error {
|
|
8
|
+
sessionId;
|
|
9
|
+
/**
|
|
10
|
+
* Construct the ownership fence.
|
|
11
|
+
* @param sessionId - identity reserved to subagent routing.
|
|
12
|
+
*/
|
|
13
|
+
constructor(sessionId) {
|
|
14
|
+
super(`session "${sessionId}" is a subagent session; use subagent delivery`);
|
|
15
|
+
this.sessionId = sessionId;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Test whether generic Host routing must leave an identity to subagent routing.
|
|
20
|
+
* @param ctx - Host Context carrying the live Agent registry.
|
|
21
|
+
* @param session - attached or live Session metadata.
|
|
22
|
+
* @param agent - live Agent when one is registered.
|
|
23
|
+
* @returns whether generic Remote and legacy API calls must reject the identity.
|
|
24
|
+
*/
|
|
25
|
+
export function hasApiRemoteSubagentOwner(ctx, session, agent) {
|
|
26
|
+
if (session.header.origin === 'subagent')
|
|
27
|
+
return true;
|
|
28
|
+
const parentId = session.header.parentSession;
|
|
29
|
+
if (parentId === undefined || agent === undefined)
|
|
30
|
+
return false;
|
|
31
|
+
const parent = ctx.agents.get(parentId);
|
|
32
|
+
return parent !== undefined && ctx.agents.isOwnedBy(agent.id, parent);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build the stable caller-facing ownership rejection.
|
|
36
|
+
* @param sessionId - identity reserved to subagent routing.
|
|
37
|
+
* @returns the existing `agent-busy` RPC shape.
|
|
38
|
+
*/
|
|
39
|
+
export function apiRemoteSubagentOwnershipError(sessionId) {
|
|
40
|
+
return {
|
|
41
|
+
code: 'agent-busy',
|
|
42
|
+
message: `session "${sessionId}" is owned by subagent routing`,
|
|
43
|
+
details: { reason: 'use subagent delivery for this child session' },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Inspect one cold served session without repairing, resuming, or publishing it.
|
|
48
|
+
* @param ctx - Host Context carrying the optional persistence provider.
|
|
49
|
+
* @param sessionId - durable identity to inspect.
|
|
50
|
+
* @returns detached metadata and events for a servable session.
|
|
51
|
+
* @throws {@link ApiRemoteSessionNotFound} when the identity has no project-backed session.
|
|
52
|
+
*/
|
|
53
|
+
export async function inspectApiRemoteSession(ctx, sessionId) {
|
|
54
|
+
const persistence = ctx.get('sessionPersistence');
|
|
55
|
+
if (persistence === undefined) {
|
|
56
|
+
throw new Error('session persistence is not configured (load a dsh-session-persistence backend)');
|
|
57
|
+
}
|
|
58
|
+
const meta = (await persistence.list()).find(candidate => candidate.id === sessionId);
|
|
59
|
+
if (meta === undefined || meta.cwd === undefined) {
|
|
60
|
+
throw new ApiRemoteSessionNotFound(`session "${sessionId}" not found`);
|
|
61
|
+
}
|
|
62
|
+
const inspected = await persistence.inspect(sessionId);
|
|
63
|
+
if (inspected.meta.cwd === undefined) {
|
|
64
|
+
throw new ApiRemoteSessionNotFound(`session "${sessionId}" not found`);
|
|
65
|
+
}
|
|
66
|
+
return { meta: inspected.meta, events: [...inspected.events] };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Create the Host's shared Agent resolver and configure Agent/Session TypeRT lookups.
|
|
70
|
+
* Live Agents are reused, ordinary cold sessions resume once per identity, and
|
|
71
|
+
* subagent-owned identities retain the legacy `agent-busy` fence.
|
|
72
|
+
* @param ctx - owning Host Context.
|
|
73
|
+
* @param options - defaults and Agent-scope setup used only for cold resume.
|
|
74
|
+
* @returns resolver shared by legacy API Proxy methods and TypeRT lookups.
|
|
75
|
+
*/
|
|
76
|
+
export function createApiRemoteAgentResolver(ctx, options) {
|
|
77
|
+
const resumes = new Map();
|
|
78
|
+
const fencedLiveAgent = (sessionId) => {
|
|
79
|
+
const live = ctx.agents.get(sessionId);
|
|
80
|
+
if (live === undefined)
|
|
81
|
+
return undefined;
|
|
82
|
+
if (hasApiRemoteSubagentOwner(ctx, live.session, live)) {
|
|
83
|
+
return { error: apiRemoteSubagentOwnershipError(sessionId) };
|
|
84
|
+
}
|
|
85
|
+
return { agent: live };
|
|
86
|
+
};
|
|
87
|
+
const agentFor = async (sessionId) => {
|
|
88
|
+
const fenced = fencedLiveAgent(sessionId);
|
|
89
|
+
if (fenced !== undefined)
|
|
90
|
+
return fenced;
|
|
91
|
+
const attached = ctx.sessions.get(sessionId);
|
|
92
|
+
if (attached !== undefined && hasApiRemoteSubagentOwner(ctx, attached, undefined)) {
|
|
93
|
+
return { error: apiRemoteSubagentOwnershipError(sessionId) };
|
|
94
|
+
}
|
|
95
|
+
let resume = resumes.get(sessionId);
|
|
96
|
+
if (resume === undefined) {
|
|
97
|
+
resume = (async () => {
|
|
98
|
+
try {
|
|
99
|
+
const inspected = await inspectApiRemoteSession(ctx, sessionId);
|
|
100
|
+
if (hasApiRemoteSubagentOwner(ctx, { header: inspected.meta }, undefined)) {
|
|
101
|
+
throw new ApiRemoteSubagentSessionOwnership(sessionId);
|
|
102
|
+
}
|
|
103
|
+
// Built from the inspected session before the published re-checks
|
|
104
|
+
// below, so those stay adjacent to `resume` and a Host setup that
|
|
105
|
+
// awaits (composing a preset, say) does not widen the collision
|
|
106
|
+
// window.
|
|
107
|
+
const setup = options.setup === undefined ? undefined : await options.setup(inspected);
|
|
108
|
+
const publishedSession = ctx.sessions.get(sessionId);
|
|
109
|
+
const publishedAgent = ctx.agents.get(sessionId);
|
|
110
|
+
if (publishedSession !== undefined
|
|
111
|
+
&& hasApiRemoteSubagentOwner(ctx, publishedSession, publishedAgent)) {
|
|
112
|
+
throw new ApiRemoteSubagentSessionOwnership(sessionId);
|
|
113
|
+
}
|
|
114
|
+
const handle = await ctx.agents.resume({
|
|
115
|
+
resumeSessionId: sessionId,
|
|
116
|
+
...options.agentOptions === undefined ? {} : { agentOptions: options.agentOptions() },
|
|
117
|
+
...setup === undefined ? {} : { setup },
|
|
118
|
+
});
|
|
119
|
+
return handle.agent;
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
resumes.delete(sessionId);
|
|
123
|
+
}
|
|
124
|
+
})();
|
|
125
|
+
resumes.set(sessionId, resume);
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
return { agent: await resume };
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
if (error instanceof ApiRemoteSessionNotFound) {
|
|
132
|
+
return { error: { code: 'session-not-found', message: error.message, details: { sessionId } } };
|
|
133
|
+
}
|
|
134
|
+
if (error instanceof ApiRemoteSubagentSessionOwnership) {
|
|
135
|
+
return { error: apiRemoteSubagentOwnershipError(error.sessionId) };
|
|
136
|
+
}
|
|
137
|
+
const fenced = fencedLiveAgent(sessionId);
|
|
138
|
+
if (fenced !== undefined)
|
|
139
|
+
return fenced;
|
|
140
|
+
const attached = ctx.sessions.get(sessionId);
|
|
141
|
+
if (attached !== undefined && hasApiRemoteSubagentOwner(ctx, attached, undefined)) {
|
|
142
|
+
return { error: apiRemoteSubagentOwnershipError(sessionId) };
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
error: {
|
|
146
|
+
code: 'internal',
|
|
147
|
+
message: `resume failed for session "${sessionId}": ${String(error)}`,
|
|
148
|
+
details: {},
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
ctx.inject(['typert'], (typeCtx) => {
|
|
154
|
+
const resolveAgent = async (sessionId) => {
|
|
155
|
+
const found = await agentFor(sessionId);
|
|
156
|
+
if ('error' in found)
|
|
157
|
+
throw new TypeRTLookupFailure(found.error);
|
|
158
|
+
return found.agent;
|
|
159
|
+
};
|
|
160
|
+
typeCtx.typert.lookups.configure('agent', resolveAgent);
|
|
161
|
+
typeCtx.typert.lookups.configure('session', async (sessionId) => (await resolveAgent(sessionId)).session);
|
|
162
|
+
typeCtx.typert.contexts.configureHost('agent', async (sessionId) => (await resolveAgent(sessionId)).ctx);
|
|
163
|
+
});
|
|
164
|
+
return agentFor;
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=agent-lookup.js.map
|
|
@@ -3,6 +3,18 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
3
3
|
import type { TypeRTClientRemote } from '@deepseek-ai/dsh-type-meta';
|
|
4
4
|
export type { TypeRTClientRemote as ClientRemote } from '@deepseek-ai/dsh-type-meta';
|
|
5
5
|
export type {} from '@deepseek-ai/dsh-goal/remote';
|
|
6
|
+
export type { ApiRemoteForwardedEvent } from '../types.ts';
|
|
7
|
+
export type {} from '@deepseek-ai/dsh-commands/types';
|
|
8
|
+
export type {} from '@deepseek-ai/dsh-credentials/types';
|
|
9
|
+
export type {} from '@deepseek-ai/dsh-llm/types';
|
|
10
|
+
export type {} from '@deepseek-ai/dsh-agent-presets/types';
|
|
11
|
+
export type {} from '@deepseek-ai/dsh-settings/types';
|
|
12
|
+
/**
|
|
13
|
+
* The Gateway Client face's own declaration merges, type-only: `ctx.remote` and
|
|
14
|
+
* with it the `$on`/`$dispatch` surface. Erased at emit, so this facade still
|
|
15
|
+
* carries no runtime edge to the Gateway implementation.
|
|
16
|
+
*/
|
|
17
|
+
export type {} from '@deepseek-ai/dsh-api-gateway/client';
|
|
6
18
|
declare module '@deepseek-ai/cordis' {
|
|
7
19
|
interface Context {
|
|
8
20
|
/** Generated Remote namespaces selected by this Client assembly. */
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Platform-neutral assembly of generated Host Remote contributions. */
|
|
2
|
+
import goalsRemote from '@deepseek-ai/dsh-goal/remote';
|
|
3
|
+
/** Required service: the typed Client Remote contribution mount. */
|
|
4
|
+
export const inject = ['remote'];
|
|
5
|
+
/**
|
|
6
|
+
* Mount the Host capabilities explicitly selected for this Client assembly.
|
|
7
|
+
* @param ctx - Client Cordis root carrying the typed API service.
|
|
8
|
+
* @returns disposer after every selected Remote namespace is ready.
|
|
9
|
+
*/
|
|
10
|
+
export async function apply(ctx) {
|
|
11
|
+
return await ctx.remote.$mount(goalsRemote);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/** Host BFF entry and Loader shell for the Remote contribution assembly. */
|
|
2
2
|
export { ApiRemoteSessionNotFound, ApiRemoteSubagentSessionOwnership, apiRemoteSubagentOwnershipError, createApiRemoteAgentResolver, hasApiRemoteSubagentOwner, inspectApiRemoteSession, } from './agent-lookup.ts';
|
|
3
3
|
export type { ApiRemoteAgentOptions, ApiRemoteAgentResult, ApiRemoteLookupError, } from './agent-lookup.ts';
|
|
4
|
+
export { API_REMOTE_FORWARDED_EVENTS } from './remote-events.ts';
|
|
5
|
+
export type { ApiRemoteForwardedEvent } from './types.ts';
|
|
4
6
|
/** Host plugin body; the selected contributions mount only in Client environments. */
|
|
5
7
|
export declare function apply(): void;
|
|
6
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Host BFF entry and Loader shell for the Remote contribution assembly. */
|
|
2
|
+
import { API_REMOTE_FORWARDED_EVENTS } from "./remote-events.js";
|
|
3
|
+
export { ApiRemoteSessionNotFound, ApiRemoteSubagentSessionOwnership, apiRemoteSubagentOwnershipError, createApiRemoteAgentResolver, hasApiRemoteSubagentOwner, inspectApiRemoteSession, } from "./agent-lookup.js";
|
|
4
|
+
export { API_REMOTE_FORWARDED_EVENTS } from "./remote-events.js";
|
|
5
|
+
// Shape gate over the allowlist, kept in the Host face because the Host's event
|
|
6
|
+
// vocabulary is the authoritative one. It pins three things at compile time:
|
|
7
|
+
// every entry NAMES a declared event (the predicate is keyed on `keyof
|
|
8
|
+
// Events`), no entry BINDS a Scope (a scoped event's `ThisParameterType` is not
|
|
9
|
+
// `unknown`, which is how "must not depend on AgentScope" is stated statically),
|
|
10
|
+
// and every entry is ONE-WAY (a waterfall or bail shape returns something other
|
|
11
|
+
// than void and is excluded). Widening the array to an event that fails any of
|
|
12
|
+
// these fails here, not on the wire.
|
|
13
|
+
API_REMOTE_FORWARDED_EVENTS;
|
|
14
|
+
/** Host plugin body; the selected contributions mount only in Client environments. */
|
|
15
|
+
export function apply() { }
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Package-owned invariant companion for `@deepseek-ai/dsh-api-remotes`. */
|
|
2
|
+
const PACKAGE_NAME = '@deepseek-ai/dsh-api-remotes';
|
|
3
|
+
/** Cordis companion plugin name. */
|
|
4
|
+
export const name = 'api-remotes-invariant';
|
|
5
|
+
/** Service required before the companion can reserve package ownership. */
|
|
6
|
+
export const inject = ['invariants'];
|
|
7
|
+
/** No runtime invariant: TypeRT and the Agent/Session registries own the observed relationships. */
|
|
8
|
+
const install = () => { };
|
|
9
|
+
/**
|
|
10
|
+
* Register this package's invariant companion.
|
|
11
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
12
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
13
|
+
*/
|
|
14
|
+
export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
15
|
+
/* jscpd:ignore-end */
|
|
16
|
+
//# sourceMappingURL=invariant.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one home of this application's forwarded-Host-event allowlist. Both
|
|
3
|
+
* compiler faces list this file, so the Host forwarding loop and the consumer
|
|
4
|
+
* `ctx.remote.$on` key face read one declaration instead of two copies that
|
|
5
|
+
* could drift; `./types.ts` derives the type projection from it and stays
|
|
6
|
+
* type-only.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Host events this application forwards to consumers verbatim: no projection,
|
|
10
|
+
* no redaction, no renaming. The wire name is the Host cordis event name and
|
|
11
|
+
* the payload is its argument list, so this array is simultaneously the whole
|
|
12
|
+
* control point over what a consumer can receive and the legal key set of
|
|
13
|
+
* `ctx.remote.$on`. Forwarding one more event is an entry here and nothing
|
|
14
|
+
* else.
|
|
15
|
+
*/
|
|
16
|
+
export declare const API_REMOTE_FORWARDED_EVENTS: readonly ["agent-preset/selected", "commands/change", "credentials/updated", "llm/adapters-updated", "settings/document-updated"];
|
|
17
|
+
//# sourceMappingURL=remote-events.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one home of this application's forwarded-Host-event allowlist. Both
|
|
3
|
+
* compiler faces list this file, so the Host forwarding loop and the consumer
|
|
4
|
+
* `ctx.remote.$on` key face read one declaration instead of two copies that
|
|
5
|
+
* could drift; `./types.ts` derives the type projection from it and stays
|
|
6
|
+
* type-only.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Host events this application forwards to consumers verbatim: no projection,
|
|
10
|
+
* no redaction, no renaming. The wire name is the Host cordis event name and
|
|
11
|
+
* the payload is its argument list, so this array is simultaneously the whole
|
|
12
|
+
* control point over what a consumer can receive and the legal key set of
|
|
13
|
+
* `ctx.remote.$on`. Forwarding one more event is an entry here and nothing
|
|
14
|
+
* else.
|
|
15
|
+
*/
|
|
16
|
+
export const API_REMOTE_FORWARDED_EVENTS = [
|
|
17
|
+
'agent-preset/selected',
|
|
18
|
+
'commands/change',
|
|
19
|
+
'credentials/updated',
|
|
20
|
+
'llm/adapters-updated',
|
|
21
|
+
'settings/document-updated',
|
|
22
|
+
];
|
|
23
|
+
//# sourceMappingURL=remote-events.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type face of the forwarded-Host-event allowlist: the consumer key projection
|
|
3
|
+
* and the selection seat it fills. The allowlist VALUE lives in
|
|
4
|
+
* `./remote-events.ts`, keeping this module type-only per the package
|
|
5
|
+
* convention; both compiler faces list both files, so the Host forwarding loop
|
|
6
|
+
* and the consumer `ctx.remote.$on` key face read one declaration instead of
|
|
7
|
+
* two copies that could drift.
|
|
8
|
+
*
|
|
9
|
+
* @module @deepseek-ai/dsh-api-remotes/types
|
|
10
|
+
*/
|
|
11
|
+
import type { API_REMOTE_FORWARDED_EVENTS } from './remote-events.ts';
|
|
12
|
+
/** Type projection of the allowlist; the consumer and the Host read this one. */
|
|
13
|
+
export type ApiRemoteForwardedEvent = typeof API_REMOTE_FORWARDED_EVENTS[number];
|
|
14
|
+
declare module '@deepseek-ai/dsh-type-meta' {
|
|
15
|
+
interface TypeRTRemoteEventSelection extends Record<ApiRemoteForwardedEvent, true> {
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type face of the forwarded-Host-event allowlist: the consumer key projection
|
|
3
|
+
* and the selection seat it fills. The allowlist VALUE lives in
|
|
4
|
+
* `./remote-events.ts`, keeping this module type-only per the package
|
|
5
|
+
* convention; both compiler faces list both files, so the Host forwarding loop
|
|
6
|
+
* and the consumer `ctx.remote.$on` key face read one declaration instead of
|
|
7
|
+
* two copies that could drift.
|
|
8
|
+
*
|
|
9
|
+
* @module @deepseek-ai/dsh-api-remotes/types
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=types.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-api-remotes",
|
|
3
3
|
"description": "Remote BFF assembly and Host Agent/Session lookup policy",
|
|
4
|
-
"version": "0.0.1-rc.
|
|
4
|
+
"version": "0.0.1-rc.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "restricted"
|
|
7
7
|
},
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"types": "./lib/types/client/index.d.ts",
|
|
27
27
|
"default": "./lib/client.js"
|
|
28
28
|
},
|
|
29
|
+
"./types": {
|
|
30
|
+
"types": "./lib/types/types.d.ts",
|
|
31
|
+
"default": "./lib/types/types.js"
|
|
32
|
+
},
|
|
29
33
|
"./src/*": "./src/*",
|
|
30
34
|
"./package.json": "./package.json"
|
|
31
35
|
},
|
|
@@ -43,28 +47,41 @@
|
|
|
43
47
|
"lib/index.js",
|
|
44
48
|
"lib/invariant.js",
|
|
45
49
|
"lib/client.js",
|
|
50
|
+
"lib/types/**/*.js",
|
|
46
51
|
"lib/types/**/*.d.ts"
|
|
47
52
|
],
|
|
48
53
|
"dependencies": {
|
|
49
|
-
"@deepseek-ai/dsh-type-meta": "^0.0.1-rc.
|
|
54
|
+
"@deepseek-ai/dsh-type-meta": "^0.0.1-rc.2"
|
|
50
55
|
},
|
|
51
56
|
"peerDependencies": {
|
|
52
|
-
"@deepseek-ai/
|
|
53
|
-
"@deepseek-ai/dsh-
|
|
54
|
-
"@deepseek-ai/dsh-
|
|
55
|
-
"@deepseek-ai/dsh-
|
|
56
|
-
"@deepseek-ai/dsh-
|
|
57
|
-
"@deepseek-ai/dsh-
|
|
58
|
-
"@deepseek-ai/
|
|
57
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.1",
|
|
58
|
+
"@deepseek-ai/dsh-api-gateway": "^0.0.1-rc.2",
|
|
59
|
+
"@deepseek-ai/dsh-commands": "^0.0.1-rc.2",
|
|
60
|
+
"@deepseek-ai/dsh-credentials": "^0.0.1-rc.2",
|
|
61
|
+
"@deepseek-ai/dsh-goal": "^0.0.1-rc.2",
|
|
62
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.2",
|
|
63
|
+
"@deepseek-ai/dsh-agent": "^0.0.1-rc.2",
|
|
64
|
+
"@deepseek-ai/dsh-llm": "^0.0.1-rc.2",
|
|
65
|
+
"@deepseek-ai/dsh-session": "^0.0.1-rc.2",
|
|
66
|
+
"@deepseek-ai/dsh-session-persistence": "^0.0.1-rc.2",
|
|
67
|
+
"@deepseek-ai/dsh-settings": "^0.0.1-rc.2",
|
|
68
|
+
"@deepseek-ai/dsh-typert-registry": "^0.0.1-rc.2",
|
|
69
|
+
"@deepseek-ai/dsh-agent-presets": "^0.0.1-rc.2"
|
|
59
70
|
},
|
|
60
71
|
"devDependencies": {
|
|
61
|
-
"@deepseek-ai/dsh-
|
|
62
|
-
"@deepseek-ai/dsh-
|
|
63
|
-
"@deepseek-ai/dsh-
|
|
64
|
-
"@deepseek-ai/dsh-
|
|
65
|
-
"@deepseek-ai/dsh-
|
|
66
|
-
"@deepseek-ai/dsh-
|
|
67
|
-
"@deepseek-ai/
|
|
72
|
+
"@deepseek-ai/dsh-api-gateway": "^0.0.1-rc.2",
|
|
73
|
+
"@deepseek-ai/dsh-agent": "^0.0.1-rc.2",
|
|
74
|
+
"@deepseek-ai/dsh-commands": "^0.0.1-rc.2",
|
|
75
|
+
"@deepseek-ai/dsh-credentials": "^0.0.1-rc.2",
|
|
76
|
+
"@deepseek-ai/dsh-goal": "^0.0.1-rc.2",
|
|
77
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.2",
|
|
78
|
+
"@deepseek-ai/dsh-llm": "^0.0.1-rc.2",
|
|
79
|
+
"@deepseek-ai/dsh-agent-presets": "^0.0.1-rc.2",
|
|
80
|
+
"@deepseek-ai/dsh-session": "^0.0.1-rc.2",
|
|
81
|
+
"@deepseek-ai/dsh-settings": "^0.0.1-rc.2",
|
|
82
|
+
"@deepseek-ai/dsh-typert-registry": "^0.0.1-rc.2",
|
|
83
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.1",
|
|
84
|
+
"@deepseek-ai/dsh-session-persistence": "^0.0.1-rc.2"
|
|
68
85
|
},
|
|
69
86
|
"scripts": {
|
|
70
87
|
"bundle": "tsdown",
|