@klarkxy/dsh-ai-services 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,41 @@
1
+ The Star And Thank Author License (SATA)
2
+ Version 2.1, September 2026
3
+
4
+ Copyright © 2026 Klarkxy(278370456@qq.com)
5
+
6
+ Project Url: https://github.com/klarkxy/dsh-editor
7
+ https://github.com/klarkxy/sata-license
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in
17
+ all copies or substantial portions of the Software.
18
+
19
+ And wait, one more thing — an invitation, not a condition: it would be best if
20
+ you star/+1/like the project(s) in the project url section above, and thank the
21
+ author(s) in the Copyright section. You may, and arguably you should — but the
22
+ permissions above hold either way, star or no star.
23
+
24
+ Here are some suggested ways:
25
+
26
+ - Email the authors a thank-you letter, and make friends with him/her/them.
27
+ - Report bugs or issues.
28
+ - Tell friends what a wonderful project this is.
29
+ - And, sure, you can just express thanks in your mind without telling the world.
30
+
31
+ Contributors of this project by forking have the option to add his/her name and
32
+ forked project url at copyright and project url sections, but shall not delete
33
+ or modify anything else in these two sections.
34
+
35
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
41
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # @klarkxy/dsh-ai-services
2
+
3
+ Shared Cordis service for model-role routing, bounded auxiliary calls, cancellation, and usage receipts. It is a supporting service, not a user-facing feature. Loading it does not start inference.
4
+
5
+ [简体中文](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-ai-services/docs/README.zh-CN.md)
6
+
7
+ ## Install
8
+
9
+ Requires Node.js ≥22 and DSH `0.1.7-alpha.1`. The host must provide `llm`, `storageDomain`, `connection`, and `webServer`.
10
+
11
+ ```sh
12
+ dsh plugin --profile web add @klarkxy/dsh-ai-services
13
+ ```
14
+
15
+ DSH Editor includes this package as a locked core service. Feature plugins call `ctx.aiServices.activate(plugin)` when they need a model; this package never calls the model by itself.
16
+
17
+ ## Routing
18
+
19
+ Roles `weak`, `normal`, `strong`, and `fantasy` represent Quick, Chat, Thinking, and Fantasy (the Haiku, Sonnet, Opus, and Fable tiers respectively). They can bind models from any provider. Built-in plugins never default to Fantasy; it is reserved for explicit user selection. Unbound tiers inherit Chat, falling back to the host default chat model if Chat is unset. Without Model Center, tier-based calls use Chat or the host default directly; saved tier bindings remain intact. The host default is read on each call, so standalone plugins need no Editor initialization. A missing default chat model fails visibly. An explicit invalid provider/model/reasoning choice fails without switching to another route. Session targets use the native Host projection, the request header, and the host default model; they do not call the removed `session.models` RPC.
20
+
21
+ Policy is one storage-domain record with compare-and-swap revision. It does not copy provider credentials. Manuscript completion and rewrite share this policy; existing Editor settings are imported once, and generated text remains a candidate until the author accepts it.
22
+
23
+ Auxiliary calls are plain text with no tools. Limits cap input size, output tokens, wall time, attempts, and per-provider concurrency. Interactive work is queued ahead of background work. Native agent-loop requests, when present, occupy the same provider’s foreground so background jobs wait.
24
+
25
+ Usage receipts store token counts when the adapter reports them. Cost is `null` when unknown. Prompts and secrets are not stored.
26
+
27
+ Host RPC is `/dsh-ai-services` (`status`, `update`, `resolve`, `usage`) and requires the host authorization policy.
28
+
29
+ From the repository root:
30
+
31
+ ```sh
32
+ pnpm --filter @klarkxy/dsh-ai-services typecheck
33
+ pnpm exec vitest run packages/dsh-ai-services/src
34
+ pnpm --filter @klarkxy/dsh-ai-services build
35
+ ```
36
+
37
+ [Publishing](https://github.com/klarkxy/dsh-editor/blob/main/packages/PUBLISHING.md) · [License](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-ai-services/LICENSE)
38
+
39
+ Manuscript completion and rewrite share purpose routing, cancellation, quotas and receipts. Host compatibility imports use `importPurposes` to persist missing defaults and a one-time marker atomically. `run({ insert: { maxChars } })` supports bounded plain-writing candidates; other auxiliary calls still require a successful complete stream.
@@ -0,0 +1,4 @@
1
+ # Supporting service: inserted enabled. Features must call aiServices explicitly; this plugin never starts inference on load.
2
+ - insert:
3
+ - id: ai-services
4
+ name: '@klarkxy/dsh-ai-services'
@@ -0,0 +1,9 @@
1
+ # @klarkxy/dsh-ai-services
2
+
3
+ 插件共用的 Cordis 服务:模型角色路由、有界辅助调用、取消与用量回执。这是支撑服务,不是面向用户的功能。装上后不会自行发起推理。
4
+
5
+ [English](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-ai-services/README.md)
6
+
7
+ 需要 Node.js ≥22、DSH `0.1.7-alpha.1`。宿主需提供 `llm`、`storageDomain`、`connection`、`webServer`。功能插件通过 `ctx.aiServices.activate(plugin)` 显式调用;稿纸补全与改写也使用本服务,保留原来的设置入口,配置统一写入用途策略。
8
+
9
+ 快速/对话/思考/幻想分别对应 Haiku/Sonnet/Opus/Fable 的档位定位,可绑定任意供应商模型。内置插件不默认使用幻想档,只有用户显式选择时才使用。档位未配置时使用对话档,再回退到宿主默认对话模型;未安装或停用模型管理器时,档位调用直接使用默认对话模型,保存的其他档位保持不变。宿主默认模型按次读取,独立插件无需 Editor 初始化即可调用。手动指定的模型或会话选择保持有效;显式无效配置仍报错。
@@ -0,0 +1,52 @@
1
+ //#region src/client-utils.d.ts
2
+ interface NativeSurfaceClient {
3
+ sessions: {
4
+ binding?(sessionId: string): {
5
+ eventSource: {
6
+ subscribe(listener: () => void): () => void;
7
+ };
8
+ } | undefined;
9
+ };
10
+ uiWorkspace: {
11
+ current?: {
12
+ getSnapshot(): {
13
+ sessionId: string;
14
+ } | undefined;
15
+ subscribe(listener: () => void): () => void;
16
+ };
17
+ };
18
+ uiSession: {
19
+ adapter: {
20
+ current: {
21
+ getSnapshot(): {
22
+ key?: string;
23
+ };
24
+ subscribe(listener: () => void): () => void;
25
+ };
26
+ };
27
+ };
28
+ locale: {
29
+ getSnapshot(): {
30
+ active: string;
31
+ };
32
+ subscribe(listener: () => void): () => void;
33
+ };
34
+ connection?: {
35
+ generation?: {
36
+ subscribe(listener: () => void): () => void;
37
+ };
38
+ };
39
+ }
40
+ /** The Editor retains its manuscript session separately; native DSH exposes the main-view binding. */
41
+ declare function selectedSessionId(client: Pick<NativeSurfaceClient, 'uiWorkspace' | 'uiSession'>): string;
42
+ /** Owner props win; native settings only supplies close, so resolve its selected session. */
43
+ declare function useNativeSeat(client: NativeSurfaceClient, props: unknown): {
44
+ sessionId: string;
45
+ locale: "zh" | "en";
46
+ hidden: boolean;
47
+ };
48
+ /** Refresh on native log boundaries/reconnect/focus. Read-only settling is bounded to an observed job. */
49
+ declare function useFeatureRefresh(client: NativeSurfaceClient, sessionId: string, refresh: () => void, working?: boolean, enabled?: boolean): void;
50
+ //#endregion
51
+ export { NativeSurfaceClient, selectedSessionId, useFeatureRefresh, useNativeSeat };
52
+ //# sourceMappingURL=client-utils.d.ts.map
@@ -0,0 +1,77 @@
1
+ import { useCallback, useEffect, useRef, useSyncExternalStore } from "react";
2
+ //#region src/client-utils.ts
3
+ /** The Editor retains its manuscript session separately; native DSH exposes the main-view binding. */
4
+ function selectedSessionId(client) {
5
+ return client.uiWorkspace.current ? client.uiWorkspace.current.getSnapshot()?.sessionId ?? "" : client.uiSession.adapter.current.getSnapshot().key ?? "";
6
+ }
7
+ /** Owner props win; native settings only supplies close, so resolve its selected session. */
8
+ function useNativeSeat(client, props) {
9
+ const current = useSyncExternalStore(useCallback((fn) => (client.uiWorkspace.current ?? client.uiSession.adapter.current).subscribe(fn), [client]), useCallback(() => selectedSessionId(client), [client]), () => "");
10
+ const language = useSyncExternalStore(useCallback((fn) => client.locale.subscribe(fn), [client]), useCallback(() => client.locale.getSnapshot().active, [client]), () => "en");
11
+ const row = props && typeof props === "object" ? props : {};
12
+ const owner = row.owner && typeof row.owner === "object" ? row.owner : {};
13
+ const sessionId = typeof row.sessionId === "string" ? row.sessionId : typeof owner.sessionId === "string" ? owner.sessionId : current;
14
+ const requestedLocale = row.locale ?? owner.locale ?? language;
15
+ return {
16
+ sessionId,
17
+ locale: String(requestedLocale).startsWith("zh") ? "zh" : "en",
18
+ hidden: row.hidden === true || owner.hidden === true
19
+ };
20
+ }
21
+ /** Refresh on native log boundaries/reconnect/focus. Read-only settling is bounded to an observed job. */
22
+ function useFeatureRefresh(client, sessionId, refresh, working = false, enabled = true) {
23
+ const refreshRef = useRef(refresh);
24
+ refreshRef.current = refresh;
25
+ useEffect(() => {
26
+ if (!enabled) return;
27
+ let timer;
28
+ const queue = () => {
29
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
30
+ clearTimeout(timer);
31
+ timer = setTimeout(() => refreshRef.current(), 150);
32
+ };
33
+ const stopEvents = (sessionId ? client.sessions.binding?.(sessionId)?.eventSource : void 0)?.subscribe(queue);
34
+ const stopConnection = client.connection?.generation?.subscribe(queue);
35
+ const visibility = () => {
36
+ if (document.visibilityState === "visible") queue();
37
+ };
38
+ window.addEventListener("focus", queue);
39
+ document.addEventListener("visibilitychange", visibility);
40
+ document.addEventListener("toggle", queue, true);
41
+ return () => {
42
+ clearTimeout(timer);
43
+ stopEvents?.();
44
+ stopConnection?.();
45
+ window.removeEventListener("focus", queue);
46
+ document.removeEventListener("visibilitychange", visibility);
47
+ document.removeEventListener("toggle", queue, true);
48
+ };
49
+ }, [
50
+ client,
51
+ sessionId,
52
+ enabled
53
+ ]);
54
+ useEffect(() => {
55
+ if (!enabled || !working) return;
56
+ let delay = 250;
57
+ const until = Date.now() + 3e5;
58
+ let timer;
59
+ const tick = () => {
60
+ if (Date.now() >= until) return;
61
+ if (document.visibilityState !== "hidden") refreshRef.current();
62
+ delay = Math.min(delay * 2, 2e3);
63
+ timer = setTimeout(tick, delay);
64
+ };
65
+ timer = setTimeout(tick, delay);
66
+ return () => clearTimeout(timer);
67
+ }, [
68
+ client,
69
+ sessionId,
70
+ working,
71
+ enabled
72
+ ]);
73
+ }
74
+ //#endregion
75
+ export { selectedSessionId, useFeatureRefresh, useNativeSeat };
76
+
77
+ //# sourceMappingURL=client-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-utils.js","names":[],"sources":["../src/client-utils.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useSyncExternalStore } from 'react'\n\nexport interface NativeSurfaceClient {\n sessions: {\n binding?(sessionId: string): { eventSource: { subscribe(listener: () => void): () => void } } | undefined\n }\n uiWorkspace: { current?: { getSnapshot(): { sessionId: string } | undefined; subscribe(listener: () => void): () => void } }\n uiSession: { adapter: { current: { getSnapshot(): { key?: string }; subscribe(listener: () => void): () => void } } }\n locale: { getSnapshot(): { active: string }; subscribe(listener: () => void): () => void }\n connection?: { generation?: { subscribe(listener: () => void): () => void } }\n}\n\n/** The Editor retains its manuscript session separately; native DSH exposes the main-view binding. */\nexport function selectedSessionId(client: Pick<NativeSurfaceClient, 'uiWorkspace' | 'uiSession'>): string {\n return client.uiWorkspace.current\n ? client.uiWorkspace.current.getSnapshot()?.sessionId ?? ''\n : client.uiSession.adapter.current.getSnapshot().key ?? ''\n}\n\n/** Owner props win; native settings only supplies close, so resolve its selected session. */\nexport function useNativeSeat(client: NativeSurfaceClient, props: unknown) {\n const current = useSyncExternalStore(\n useCallback((fn: () => void) => (client.uiWorkspace.current ?? client.uiSession.adapter.current).subscribe(fn), [client]),\n useCallback(() => selectedSessionId(client), [client]),\n () => '',\n )\n const language = useSyncExternalStore(\n useCallback((fn: () => void) => client.locale.subscribe(fn), [client]),\n useCallback(() => client.locale.getSnapshot().active, [client]),\n () => 'en',\n )\n const row = props && typeof props === 'object' ? props as Record<string, unknown> : {}\n const owner = row.owner && typeof row.owner === 'object' ? row.owner as Record<string, unknown> : {}\n const sessionId = typeof row.sessionId === 'string' ? row.sessionId\n : typeof owner.sessionId === 'string' ? owner.sessionId : current\n const requestedLocale = row.locale ?? owner.locale ?? language\n return { sessionId, locale: (String(requestedLocale).startsWith('zh') ? 'zh' : 'en') as 'zh' | 'en',\n hidden: row.hidden === true || owner.hidden === true }\n}\n\n/** Refresh on native log boundaries/reconnect/focus. Read-only settling is bounded to an observed job. */\nexport function useFeatureRefresh(\n client: NativeSurfaceClient, sessionId: string, refresh: () => void,\n working = false, enabled = true,\n): void {\n const refreshRef = useRef(refresh)\n refreshRef.current = refresh\n useEffect(() => {\n if (!enabled) return\n let timer: ReturnType<typeof setTimeout> | undefined\n const queue = () => {\n if (typeof document !== 'undefined' && document.visibilityState === 'hidden') return\n clearTimeout(timer)\n timer = setTimeout(() => refreshRef.current(), 150)\n }\n const source = sessionId ? client.sessions.binding?.(sessionId)?.eventSource : undefined\n const stopEvents = source?.subscribe(queue)\n const stopConnection = client.connection?.generation?.subscribe(queue)\n const visibility = () => { if (document.visibilityState === 'visible') queue() }\n window.addEventListener('focus', queue)\n document.addEventListener('visibilitychange', visibility)\n // Details are collapsed by default; opening an existing panel must get current state.\n document.addEventListener('toggle', queue, true)\n return () => {\n clearTimeout(timer)\n stopEvents?.(); stopConnection?.()\n window.removeEventListener('focus', queue)\n document.removeEventListener('visibilitychange', visibility)\n document.removeEventListener('toggle', queue, true)\n }\n }, [client, sessionId, enabled])\n useEffect(() => {\n if (!enabled || !working) return\n let delay = 250\n const until = Date.now() + 300_000\n let timer: ReturnType<typeof setTimeout>\n const tick = () => {\n if (Date.now() >= until) return\n if (document.visibilityState !== 'hidden') refreshRef.current()\n delay = Math.min(delay * 2, 2000)\n timer = setTimeout(tick, delay)\n }\n timer = setTimeout(tick, delay)\n return () => clearTimeout(timer)\n }, [client, sessionId, working, enabled])\n}\n"],"mappings":";;;AAaA,SAAgB,kBAAkB,QAAwE;CACxG,OAAO,OAAO,YAAY,UACtB,OAAO,YAAY,QAAQ,YAAY,CAAC,EAAE,aAAa,KACvD,OAAO,UAAU,QAAQ,QAAQ,YAAY,CAAC,CAAC,OAAO;AAC5D;;AAGA,SAAgB,cAAc,QAA6B,OAAgB;CACzE,MAAM,UAAU,qBACd,aAAa,QAAoB,OAAO,YAAY,WAAW,OAAO,UAAU,QAAQ,QAAA,CAAS,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GACxH,kBAAkB,kBAAkB,MAAM,GAAG,CAAC,MAAM,CAAC,SAC/C,EACR;CACA,MAAM,WAAW,qBACf,aAAa,OAAmB,OAAO,OAAO,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GACrE,kBAAkB,OAAO,OAAO,YAAY,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,SACxD,IACR;CACA,MAAM,MAAM,SAAS,OAAO,UAAU,WAAW,QAAmC,CAAC;CACrF,MAAM,QAAQ,IAAI,SAAS,OAAO,IAAI,UAAU,WAAW,IAAI,QAAmC,CAAC;CACnG,MAAM,YAAY,OAAO,IAAI,cAAc,WAAW,IAAI,YACtD,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY;CAC5D,MAAM,kBAAkB,IAAI,UAAU,MAAM,UAAU;CACtD,OAAO;EAAE;EAAW,QAAS,OAAO,eAAe,CAAC,CAAC,WAAW,IAAI,IAAI,OAAO;EAC7E,QAAQ,IAAI,WAAW,QAAQ,MAAM,WAAW;CAAK;AACzD;;AAGA,SAAgB,kBACd,QAA6B,WAAmB,SAChD,UAAU,OAAO,UAAU,MACrB;CACN,MAAM,aAAa,OAAO,OAAO;CACjC,WAAW,UAAU;CACrB,gBAAgB;EACd,IAAI,CAAC,SAAS;EACd,IAAI;EACJ,MAAM,cAAc;GAClB,IAAI,OAAO,aAAa,eAAe,SAAS,oBAAoB,UAAU;GAC9E,aAAa,KAAK;GAClB,QAAQ,iBAAiB,WAAW,QAAQ,GAAG,GAAG;EACpD;EAEA,MAAM,cADS,YAAY,OAAO,SAAS,UAAU,SAAS,CAAC,EAAE,cAAc,KAAA,EAAA,EACpD,UAAU,KAAK;EAC1C,MAAM,iBAAiB,OAAO,YAAY,YAAY,UAAU,KAAK;EACrE,MAAM,mBAAmB;GAAE,IAAI,SAAS,oBAAoB,WAAW,MAAM;EAAE;EAC/E,OAAO,iBAAiB,SAAS,KAAK;EACtC,SAAS,iBAAiB,oBAAoB,UAAU;EAExD,SAAS,iBAAiB,UAAU,OAAO,IAAI;EAC/C,aAAa;GACX,aAAa,KAAK;GAClB,aAAa;GAAG,iBAAiB;GACjC,OAAO,oBAAoB,SAAS,KAAK;GACzC,SAAS,oBAAoB,oBAAoB,UAAU;GAC3D,SAAS,oBAAoB,UAAU,OAAO,IAAI;EACpD;CACF,GAAG;EAAC;EAAQ;EAAW;CAAO,CAAC;CAC/B,gBAAgB;EACd,IAAI,CAAC,WAAW,CAAC,SAAS;EAC1B,IAAI,QAAQ;EACZ,MAAM,QAAQ,KAAK,IAAI,IAAI;EAC3B,IAAI;EACJ,MAAM,aAAa;GACjB,IAAI,KAAK,IAAI,KAAK,OAAO;GACzB,IAAI,SAAS,oBAAoB,UAAU,WAAW,QAAQ;GAC9D,QAAQ,KAAK,IAAI,QAAQ,GAAG,GAAI;GAChC,QAAQ,WAAW,MAAM,KAAK;EAChC;EACA,QAAQ,WAAW,MAAM,KAAK;EAC9B,aAAa,aAAa,KAAK;CACjC,GAAG;EAAC;EAAQ;EAAW;EAAS;CAAO,CAAC;AAC1C"}
@@ -0,0 +1,230 @@
1
+ import { ContextFormed } from "@deepseek-ai/dsh-llm";
2
+ //#region src/contracts.d.ts
3
+ /** Public, browser-safe contracts. Host implementations stay in their owning packages. */
4
+ type ProducerSourceKind = `plugin:${string}`;
5
+ type ProducerMessageSource = {
6
+ kind: ProducerSourceKind;
7
+ plugin: string;
8
+ } & ContextFormed;
9
+ /**
10
+ * V4 sessions persist the producing extension in `kind`; the retired generic
11
+ * `plugin` wrapper is not admitted. The AI service is the shared producer for
12
+ * feature scopes, so its map must cover every registered feature id.
13
+ */
14
+ declare module '@deepseek-ai/dsh-llm' {
15
+ interface MessageSourceMap {
16
+ [kind: ProducerSourceKind]: ProducerMessageSource;
17
+ }
18
+ }
19
+ declare function producerMessageSource(plugin: string): ProducerMessageSource;
20
+ type ModelRole = 'normal' | 'weak' | 'strong' | 'fantasy';
21
+ interface ModelRoute {
22
+ provider: string;
23
+ model: string;
24
+ reasoningEffort?: string;
25
+ }
26
+ type ModelTarget = {
27
+ kind: 'role';
28
+ role: ModelRole;
29
+ } | {
30
+ kind: 'session';
31
+ } | ({
32
+ kind: 'model';
33
+ } & ModelRoute);
34
+ interface AiPolicy {
35
+ revision: number;
36
+ roles: Partial<Record<ModelRole, ModelRoute>>;
37
+ purposes: Record<string, ModelTarget>;
38
+ limits: {
39
+ concurrency: number;
40
+ timeoutMs: number;
41
+ maxInputChars: number;
42
+ maxOutputTokens: number;
43
+ maxAttempts: number;
44
+ };
45
+ }
46
+ interface PurposeSpec {
47
+ id: string;
48
+ label: string;
49
+ defaultTarget: ModelTarget;
50
+ maxOutputTokens?: number;
51
+ maxInputChars?: number;
52
+ timeoutMs?: number;
53
+ }
54
+ interface ResolvedRoute extends ModelRoute {
55
+ source: 'override' | 'purpose' | 'default';
56
+ target: ModelTarget;
57
+ policyRevision: number;
58
+ inheritedRole?: ModelRole;
59
+ }
60
+ interface AuxiliaryRequest {
61
+ purpose: string;
62
+ sessionId?: string;
63
+ input: string;
64
+ system: string;
65
+ sourceVersion: string;
66
+ contractVersion?: number;
67
+ promptVersion?: string;
68
+ schemaVersion?: string;
69
+ override?: ModelTarget;
70
+ signal?: AbortSignal;
71
+ /** Rechecked after every await and before returning a publishable result. */
72
+ isCurrent?: () => boolean;
73
+ priority?: 'interactive' | 'background';
74
+ /** Plain writing candidates can stop once this many visible characters are available. */
75
+ insert?: {
76
+ maxChars: number;
77
+ };
78
+ }
79
+ interface UsageReceipt {
80
+ id: string;
81
+ plugin: string;
82
+ purpose: string;
83
+ sessionId?: string;
84
+ sourceVersion: string;
85
+ contractVersion?: number;
86
+ promptVersion?: string;
87
+ schemaVersion?: string;
88
+ route?: ResolvedRoute;
89
+ status: 'success' | 'failed' | 'cancelled' | 'superseded' | 'skipped';
90
+ attempts: number;
91
+ inputTokens?: number;
92
+ outputTokens?: number;
93
+ cost: number | null;
94
+ startedAt: number;
95
+ finishedAt: number;
96
+ error?: string;
97
+ }
98
+ interface AuxiliaryResult {
99
+ text: string;
100
+ receipt: UsageReceipt;
101
+ }
102
+ interface AiFeatureScope {
103
+ readonly plugin: string;
104
+ readonly signal: AbortSignal;
105
+ readonly active: boolean;
106
+ registerPurpose(spec: PurposeSpec): () => void;
107
+ run(request: AuxiliaryRequest): Promise<AuxiliaryResult>;
108
+ dispose(): void;
109
+ }
110
+ interface AiServices {
111
+ activate(plugin: string): AiFeatureScope;
112
+ /** Atomically import missing purpose defaults once; host-only compatibility entrypoint. */
113
+ importPurposes(migrationId: string, defaults: Record<string, ModelTarget>, roles?: AiPolicy['roles']): Promise<AiPolicy>;
114
+ getPolicy(): AiPolicy;
115
+ updatePolicy(policy: Omit<AiPolicy, 'revision'>, expectedRevision: number): Promise<AiPolicy>;
116
+ resolve(purpose: string, sessionId?: string, override?: ModelTarget): Promise<ResolvedRoute>;
117
+ purposes(): Array<PurposeSpec & {
118
+ plugin: string;
119
+ }>;
120
+ usage(): UsageReceipt[];
121
+ }
122
+ type RpcResult<T = unknown> = {
123
+ ok: true;
124
+ value: T;
125
+ } | {
126
+ ok: false;
127
+ error: {
128
+ code: string;
129
+ message: string;
130
+ };
131
+ };
132
+ declare const AI_RPC_CHANNEL = "/dsh-ai-services";
133
+ declare const CHAT_EVENTS_SLOT = "dsh-editor.chat.events";
134
+ declare const MODEL_SETTINGS_SLOT = "dsh-editor.settings.models";
135
+ interface EvidenceRef {
136
+ sessionId: string;
137
+ seq: number;
138
+ kind: 'user' | 'tool' | 'turn' | 'manual';
139
+ excerpt?: string;
140
+ }
141
+ interface TaskContract {
142
+ id: string;
143
+ sessionId: string;
144
+ sourceVersion: string;
145
+ revision: number;
146
+ goal: string;
147
+ deliverables: string[];
148
+ inScope: string[];
149
+ outOfScope: string[];
150
+ constraints: string[];
151
+ acceptance: string[];
152
+ assumptions: string[];
153
+ questions: string[];
154
+ evidence: EvidenceRef[];
155
+ readiness: 'pending' | 'clear-request' | 'user-confirmed' | 'disclosed-assumptions' | 'cancelled' | 'stale';
156
+ updatedAt: number;
157
+ }
158
+ interface TaskCheckpoint {
159
+ id: string;
160
+ sessionId: string;
161
+ sourceVersion: string;
162
+ contractVersion?: number;
163
+ fromSeq: number;
164
+ toSeq: number;
165
+ revision: number;
166
+ status: 'running' | 'completed' | 'failed' | 'cancelled';
167
+ items: Array<{
168
+ label: string;
169
+ state: 'pending' | 'verified' | 'failed';
170
+ evidence: EvidenceRef[];
171
+ }>;
172
+ constraints: string[];
173
+ nextAction: string;
174
+ createdAt: number;
175
+ }
176
+ type KnowledgeScope = {
177
+ kind: 'global';
178
+ } | {
179
+ kind: 'project';
180
+ projectId: string;
181
+ };
182
+ type KnowledgeKind = 'preference' | 'project-fact' | 'decision' | 'lesson';
183
+ interface MemoryRecord {
184
+ id: string;
185
+ revision: number;
186
+ scope: KnowledgeScope;
187
+ kind: KnowledgeKind;
188
+ status: 'candidate' | 'active' | 'rejected' | 'superseded' | 'revoked' | 'deleted';
189
+ title: string;
190
+ content: string;
191
+ tags: string[];
192
+ evidence: EvidenceRef[];
193
+ exceptions: string[];
194
+ source: 'user' | 'memory' | 'dream' | 'self-improvement';
195
+ createdAt: number;
196
+ updatedAt: number;
197
+ expiresAt?: number;
198
+ supersedes?: string[];
199
+ /** Exact record revisions behind a derived candidate; revalidated on adoption. */
200
+ basis?: Array<{
201
+ id: string;
202
+ revision: number;
203
+ }>;
204
+ }
205
+ type NewMemoryRecord = Omit<MemoryRecord, 'id' | 'revision' | 'createdAt' | 'updatedAt'>;
206
+ interface MemoryQuery {
207
+ scope: KnowledgeScope;
208
+ query?: string;
209
+ kinds?: KnowledgeKind[];
210
+ statuses?: MemoryRecord['status'][];
211
+ limit?: number;
212
+ }
213
+ interface MemoryMutationOptions {
214
+ signal?: AbortSignal;
215
+ isCurrent?: () => boolean;
216
+ }
217
+ interface MemoryService {
218
+ list(query: MemoryQuery): Promise<MemoryRecord[]>;
219
+ create(record: NewMemoryRecord, options?: MemoryMutationOptions): Promise<MemoryRecord>;
220
+ update(id: string, patch: Partial<Pick<MemoryRecord, 'title' | 'content' | 'tags' | 'exceptions' | 'status' | 'expiresAt'>>, expectedRevision: number, options?: MemoryMutationOptions): Promise<MemoryRecord>;
221
+ /** Explicit user promotion, persisted atomically on the same record; preserves expiry and evidence. */
222
+ promoteToGlobal(id: string, expectedRevision: number, options?: MemoryMutationOptions): Promise<MemoryRecord>;
223
+ remove(id: string, expectedRevision: number, options?: MemoryMutationOptions): Promise<void>;
224
+ recall(query: MemoryQuery): Promise<MemoryRecord[]>;
225
+ }
226
+ /** Project keys come only from the host-validated session directory, never client input. */
227
+ declare function projectIdFromCwd(cwd: string | undefined): string | undefined;
228
+ //#endregion
229
+ export { AI_RPC_CHANNEL, AiFeatureScope, AiPolicy, AiServices, AuxiliaryRequest, AuxiliaryResult, CHAT_EVENTS_SLOT, EvidenceRef, KnowledgeKind, KnowledgeScope, MODEL_SETTINGS_SLOT, MemoryMutationOptions, MemoryQuery, MemoryRecord, MemoryService, ModelRole, ModelRoute, ModelTarget, NewMemoryRecord, ProducerMessageSource, ProducerSourceKind, PurposeSpec, ResolvedRoute, RpcResult, TaskCheckpoint, TaskContract, UsageReceipt, producerMessageSource, projectIdFromCwd };
230
+ //# sourceMappingURL=contracts.d.ts.map
@@ -0,0 +1,21 @@
1
+ //#region src/contracts.ts
2
+ function producerMessageSource(plugin) {
3
+ return {
4
+ kind: `plugin:${plugin}`,
5
+ plugin
6
+ };
7
+ }
8
+ const AI_RPC_CHANNEL = "/dsh-ai-services";
9
+ const CHAT_EVENTS_SLOT = "dsh-editor.chat.events";
10
+ const MODEL_SETTINGS_SLOT = "dsh-editor.settings.models";
11
+ /** Project keys come only from the host-validated session directory, never client input. */
12
+ function projectIdFromCwd(cwd) {
13
+ if (typeof cwd !== "string") return void 0;
14
+ const normalized = cwd.trim().replaceAll("\\", "/");
15
+ if (!normalized) return void 0;
16
+ return normalized === "/" || /^[A-Za-z]:\/$/.test(normalized) ? normalized : normalized.replace(/\/+$/, "");
17
+ }
18
+ //#endregion
19
+ export { AI_RPC_CHANNEL, CHAT_EVENTS_SLOT, MODEL_SETTINGS_SLOT, producerMessageSource, projectIdFromCwd };
20
+
21
+ //# sourceMappingURL=contracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.js","names":[],"sources":["../src/contracts.ts"],"sourcesContent":["import type { ContextFormed } from '@deepseek-ai/dsh-llm'\n\n/** Public, browser-safe contracts. Host implementations stay in their owning packages. */\nexport type ProducerSourceKind = `plugin:${string}`\nexport type ProducerMessageSource = { kind: ProducerSourceKind; plugin: string } & ContextFormed\n\n/**\n * V4 sessions persist the producing extension in `kind`; the retired generic\n * `plugin` wrapper is not admitted. The AI service is the shared producer for\n * feature scopes, so its map must cover every registered feature id.\n */\ndeclare module '@deepseek-ai/dsh-llm' {\n interface MessageSourceMap {\n [kind: ProducerSourceKind]: ProducerMessageSource\n }\n}\n\nexport function producerMessageSource(plugin: string): ProducerMessageSource {\n return { kind: `plugin:${plugin}`, plugin }\n}\nexport type ModelRole = 'normal' | 'weak' | 'strong' | 'fantasy'\nexport interface ModelRoute { provider: string; model: string; reasoningEffort?: string }\nexport type ModelTarget = { kind: 'role'; role: ModelRole } | { kind: 'session' } | ({ kind: 'model' } & ModelRoute)\nexport interface AiPolicy {\n revision: number\n roles: Partial<Record<ModelRole, ModelRoute>>\n purposes: Record<string, ModelTarget>\n limits: { concurrency: number; timeoutMs: number; maxInputChars: number; maxOutputTokens: number; maxAttempts: number }\n}\nexport interface PurposeSpec {\n id: string\n label: string\n defaultTarget: ModelTarget\n maxOutputTokens?: number\n maxInputChars?: number\n timeoutMs?: number\n}\nexport interface ResolvedRoute extends ModelRoute {\n source: 'override' | 'purpose' | 'default'\n target: ModelTarget\n policyRevision: number\n inheritedRole?: ModelRole\n}\nexport interface AuxiliaryRequest {\n purpose: string\n sessionId?: string\n input: string\n system: string\n sourceVersion: string\n contractVersion?: number\n promptVersion?: string\n schemaVersion?: string\n override?: ModelTarget\n signal?: AbortSignal\n /** Rechecked after every await and before returning a publishable result. */\n isCurrent?: () => boolean\n priority?: 'interactive' | 'background'\n /** Plain writing candidates can stop once this many visible characters are available. */\n insert?: { maxChars: number }\n}\nexport interface UsageReceipt {\n id: string\n plugin: string\n purpose: string\n sessionId?: string\n sourceVersion: string\n contractVersion?: number\n promptVersion?: string\n schemaVersion?: string\n route?: ResolvedRoute\n status: 'success' | 'failed' | 'cancelled' | 'superseded' | 'skipped'\n attempts: number\n inputTokens?: number\n outputTokens?: number\n cost: number | null\n startedAt: number\n finishedAt: number\n error?: string\n}\nexport interface AuxiliaryResult { text: string; receipt: UsageReceipt }\nexport interface AiFeatureScope {\n readonly plugin: string\n readonly signal: AbortSignal\n readonly active: boolean\n registerPurpose(spec: PurposeSpec): () => void\n run(request: AuxiliaryRequest): Promise<AuxiliaryResult>\n dispose(): void\n}\nexport interface AiServices {\n activate(plugin: string): AiFeatureScope\n /** Atomically import missing purpose defaults once; host-only compatibility entrypoint. */\n importPurposes(migrationId: string, defaults: Record<string, ModelTarget>, roles?: AiPolicy['roles']): Promise<AiPolicy>\n getPolicy(): AiPolicy\n updatePolicy(policy: Omit<AiPolicy, 'revision'>, expectedRevision: number): Promise<AiPolicy>\n resolve(purpose: string, sessionId?: string, override?: ModelTarget): Promise<ResolvedRoute>\n purposes(): Array<PurposeSpec & { plugin: string }>\n usage(): UsageReceipt[]\n}\nexport type RpcResult<T = unknown> = { ok: true; value: T } | { ok: false; error: { code: string; message: string } }\nexport const AI_RPC_CHANNEL = '/dsh-ai-services'\nexport const CHAT_EVENTS_SLOT = 'dsh-editor.chat.events'\nexport const MODEL_SETTINGS_SLOT = 'dsh-editor.settings.models'\nexport interface EvidenceRef { sessionId: string; seq: number; kind: 'user' | 'tool' | 'turn' | 'manual'; excerpt?: string }\nexport interface TaskContract {\n id: string\n sessionId: string\n sourceVersion: string\n revision: number\n goal: string\n deliverables: string[]\n inScope: string[]\n outOfScope: string[]\n constraints: string[]\n acceptance: string[]\n assumptions: string[]\n questions: string[]\n evidence: EvidenceRef[]\n readiness: 'pending' | 'clear-request' | 'user-confirmed' | 'disclosed-assumptions' | 'cancelled' | 'stale'\n updatedAt: number\n}\nexport interface TaskCheckpoint {\n id: string\n sessionId: string\n sourceVersion: string\n contractVersion?: number\n fromSeq: number\n toSeq: number\n revision: number\n status: 'running' | 'completed' | 'failed' | 'cancelled'\n items: Array<{ label: string; state: 'pending' | 'verified' | 'failed'; evidence: EvidenceRef[] }>\n constraints: string[]\n nextAction: string\n createdAt: number\n}\nexport type KnowledgeScope = { kind: 'global' } | { kind: 'project'; projectId: string }\nexport type KnowledgeKind = 'preference' | 'project-fact' | 'decision' | 'lesson'\nexport interface MemoryRecord {\n id: string\n revision: number\n scope: KnowledgeScope\n kind: KnowledgeKind\n status: 'candidate' | 'active' | 'rejected' | 'superseded' | 'revoked' | 'deleted'\n title: string\n content: string\n tags: string[]\n evidence: EvidenceRef[]\n exceptions: string[]\n source: 'user' | 'memory' | 'dream' | 'self-improvement'\n createdAt: number\n updatedAt: number\n expiresAt?: number\n supersedes?: string[]\n /** Exact record revisions behind a derived candidate; revalidated on adoption. */\n basis?: Array<{ id: string; revision: number }>\n}\nexport type NewMemoryRecord = Omit<MemoryRecord, 'id' | 'revision' | 'createdAt' | 'updatedAt'>\nexport interface MemoryQuery { scope: KnowledgeScope; query?: string; kinds?: KnowledgeKind[]; statuses?: MemoryRecord['status'][]; limit?: number }\nexport interface MemoryMutationOptions { signal?: AbortSignal; isCurrent?: () => boolean }\nexport interface MemoryService {\n list(query: MemoryQuery): Promise<MemoryRecord[]>\n create(record: NewMemoryRecord, options?: MemoryMutationOptions): Promise<MemoryRecord>\n update(id: string, patch: Partial<Pick<MemoryRecord, 'title' | 'content' | 'tags' | 'exceptions' | 'status' | 'expiresAt'>>, expectedRevision: number, options?: MemoryMutationOptions): Promise<MemoryRecord>\n /** Explicit user promotion, persisted atomically on the same record; preserves expiry and evidence. */\n promoteToGlobal(id: string, expectedRevision: number, options?: MemoryMutationOptions): Promise<MemoryRecord>\n remove(id: string, expectedRevision: number, options?: MemoryMutationOptions): Promise<void>\n recall(query: MemoryQuery): Promise<MemoryRecord[]>\n}\n\n/** Project keys come only from the host-validated session directory, never client input. */\nexport function projectIdFromCwd(cwd: string | undefined): string | undefined {\n if (typeof cwd !== 'string') return undefined\n const normalized = cwd.trim().replaceAll('\\\\', '/')\n if (!normalized) return undefined\n return normalized === '/' || /^[A-Za-z]:\\/$/.test(normalized) ? normalized : normalized.replace(/\\/+$/, '')\n}\n"],"mappings":";AAiBA,SAAgB,sBAAsB,QAAuC;CAC3E,OAAO;EAAE,MAAM,UAAU;EAAU;CAAO;AAC5C;AAgFA,MAAa,iBAAiB;AAC9B,MAAa,mBAAmB;AAChC,MAAa,sBAAsB;;AAoEnC,SAAgB,iBAAiB,KAA6C;CAC5E,IAAI,OAAO,QAAQ,UAAU,OAAO,KAAA;CACpC,MAAM,aAAa,IAAI,KAAK,CAAC,CAAC,WAAW,MAAM,GAAG;CAClD,IAAI,CAAC,YAAY,OAAO,KAAA;CACxB,OAAO,eAAe,OAAO,gBAAgB,KAAK,UAAU,IAAI,aAAa,WAAW,QAAQ,QAAQ,EAAE;AAC5G"}
@@ -0,0 +1,20 @@
1
+ import { RpcResult } from "./contracts.js";
2
+ import { IncomingMessage, ServerResponse } from "node:http";
3
+ //#region src/host-rpc.d.ts
4
+ interface HostRpcContext {
5
+ webServer: {
6
+ register(route: {
7
+ kind: 'prefix';
8
+ path: string;
9
+ handler(req: IncomingMessage, res: ServerResponse): void | Promise<void>;
10
+ }): () => void;
11
+ };
12
+ connection: {
13
+ requestRejection?: (request: IncomingMessage) => number | undefined;
14
+ };
15
+ }
16
+ /** Safe host RPC registration. Missing requestRejection fails closed. Result type accepts any RpcResult shape. */
17
+ declare function registerHostRpc<R extends RpcResult = RpcResult>(ctx: HostRpcContext, channel: string, call: (endpoint: string, payload: unknown, signal: AbortSignal) => Promise<R>): () => void;
18
+ //#endregion
19
+ export { HostRpcContext, registerHostRpc };
20
+ //# sourceMappingURL=host-rpc.d.ts.map
@@ -0,0 +1,95 @@
1
+ //#region src/host-rpc.ts
2
+ /** Safe host RPC registration. Missing requestRejection fails closed. Result type accepts any RpcResult shape. */
3
+ function registerHostRpc(ctx, channel, call) {
4
+ return ctx.webServer.register({
5
+ kind: "prefix",
6
+ path: channel,
7
+ handler: async (req, res) => {
8
+ if (!ctx.connection.requestRejection) {
9
+ res.writeHead(503);
10
+ res.end();
11
+ return;
12
+ }
13
+ const rejection = ctx.connection.requestRejection(req);
14
+ if (rejection !== void 0) {
15
+ res.writeHead(rejection);
16
+ res.end();
17
+ return;
18
+ }
19
+ const pathname = new URL(req.url ?? "/", "http://dsh.internal").pathname;
20
+ const endpoint = pathname.startsWith(`${channel}/`) ? pathname.slice(channel.length + 1) : "";
21
+ if (req.method !== "POST" || !/^[a-z.]+$/.test(endpoint)) {
22
+ res.writeHead(404);
23
+ res.end();
24
+ return;
25
+ }
26
+ if (String(req.headers["content-type"] ?? "").split(";")[0]?.trim() !== "application/json") {
27
+ res.writeHead(415);
28
+ res.end();
29
+ return;
30
+ }
31
+ const controller = new AbortController();
32
+ res.on("close", () => {
33
+ if (!res.writableEnded) controller.abort();
34
+ });
35
+ try {
36
+ const chunks = [];
37
+ let size = 0;
38
+ for await (const chunk of req) {
39
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
40
+ size += buffer.length;
41
+ if (size > 65536) {
42
+ res.writeHead(413, { connection: "close" });
43
+ res.end();
44
+ req.destroy();
45
+ return;
46
+ }
47
+ chunks.push(buffer);
48
+ }
49
+ let request;
50
+ try {
51
+ const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
52
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error();
53
+ request = parsed;
54
+ } catch {
55
+ res.writeHead(400);
56
+ res.end();
57
+ return;
58
+ }
59
+ if (request.type !== "client-request" || request.method !== endpoint || typeof request.rpcId !== "string") {
60
+ res.writeHead(400);
61
+ res.end();
62
+ return;
63
+ }
64
+ const raw = await call(endpoint, request.payload, controller.signal);
65
+ const result = raw.ok ? raw : {
66
+ ...raw,
67
+ error: {
68
+ details: {},
69
+ ...raw.error
70
+ }
71
+ };
72
+ if (!res.destroyed) {
73
+ res.writeHead(200, {
74
+ "content-type": "application/json",
75
+ "cache-control": "no-store"
76
+ });
77
+ res.end(JSON.stringify({
78
+ type: "server-response",
79
+ rpcId: request.rpcId,
80
+ result
81
+ }));
82
+ }
83
+ } catch {
84
+ if (!res.destroyed) {
85
+ res.writeHead(500);
86
+ res.end("rpc request failed");
87
+ }
88
+ }
89
+ }
90
+ });
91
+ }
92
+ //#endregion
93
+ export { registerHostRpc };
94
+
95
+ //# sourceMappingURL=host-rpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-rpc.js","names":[],"sources":["../src/host-rpc.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { RpcResult } from './contracts.ts'\n\nexport interface HostRpcContext {\n webServer: { register(route: { kind: 'prefix'; path: string; handler(req: IncomingMessage, res: ServerResponse): void | Promise<void> }): () => void }\n connection: { requestRejection?: (request: IncomingMessage) => number | undefined }\n}\n\n/** Safe host RPC registration. Missing requestRejection fails closed. Result type accepts any RpcResult shape. */\nexport function registerHostRpc<R extends RpcResult = RpcResult>(\n ctx: HostRpcContext,\n channel: string,\n call: (endpoint: string, payload: unknown, signal: AbortSignal) => Promise<R>,\n): () => void {\n return ctx.webServer.register({ kind: 'prefix', path: channel, handler: async (req, res) => {\n // Require the host's origin/auth policy. Missing policy must not expose settings.\n if (!ctx.connection.requestRejection) { res.writeHead(503); res.end(); return }\n const rejection = ctx.connection.requestRejection(req)\n if (rejection !== undefined) { res.writeHead(rejection); res.end(); return }\n const pathname = new URL(req.url ?? '/', 'http://dsh.internal').pathname\n const endpoint = pathname.startsWith(`${channel}/`) ? pathname.slice(channel.length + 1) : ''\n if (req.method !== 'POST' || !/^[a-z.]+$/.test(endpoint)) { res.writeHead(404); res.end(); return }\n if (String(req.headers['content-type'] ?? '').split(';')[0]?.trim() !== 'application/json') {\n res.writeHead(415); res.end(); return\n }\n const controller = new AbortController()\n res.on('close', () => { if (!res.writableEnded) controller.abort() })\n try {\n const chunks: Buffer[] = []\n let size = 0\n for await (const chunk of req) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n size += buffer.length\n if (size > 64 * 1024) { res.writeHead(413, { connection: 'close' }); res.end(); req.destroy(); return }\n chunks.push(buffer)\n }\n let request: Record<string, unknown>\n try {\n const parsed: unknown = JSON.parse(Buffer.concat(chunks).toString('utf8'))\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error()\n request = parsed as Record<string, unknown>\n } catch { res.writeHead(400); res.end(); return }\n if (request.type !== 'client-request' || request.method !== endpoint || typeof request.rpcId !== 'string') {\n res.writeHead(400); res.end(); return\n }\n const raw = await call(endpoint, request.payload, controller.signal)\n const result = raw.ok ? raw : { ...raw, error: { details: {}, ...raw.error } }\n if (!res.destroyed) {\n res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' })\n res.end(JSON.stringify({ type: 'server-response', rpcId: request.rpcId, result }))\n }\n } catch {\n if (!res.destroyed) { res.writeHead(500); res.end('rpc request failed') }\n }\n } })\n}\n"],"mappings":";;AASA,SAAgB,gBACd,KACA,SACA,MACY;CACZ,OAAO,IAAI,UAAU,SAAS;EAAE,MAAM;EAAU,MAAM;EAAS,SAAS,OAAO,KAAK,QAAQ;GAE1F,IAAI,CAAC,IAAI,WAAW,kBAAkB;IAAE,IAAI,UAAU,GAAG;IAAG,IAAI,IAAI;IAAG;GAAO;GAC9E,MAAM,YAAY,IAAI,WAAW,iBAAiB,GAAG;GACrD,IAAI,cAAc,KAAA,GAAW;IAAE,IAAI,UAAU,SAAS;IAAG,IAAI,IAAI;IAAG;GAAO;GAC3E,MAAM,WAAW,IAAI,IAAI,IAAI,OAAO,KAAK,qBAAqB,CAAC,CAAC;GAChE,MAAM,WAAW,SAAS,WAAW,GAAG,QAAQ,EAAE,IAAI,SAAS,MAAM,QAAQ,SAAS,CAAC,IAAI;GAC3F,IAAI,IAAI,WAAW,UAAU,CAAC,YAAY,KAAK,QAAQ,GAAG;IAAE,IAAI,UAAU,GAAG;IAAG,IAAI,IAAI;IAAG;GAAO;GAClG,IAAI,OAAO,IAAI,QAAQ,mBAAmB,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,KAAK,MAAM,oBAAoB;IAC1F,IAAI,UAAU,GAAG;IAAG,IAAI,IAAI;IAAG;GACjC;GACA,MAAM,aAAa,IAAI,gBAAgB;GACvC,IAAI,GAAG,eAAe;IAAE,IAAI,CAAC,IAAI,eAAe,WAAW,MAAM;GAAE,CAAC;GACpE,IAAI;IACF,MAAM,SAAmB,CAAC;IAC1B,IAAI,OAAO;IACX,WAAW,MAAM,SAAS,KAAK;KAC7B,MAAM,SAAS,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAK;KACjE,QAAQ,OAAO;KACf,IAAI,OAAO,OAAW;MAAE,IAAI,UAAU,KAAK,EAAE,YAAY,QAAQ,CAAC;MAAG,IAAI,IAAI;MAAG,IAAI,QAAQ;MAAG;KAAO;KACtG,OAAO,KAAK,MAAM;IACpB;IACA,IAAI;IACJ,IAAI;KACF,MAAM,SAAkB,KAAK,MAAM,OAAO,OAAO,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC;KACzE,IAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG,MAAM,IAAI,MAAM;KACpF,UAAU;IACZ,QAAQ;KAAE,IAAI,UAAU,GAAG;KAAG,IAAI,IAAI;KAAG;IAAO;IAChD,IAAI,QAAQ,SAAS,oBAAoB,QAAQ,WAAW,YAAY,OAAO,QAAQ,UAAU,UAAU;KACzG,IAAI,UAAU,GAAG;KAAG,IAAI,IAAI;KAAG;IACjC;IACA,MAAM,MAAM,MAAM,KAAK,UAAU,QAAQ,SAAS,WAAW,MAAM;IACnE,MAAM,SAAS,IAAI,KAAK,MAAM;KAAE,GAAG;KAAK,OAAO;MAAE,SAAS,CAAC;MAAG,GAAG,IAAI;KAAM;IAAE;IAC7E,IAAI,CAAC,IAAI,WAAW;KAClB,IAAI,UAAU,KAAK;MAAE,gBAAgB;MAAoB,iBAAiB;KAAW,CAAC;KACtF,IAAI,IAAI,KAAK,UAAU;MAAE,MAAM;MAAmB,OAAO,QAAQ;MAAO;KAAO,CAAC,CAAC;IACnF;GACF,QAAQ;IACN,IAAI,CAAC,IAAI,WAAW;KAAE,IAAI,UAAU,GAAG;KAAG,IAAI,IAAI,oBAAoB;IAAE;GAC1E;EACF;CAAE,CAAC;AACL"}