@artenx/dshb 0.1.15 → 0.1.16

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.md CHANGED
@@ -10,9 +10,11 @@ dsh plugin --profile web add @artenx/dshb
10
10
 
11
11
  Restart the corresponding DSH Web profile after installation.
12
12
 
13
+ Desktop app: install from **Settings → Plugins → More** in the DSH desktop app; the plugin takes effect on next startup.
14
+
13
15
  ## Requirements
14
16
 
15
17
  - Node.js 22.13 or newer
16
- - DeepSeek Harness 0.1.7-rc.1
18
+ - DeepSeek Harness 0.1.7-rc.1 or 0.2.1-rc.2 (Web or Desktop)
17
19
 
18
20
  Source code and complete documentation: https://github.com/Artenx/dsh-honeybee
@@ -8,7 +8,7 @@ declare class AuthGate {
8
8
  decide(req: IncomingMessage): GateDecision;
9
9
  private isPageRequest;
10
10
  }
11
- declare function rewriteToLoopback(req: IncomingMessage, port: number): void;
11
+ declare function rewriteToLoopback(req: IncomingMessage, port?: number): void;
12
12
  //#endregion
13
13
  export { AuthGate, GateDecision, rewriteToLoopback };
14
14
  //# sourceMappingURL=gate.d.ts.map
package/lib/auth/gate.js CHANGED
@@ -27,7 +27,9 @@ var AuthGate = class {
27
27
  }
28
28
  };
29
29
  function rewriteToLoopback(req, port) {
30
- const authority = `127.0.0.1:${port}`;
30
+ const localPort = port ?? req.socket.localPort;
31
+ if (typeof localPort !== "number") return;
32
+ const authority = `127.0.0.1:${localPort}`;
31
33
  req.headers.host = authority;
32
34
  for (const name of ["origin", "referer"]) {
33
35
  const value = req.headers[name];
@@ -1 +1 @@
1
- {"version":3,"file":"gate.js","names":[],"sources":["../../src/auth/gate.ts"],"sourcesContent":["import type { IncomingMessage } from 'node:http'\nimport { isLoopbackRequest } from './loopback.js'\nimport { readCookie, SESSION_COOKIE, verifySession } from './session.js'\nimport type { CredentialStore } from './credentials.js'\n\nexport type GateDecision = 'allow' | 'exempt' | 'redirect-login' | 'reject'\n\nexport class AuthGate {\n constructor(private readonly store: CredentialStore) {}\n\n decide(req: IncomingMessage): GateDecision {\n const path = new URL(req.url ?? '/', 'http://x').pathname\n if (path === '/login' || path.startsWith('/api/auth/')) return 'exempt'\n if (isLoopbackRequest(req.socket.remoteAddress, req.headers.host)) return 'allow'\n const secret = this.store.sessionSecret\n const username = this.store.username\n if (secret && username) {\n const value = readCookie(req.headers.cookie, SESSION_COOKIE)\n if (verifySession(value, secret, username)) return 'allow'\n }\n if (this.isPageRequest(req)) return 'redirect-login'\n return 'reject'\n }\n\n private isPageRequest(req: IncomingMessage): boolean {\n if (req.method !== 'GET' && req.method !== 'HEAD') return false\n const path = new URL(req.url ?? '/', 'http://x').pathname\n if (path.startsWith('/api/')) return false\n const accept = req.headers.accept ?? ''\n return accept.includes('text/html') || accept.includes('*/*')\n }\n}\n\nexport function rewriteToLoopback(req: IncomingMessage, port: number): void {\n const authority = `127.0.0.1:${port}`\n req.headers.host = authority\n for (const name of ['origin', 'referer'] as const) {\n const value = req.headers[name]\n if (typeof value !== 'string') continue\n try {\n const url = new URL(value)\n url.host = authority\n req.headers[name] = url.toString()\n } catch {}\n }\n}\n"],"mappings":";;;AAOA,IAAa,WAAb,MAAsB;CACS;CAA7B,YAAY,OAAyC;EAAxB,KAAA,QAAA;CAAyB;CAEtD,OAAO,KAAoC;EACzC,MAAM,OAAO,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,CAAC,CAAC;EACjD,IAAI,SAAS,YAAY,KAAK,WAAW,YAAY,GAAG,OAAO;EAC/D,IAAI,kBAAkB,IAAI,OAAO,eAAe,IAAI,QAAQ,IAAI,GAAG,OAAO;EAC1E,MAAM,SAAS,KAAK,MAAM;EAC1B,MAAM,WAAW,KAAK,MAAM;EAC5B,IAAI,UAAU,UAAU;GACtB,MAAM,QAAQ,WAAW,IAAI,QAAQ,QAAQ,cAAc;GAC3D,IAAI,cAAc,OAAO,QAAQ,QAAQ,GAAG,OAAO;EACrD;EACA,IAAI,KAAK,cAAc,GAAG,GAAG,OAAO;EACpC,OAAO;CACT;CAEA,cAAsB,KAA+B;EACnD,IAAI,IAAI,WAAW,SAAS,IAAI,WAAW,QAAQ,OAAO;EAE1D,IADa,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,CAAC,CAAC,SACxC,WAAW,OAAO,GAAG,OAAO;EACrC,MAAM,SAAS,IAAI,QAAQ,UAAU;EACrC,OAAO,OAAO,SAAS,WAAW,KAAK,OAAO,SAAS,KAAK;CAC9D;AACF;AAEA,SAAgB,kBAAkB,KAAsB,MAAoB;CAC1E,MAAM,YAAY,aAAa;CAC/B,IAAI,QAAQ,OAAO;CACnB,KAAK,MAAM,QAAQ,CAAC,UAAU,SAAS,GAAY;EACjD,MAAM,QAAQ,IAAI,QAAQ;EAC1B,IAAI,OAAO,UAAU,UAAU;EAC/B,IAAI;GACF,MAAM,MAAM,IAAI,IAAI,KAAK;GACzB,IAAI,OAAO;GACX,IAAI,QAAQ,QAAQ,IAAI,SAAS;EACnC,QAAQ,CAAC;CACX;AACF"}
1
+ {"version":3,"file":"gate.js","names":[],"sources":["../../src/auth/gate.ts"],"sourcesContent":["import type { IncomingMessage } from 'node:http'\nimport { isLoopbackRequest } from './loopback.js'\nimport { readCookie, SESSION_COOKIE, verifySession } from './session.js'\nimport type { CredentialStore } from './credentials.js'\n\nexport type GateDecision = 'allow' | 'exempt' | 'redirect-login' | 'reject'\n\nexport class AuthGate {\n constructor(private readonly store: CredentialStore) {}\n\n decide(req: IncomingMessage): GateDecision {\n const path = new URL(req.url ?? '/', 'http://x').pathname\n if (path === '/login' || path.startsWith('/api/auth/')) return 'exempt'\n if (isLoopbackRequest(req.socket.remoteAddress, req.headers.host)) return 'allow'\n const secret = this.store.sessionSecret\n const username = this.store.username\n if (secret && username) {\n const value = readCookie(req.headers.cookie, SESSION_COOKIE)\n if (verifySession(value, secret, username)) return 'allow'\n }\n if (this.isPageRequest(req)) return 'redirect-login'\n return 'reject'\n }\n\n private isPageRequest(req: IncomingMessage): boolean {\n if (req.method !== 'GET' && req.method !== 'HEAD') return false\n const path = new URL(req.url ?? '/', 'http://x').pathname\n if (path.startsWith('/api/')) return false\n const accept = req.headers.accept ?? ''\n return accept.includes('text/html') || accept.includes('*/*')\n }\n}\n\nexport function rewriteToLoopback(req: IncomingMessage, port?: number): void {\n const localPort = port ?? req.socket.localPort\n if (typeof localPort !== 'number') return\n const authority = `127.0.0.1:${localPort}`\n req.headers.host = authority\n for (const name of ['origin', 'referer'] as const) {\n const value = req.headers[name]\n if (typeof value !== 'string') continue\n try {\n const url = new URL(value)\n url.host = authority\n req.headers[name] = url.toString()\n } catch {}\n }\n}\n"],"mappings":";;;AAOA,IAAa,WAAb,MAAsB;CACS;CAA7B,YAAY,OAAyC;EAAxB,KAAA,QAAA;CAAyB;CAEtD,OAAO,KAAoC;EACzC,MAAM,OAAO,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,CAAC,CAAC;EACjD,IAAI,SAAS,YAAY,KAAK,WAAW,YAAY,GAAG,OAAO;EAC/D,IAAI,kBAAkB,IAAI,OAAO,eAAe,IAAI,QAAQ,IAAI,GAAG,OAAO;EAC1E,MAAM,SAAS,KAAK,MAAM;EAC1B,MAAM,WAAW,KAAK,MAAM;EAC5B,IAAI,UAAU,UAAU;GACtB,MAAM,QAAQ,WAAW,IAAI,QAAQ,QAAQ,cAAc;GAC3D,IAAI,cAAc,OAAO,QAAQ,QAAQ,GAAG,OAAO;EACrD;EACA,IAAI,KAAK,cAAc,GAAG,GAAG,OAAO;EACpC,OAAO;CACT;CAEA,cAAsB,KAA+B;EACnD,IAAI,IAAI,WAAW,SAAS,IAAI,WAAW,QAAQ,OAAO;EAE1D,IADa,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,CAAC,CAAC,SACxC,WAAW,OAAO,GAAG,OAAO;EACrC,MAAM,SAAS,IAAI,QAAQ,UAAU;EACrC,OAAO,OAAO,SAAS,WAAW,KAAK,OAAO,SAAS,KAAK;CAC9D;AACF;AAEA,SAAgB,kBAAkB,KAAsB,MAAqB;CAC3E,MAAM,YAAY,QAAQ,IAAI,OAAO;CACrC,IAAI,OAAO,cAAc,UAAU;CACnC,MAAM,YAAY,aAAa;CAC/B,IAAI,QAAQ,OAAO;CACnB,KAAK,MAAM,QAAQ,CAAC,UAAU,SAAS,GAAY;EACjD,MAAM,QAAQ,IAAI,QAAQ;EAC1B,IAAI,OAAO,UAAU,UAAU;EAC/B,IAAI;GACF,MAAM,MAAM,IAAI,IAAI,KAAK;GACzB,IAAI,OAAO;GACX,IAAI,QAAQ,QAAQ,IAAI,SAAS;EACnC,QAAQ,CAAC;CACX;AACF"}
@@ -4,7 +4,7 @@ import { Context } from "@deepseek-ai/cordis";
4
4
  declare class AuthedWebServer extends WebServer {
5
5
  private readonly gate;
6
6
  constructor(ctx: Context, config: {
7
- host: '127.0.0.1' | '0.0.0.0';
7
+ host: string;
8
8
  port: number;
9
9
  });
10
10
  register(route: WebRoute): () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"webserver.js","names":[],"sources":["../../src/auth/webserver.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { Duplex } from 'node:stream'\nimport WebServer from '@deepseek-ai/dsh-host-webserver'\nimport type { WebRoute, WebUpgradeRoute } from '@deepseek-ai/dsh-host-webserver'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { sharedCredentialStore } from './credentials.js'\nimport { AuthGate, rewriteToLoopback } from './gate.js'\n\nexport default class AuthedWebServer extends WebServer {\n private readonly gate = new AuthGate(sharedCredentialStore())\n\n constructor(ctx: Context, config: { host: '127.0.0.1' | '0.0.0.0'; port: number }) {\n super(ctx, config)\n }\n\n override register(route: WebRoute): () => void {\n return super.register({ ...route, handler: (req, res) => this.guarded(route, req, res) })\n }\n\n override registerFallback(handler: WebRoute['handler']): () => void {\n return super.registerFallback((req, res) =>\n this.guarded({ kind: 'exact', path: '/', handler }, req, res),\n )\n }\n\n override registerUpgrade(route: WebUpgradeRoute): () => void {\n return super.registerUpgrade({\n ...route,\n handler: (req, socket, head) => this.guardedUpgrade(route, req, socket, head),\n })\n }\n\n private guarded(route: WebRoute, req: IncomingMessage, res: ServerResponse): void | Promise<void> {\n const decision = this.gate.decide(req)\n if (decision === 'allow') {\n rewriteToLoopback(req, this.port)\n return route.handler(req, res)\n }\n if (decision === 'exempt') {\n return route.handler(req, res)\n }\n if (decision === 'redirect-login') {\n res.writeHead(302, { location: '/login' })\n res.end()\n return\n }\n res.writeHead(401, { 'content-type': 'application/json' })\n res.end(JSON.stringify({ ok: false, error: 'unauthorized' }))\n }\n\n private guardedUpgrade(\n route: WebUpgradeRoute,\n req: IncomingMessage,\n socket: Duplex,\n head: Buffer,\n ): void | Promise<void> {\n const decision = this.gate.decide(req)\n if (decision === 'allow' || decision === 'exempt') {\n if (decision === 'allow') rewriteToLoopback(req, this.port)\n return route.handler(req, socket, head)\n }\n socket.write('HTTP/1.1 401 Unauthorized\\r\\nConnection: close\\r\\n\\r\\n')\n socket.destroy()\n }\n}\n"],"mappings":";;;;AAQA,IAAqB,kBAArB,cAA6C,UAAU;CACrD,OAAwB,IAAI,SAAS,sBAAsB,CAAC;CAE5D,YAAY,KAAc,QAAyD;EACjF,MAAM,KAAK,MAAM;CACnB;CAEA,SAAkB,OAA6B;EAC7C,OAAO,MAAM,SAAS;GAAE,GAAG;GAAO,UAAU,KAAK,QAAQ,KAAK,QAAQ,OAAO,KAAK,GAAG;EAAE,CAAC;CAC1F;CAEA,iBAA0B,SAA0C;EAClE,OAAO,MAAM,kBAAkB,KAAK,QAClC,KAAK,QAAQ;GAAE,MAAM;GAAS,MAAM;GAAK;EAAQ,GAAG,KAAK,GAAG,CAC9D;CACF;CAEA,gBAAyB,OAAoC;EAC3D,OAAO,MAAM,gBAAgB;GAC3B,GAAG;GACH,UAAU,KAAK,QAAQ,SAAS,KAAK,eAAe,OAAO,KAAK,QAAQ,IAAI;EAC9E,CAAC;CACH;CAEA,QAAgB,OAAiB,KAAsB,KAA2C;EAChG,MAAM,WAAW,KAAK,KAAK,OAAO,GAAG;EACrC,IAAI,aAAa,SAAS;GACxB,kBAAkB,KAAK,KAAK,IAAI;GAChC,OAAO,MAAM,QAAQ,KAAK,GAAG;EAC/B;EACA,IAAI,aAAa,UACf,OAAO,MAAM,QAAQ,KAAK,GAAG;EAE/B,IAAI,aAAa,kBAAkB;GACjC,IAAI,UAAU,KAAK,EAAE,UAAU,SAAS,CAAC;GACzC,IAAI,IAAI;GACR;EACF;EACA,IAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;EACzD,IAAI,IAAI,KAAK,UAAU;GAAE,IAAI;GAAO,OAAO;EAAe,CAAC,CAAC;CAC9D;CAEA,eACE,OACA,KACA,QACA,MACsB;EACtB,MAAM,WAAW,KAAK,KAAK,OAAO,GAAG;EACrC,IAAI,aAAa,WAAW,aAAa,UAAU;GACjD,IAAI,aAAa,SAAS,kBAAkB,KAAK,KAAK,IAAI;GAC1D,OAAO,MAAM,QAAQ,KAAK,QAAQ,IAAI;EACxC;EACA,OAAO,MAAM,wDAAwD;EACrE,OAAO,QAAQ;CACjB;AACF"}
1
+ {"version":3,"file":"webserver.js","names":[],"sources":["../../src/auth/webserver.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { Duplex } from 'node:stream'\nimport WebServer from '@deepseek-ai/dsh-host-webserver'\nimport type { WebRoute, WebUpgradeRoute } from '@deepseek-ai/dsh-host-webserver'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { sharedCredentialStore } from './credentials.js'\nimport { AuthGate, rewriteToLoopback } from './gate.js'\n\nexport default class AuthedWebServer extends WebServer {\n private readonly gate = new AuthGate(sharedCredentialStore())\n\n constructor(ctx: Context, config: { host: string; port: number }) {\n super(ctx, config)\n }\n\n override register(route: WebRoute): () => void {\n return super.register({ ...route, handler: (req, res) => this.guarded(route, req, res) })\n }\n\n override registerFallback(handler: WebRoute['handler']): () => void {\n return super.registerFallback((req, res) =>\n this.guarded({ kind: 'exact', path: '/', handler }, req, res),\n )\n }\n\n override registerUpgrade(route: WebUpgradeRoute): () => void {\n return super.registerUpgrade({\n ...route,\n handler: (req, socket, head) => this.guardedUpgrade(route, req, socket, head),\n })\n }\n\n private guarded(route: WebRoute, req: IncomingMessage, res: ServerResponse): void | Promise<void> {\n const decision = this.gate.decide(req)\n if (decision === 'allow') {\n rewriteToLoopback(req, this.port)\n return route.handler(req, res)\n }\n if (decision === 'exempt') {\n return route.handler(req, res)\n }\n if (decision === 'redirect-login') {\n res.writeHead(302, { location: '/login' })\n res.end()\n return\n }\n res.writeHead(401, { 'content-type': 'application/json' })\n res.end(JSON.stringify({ ok: false, error: 'unauthorized' }))\n }\n\n private guardedUpgrade(\n route: WebUpgradeRoute,\n req: IncomingMessage,\n socket: Duplex,\n head: Buffer,\n ): void | Promise<void> {\n const decision = this.gate.decide(req)\n if (decision === 'allow' || decision === 'exempt') {\n if (decision === 'allow') rewriteToLoopback(req, this.port)\n return route.handler(req, socket, head)\n }\n socket.write('HTTP/1.1 401 Unauthorized\\r\\nConnection: close\\r\\n\\r\\n')\n socket.destroy()\n }\n}\n"],"mappings":";;;;AAQA,IAAqB,kBAArB,cAA6C,UAAU;CACrD,OAAwB,IAAI,SAAS,sBAAsB,CAAC;CAE5D,YAAY,KAAc,QAAwC;EAChE,MAAM,KAAK,MAAM;CACnB;CAEA,SAAkB,OAA6B;EAC7C,OAAO,MAAM,SAAS;GAAE,GAAG;GAAO,UAAU,KAAK,QAAQ,KAAK,QAAQ,OAAO,KAAK,GAAG;EAAE,CAAC;CAC1F;CAEA,iBAA0B,SAA0C;EAClE,OAAO,MAAM,kBAAkB,KAAK,QAClC,KAAK,QAAQ;GAAE,MAAM;GAAS,MAAM;GAAK;EAAQ,GAAG,KAAK,GAAG,CAC9D;CACF;CAEA,gBAAyB,OAAoC;EAC3D,OAAO,MAAM,gBAAgB;GAC3B,GAAG;GACH,UAAU,KAAK,QAAQ,SAAS,KAAK,eAAe,OAAO,KAAK,QAAQ,IAAI;EAC9E,CAAC;CACH;CAEA,QAAgB,OAAiB,KAAsB,KAA2C;EAChG,MAAM,WAAW,KAAK,KAAK,OAAO,GAAG;EACrC,IAAI,aAAa,SAAS;GACxB,kBAAkB,KAAK,KAAK,IAAI;GAChC,OAAO,MAAM,QAAQ,KAAK,GAAG;EAC/B;EACA,IAAI,aAAa,UACf,OAAO,MAAM,QAAQ,KAAK,GAAG;EAE/B,IAAI,aAAa,kBAAkB;GACjC,IAAI,UAAU,KAAK,EAAE,UAAU,SAAS,CAAC;GACzC,IAAI,IAAI;GACR;EACF;EACA,IAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;EACzD,IAAI,IAAI,KAAK,UAAU;GAAE,IAAI;GAAO,OAAO;EAAe,CAAC,CAAC;CAC9D;CAEA,eACE,OACA,KACA,QACA,MACsB;EACtB,MAAM,WAAW,KAAK,KAAK,OAAO,GAAG;EACrC,IAAI,aAAa,WAAW,aAAa,UAAU;GACjD,IAAI,aAAa,SAAS,kBAAkB,KAAK,KAAK,IAAI;GAC1D,OAAO,MAAM,QAAQ,KAAK,QAAQ,IAAI;EACxC;EACA,OAAO,MAAM,wDAAwD;EACrE,OAAO,QAAQ;CACjB;AACF"}
package/lib/core/index.js CHANGED
@@ -8,7 +8,7 @@ import { registerRemoteDownloadRoutes, registerWorkspaceRoutes } from "./workspa
8
8
  import { sharedWorkspaceInstructionStore } from "./workspace-instructions.js";
9
9
  import { registerWorkspaceInstructionRoutes } from "./workspace-instruction-routes.js";
10
10
  import { WorkspaceBindingsStore } from "./workspace-bindings.js";
11
- import { a as registerWorkspaceInstructionInjector } from "../workspace-instruction-context-DzHFTN87.js";
11
+ import { a as registerWorkspaceInstructionInjector } from "../workspace-instruction-context-DytAmaPY.js";
12
12
  //#region src/core/index.ts
13
13
  const name = "dshb-core";
14
14
  const inject = ["webServer", "credentials"];
@@ -1,6 +1,6 @@
1
1
  import { SessionInstructionSnapshot, SessionInstructionSnapshotStore, WorkspaceInstructionStore } from "./workspace-instructions.js";
2
2
  import { Context, Service } from "@deepseek-ai/cordis";
3
- import { FileAttachmentRef, ImageAttachmentRef } from "@deepseek-ai/dsh-attachment";
3
+ import { FileAttachmentRef, ImageAttachmentRef, ImageRequestTarget } from "@deepseek-ai/dsh-attachment";
4
4
  //#region ../../node_modules/@deepseek-ai/dsh-brand/lib/types/index.d.ts
5
5
  /**
6
6
  * Duplicate-install-safe nominal primitive helpers.
@@ -35,6 +35,7 @@ interface TypertOwnedValue<Value> extends Disposable {
35
35
  declare const LOOKUP_HOST: unique symbol;
36
36
  declare const LOOKUP_WIRE: unique symbol;
37
37
  declare const CONTEXT_WIRE: unique symbol;
38
+ declare const RESOURCE_ID: unique symbol;
38
39
  /** Type-level association between a Host object and its wire identity. */
39
40
  interface TypertLookup<Host, Wire> {
40
41
  readonly [LOOKUP_HOST]: Host;
@@ -54,6 +55,21 @@ type TypertContextWire<ContextType> = ContextType extends TypertContext<infer Wi
54
55
  interface TypertLookupMap {}
55
56
  /** Merge-extensible scoped Context declarations. */
56
57
  interface TypertContextMap {}
58
+ /** Type-level association between a resource kind and its identity type. */
59
+ interface TypertResource<Id> {
60
+ readonly [RESOURCE_ID]: Id;
61
+ }
62
+ /** Extract the identity type associated with one resource declaration. */
63
+ type TypertResourceId<Resource> = Resource extends TypertResource<infer Id> ? Id : never;
64
+ /**
65
+ * Merge-extensible resource kinds whose identities a Remote call or forwarded
66
+ * event may carry. The package that owns an identity type declares its kind;
67
+ * the value is that kind's identity type, a string type such as a branded id.
68
+ * `global` names the built-in identity-less resource and is never a kind here.
69
+ */
70
+ interface TypertResourceMap {}
71
+ /** One declared resource kind. */
72
+ type TypertResourceKind = Extract<keyof TypertResourceMap, string>;
57
73
  /** Awaitable disposer returned by Cordis-owned Typert registrations. */
58
74
  type TypertDisposer = () => Promise<void>;
59
75
  type StringKeyOf<Value> = Extract<keyof Value, string>;
@@ -67,31 +83,31 @@ interface TypertSchema<Output = unknown> {
67
83
  parse(value: unknown): Output;
68
84
  }
69
85
  /** Codec attached to one invocation parameter or result. */
70
- type TypertCodec = {
86
+ type TypertCodec<Value = unknown> = (TypertRemoteValue<Value> & {
71
87
  readonly mode: 'strict';
72
88
  readonly typeSymbol: string;
73
- /** Materialize and return the process-realm schema on first boundary use. */
74
- readonly create: () => TypertSchema;
75
89
  /**
76
90
  * Decode a unary result whose fields require type-specific handling.
77
91
  * @param value - result reconstructed by the RPC carrier.
78
92
  * @returns the validated result, retaining native byte views.
79
93
  */
80
- readonly decode?: (value: unknown) => unknown;
94
+ readonly decode?: ((value: unknown) => unknown) | undefined;
81
95
  /**
82
96
  * Project typed binary fields into RPC result attachments.
83
97
  * @param value - native unary result.
84
98
  * @param writeBytes - records a byte view at its result-relative path and returns its JSON placeholder.
85
99
  * @returns JSON metadata with untouched JSON subtrees retained.
86
100
  */
87
- readonly encode?: (value: unknown, writeBytes: (bytes: Uint8Array, path: readonly (string | number)[]) => null) => unknown;
88
- } | {
101
+ readonly encode?: ((value: unknown, writeBytes: (bytes: Uint8Array, path: readonly (string | number)[]) => null) => unknown) | undefined;
102
+ }) | {
89
103
  readonly mode: 'src-json';
90
104
  };
91
105
  /** One ordered business parameter in a Remote invocation. */
92
106
  interface InvocationParameterDescriptor {
93
107
  /** Source-level parameter name. */
94
108
  readonly name: string;
109
+ /** Source parameter documentation, independent of JSON Schema support. */
110
+ readonly description?: string;
95
111
  /** Required key in the wire `args` object. */
96
112
  readonly wire: string;
97
113
  /** Whether the value is JSON or requires a registered Host lookup. */
@@ -158,6 +174,8 @@ interface InvocationDescriptor {
158
174
  };
159
175
  /** Codec for the unary result or each yielded stream item. */
160
176
  readonly result: TypertCodec;
177
+ /** Source-owned method documentation; ordinary methods need not have JSDoc. */
178
+ readonly description?: string;
161
179
  /** Source declaration used only for diagnostics. */
162
180
  readonly sourceLocation?: InvocationSourceLocation;
163
181
  }
@@ -168,19 +186,46 @@ interface InvocationDescriptor {
168
186
  */
169
187
  type PeerId = Branded<'PeerId'>;
170
188
  /**
171
- * One Peer's session on this Host. Connection owns it: `ctx` is the Cordis
189
+ * Merge-extensible caller identities. Each driver declares its own entry.
190
+ * Every entry is a JSON value with a unique literal `kind`, carries no
191
+ * credential, and is safe to persist in a Session log.
192
+ */
193
+ interface PrincipalMap {
194
+ /** The local user who owns this Host. */
195
+ owner: {
196
+ readonly kind: 'owner';
197
+ };
198
+ }
199
+ /** One caller identity; the authorization layer reads only this. */
200
+ type Principal = PrincipalMap[keyof PrincipalMap];
201
+ /**
202
+ * One Peer's session on this Host. Its creator owns it: `ctx` is the Cordis
172
203
  * scope that owns connection-lifetime registrations and is disposed with the
173
- * Peer. Who the Peer is and what it may do are not recorded here.
204
+ * Peer, and `principal` is the caller identity fixed when the Peer was created.
174
205
  */
175
206
  interface PeerScope {
176
207
  readonly id: PeerId;
208
+ /** Connection-lifetime registrations; disposed with the Peer. */
177
209
  readonly ctx: Context;
210
+ /** Caller identity fixed at creation by in-process Host code; no wire field can set it. */
211
+ readonly principal: Principal;
212
+ /** Aborted once the Peer is disposed; Remote streams that speak for the Peer end with it. */
213
+ readonly signal: AbortSignal;
214
+ /** Whether the Peer may open a forwarded-event stream; this does not grant business permissions. */
215
+ readonly interactions: 'answer' | 'none';
178
216
  /**
179
217
  * Tear down every registration made through `ctx`.
180
218
  * @returns settles once the scope has quiesced; racing calls share one completion.
181
219
  */
182
220
  dispose(): Promise<void>;
183
221
  }
222
+ /** Carrier capacity for buffered uplink frames of one logical stream. */
223
+ interface RemoteUplinkWindow {
224
+ /** Maximum complete encoded frame bytes waiting for the Host consumer. */
225
+ readonly maxBytes: number;
226
+ /** UTF-8 envelope bytes added to a compact JSON item with a present value field. */
227
+ readonly frameOverheadBytes: number;
228
+ }
184
229
  /**
185
230
  * The context of one Remote call, reachable inside the receiving method as
186
231
  * `this.ctx.invocation`. The Gateway derives the receiver from a Context that
@@ -194,10 +239,12 @@ interface RemoteInvocation {
194
239
  };
195
240
  /** Cordis service key of the receiving Service. */
196
241
  readonly service: string;
197
- /** Peer the call speaks for; an in-process carrier speaks for the operator. */
242
+ /** Peer the call speaks for; every carrier names it explicitly. */
198
243
  readonly peer: PeerScope;
199
244
  /** Carrier cancellation: Client cancel, socket close, or an uplink failure. */
200
245
  readonly signal: AbortSignal;
246
+ /** Carrier frame capacity; absent when the carrier imposes no encoded-frame inbox limit. */
247
+ readonly uplinkWindow?: RemoteUplinkWindow | undefined;
201
248
  /**
202
249
  * The Client's uplink items for this call. Available once; a second call
203
250
  * throws. With an uplink codec on the descriptor every item is decoded to
@@ -218,18 +265,33 @@ interface TypertRemoteContribution {
218
265
  /** Consumer-side invocation descriptors generated from that package. */
219
266
  readonly descriptors: readonly InvocationDescriptor[];
220
267
  }
268
+ /** Generated parser and optional lossless tool-schema projection of one Remote value. */
269
+ interface TypertRemoteValue<Value = unknown> {
270
+ /** Materialize the generated parser without loading the owning Service. */
271
+ readonly create: () => TypertSchema<Value>;
272
+ /** Source-owned value documentation, independent of tool projection support. */
273
+ readonly description?: string | undefined;
274
+ /** Present when the value is representable in the tool-schema subset without losing constraints; may be derived lazily. */
275
+ readonly jsonSchema?: Readonly<Record<string, unknown>> | undefined;
276
+ /** Projection failure for consumers that require JSON Schema; parsing remains available. */
277
+ readonly jsonSchemaError?: string | undefined;
278
+ }
221
279
  /**
222
280
  * Resolve one validated wire identity, synchronously or asynchronously.
223
281
  * @param id - validated wire identity.
224
282
  * @returns the Host object, or `undefined` when unavailable.
225
283
  */
226
284
  type TypertLookupResolver<Host = unknown, Wire = unknown> = (id: Wire) => Host | undefined | Promise<Host | undefined>;
285
+ /** Resource kinds whose identity type is a wire identity; every kind for a type-erased provider. */
286
+ type TypertResourceKindFor<Wire> = unknown extends Wire ? TypertResourceKind : { [Kind in TypertResourceKind]: [Wire] extends [TypertResourceId<TypertResourceMap[Kind]>] ? Kind : never; }[TypertResourceKind];
227
287
  /** Runtime provider for one declared Host object lookup. */
228
288
  interface TypertLookupProvider<Host = unknown, Wire = unknown> {
229
289
  /** Source parameter name recognized by the SRC weak parser. */
230
290
  readonly parameter: string;
231
291
  /** Wire field replacing the Host object parameter. */
232
292
  readonly wire: string;
293
+ /** Resource kind the wire identity names; a parameter of this lookup carries that resource. */
294
+ readonly resource?: TypertResourceKindFor<Wire>;
233
295
  /** Canonical Host type symbol used by strict generation. */
234
296
  readonly hostTypeSymbol: string;
235
297
  /** Canonical wire type symbol used by strict generation. */
@@ -260,6 +322,8 @@ interface TypertHostContextAdapter<Wire = unknown> {
260
322
  readonly wire: string;
261
323
  /** Canonical wire type symbol used by strict generation. */
262
324
  readonly wireTypeSymbol: string;
325
+ /** Resource kind the wire identity names; a call scoped to this Context carries that resource. */
326
+ readonly resource?: TypertResourceKindFor<Wire>;
263
327
  /**
264
328
  * Resolve a validated wire identity to a live Host Context.
265
329
  * @param id - validated wire identity.
@@ -286,7 +350,7 @@ interface TypertClientContextAdapter<Wire = unknown> {
286
350
  }
287
351
  /** Notification emitted after a Typert runtime registry changes. */
288
352
  interface TypertRegistryChange {
289
- readonly kind: 'local' | 'remote' | 'lookup' | 'host-context' | 'client-context';
353
+ readonly kind: 'local' | 'remote' | 'lookup' | 'host-context' | 'client-context' | 'resource';
290
354
  readonly key: string;
291
355
  }
292
356
  /** Listener for one Typert runtime registry. */
@@ -396,7 +460,7 @@ interface TypertContextRegistry {
396
460
  */
397
461
  registerClient<K extends StringKeyOf<TypertContextMap>>(key: K, adapter: TypertClientContextAdapter<TypertContextWire<TypertContextMap[K]>>): TypertDisposer;
398
462
  /**
399
- * Look up a Host Context adapter.
463
+ * Look up a Host Context adapter; a configured resolver keeps the adapter's declaration and `resource`.
400
464
  * @param key - descriptor Context key.
401
465
  * @returns the adapter, or `undefined` when absent.
402
466
  */
@@ -414,12 +478,49 @@ interface TypertContextRegistry {
414
478
  */
415
479
  subscribe(listener: TypertRegistryListener): TypertDisposer;
416
480
  }
481
+ /** Default wire field of one resource kind. */
482
+ interface TypertResourceDeclaration {
483
+ /** Top-level or first-object wire field holding one identity of the kind. */
484
+ readonly field: string;
485
+ }
486
+ /** One registered resource kind with its default wire field. */
487
+ interface TypertResourceEntry extends TypertResourceDeclaration {
488
+ /** Registered resource kind. */
489
+ readonly kind: string;
490
+ }
491
+ /** Runtime registry of resource kinds' default wire fields. */
492
+ interface TypertResourceRegistry {
493
+ /**
494
+ * Register one kind's default wire field for the calling fiber. Only the
495
+ * domain that owns the kind registers it.
496
+ * @param kind - merge-declared resource kind.
497
+ * @param declaration - the kind's default wire field.
498
+ * @returns disposer withdrawing the exact registration.
499
+ * @throws Error when the kind is already registered or is `global`, the built-in resource kind.
500
+ */
501
+ register(kind: TypertResourceKind, declaration: TypertResourceDeclaration): TypertDisposer;
502
+ /**
503
+ * Look up one kind's declaration.
504
+ * @param kind - resource kind.
505
+ * @returns the live declaration, or `undefined` when unregistered.
506
+ */
507
+ get(kind: string): TypertResourceDeclaration | undefined;
508
+ /** @returns a registration-order snapshot of registered kinds with their fields. */
509
+ entries(): readonly TypertResourceEntry[];
510
+ /**
511
+ * Observe later resource-kind changes.
512
+ * @param listener - synchronous contained observer.
513
+ * @returns disposer for this subscription.
514
+ */
515
+ subscribe(listener: TypertRegistryListener): TypertDisposer;
516
+ }
417
517
  /** Minimal Typert runtime consumed through dependency inversion. */
418
518
  interface TypertRegistryContract {
419
519
  readonly local: TypertLocalRegistry;
420
520
  readonly remotes: TypertRemoteRegistry;
421
521
  readonly lookups: TypertLookupRegistry;
422
522
  readonly contexts: TypertContextRegistry;
523
+ readonly resources: TypertResourceRegistry;
423
524
  }
424
525
  declare module '@deepseek-ai/cordis' {
425
526
  interface Context {
@@ -453,7 +554,7 @@ declare abstract class TypertRemoteService<out T = never> extends Service<T> {
453
554
  * Register the Service and bind the same key to Typert Gateway.
454
555
  * @param ctx - owning Cordis Context.
455
556
  * @param serviceKey - exact Cordis service key and default wire namespace.
456
- * @param options - optional distinct wire namespace.
557
+ * @param options - optional wire namespace.
457
558
  */
458
559
  protected constructor(ctx: Context, serviceKey: string, options?: TypertGatewayBindingOptions);
459
560
  }
@@ -703,9 +804,8 @@ interface ToolRemovalBlock {
703
804
  }
704
805
  /**
705
806
  * Merge-extensible content blocks keyed by `type`. New core blocks must land
706
- * with adapter, UI, and compaction support. Developer tool-change blocks are
707
- * reserved for Session V4 persistence; providers and UI reject them until
708
- * their producers and consumers are implemented together.
807
+ * with adapter, UI, and compaction support. Tool-change blocks belong to
808
+ * developer messages; `projectToolUpdates` selects what each route receives.
709
809
  */
710
810
  interface ContentBlockMap {
711
811
  'text': TextBlock;
@@ -838,9 +938,26 @@ interface LlmConfigurableProvider {
838
938
  * from outside.
839
939
  */
840
940
  declared?: boolean;
941
+ /**
942
+ * Route endpoint the adapter uses when the profile sets no `baseURL`; the
943
+ * adapter may still send an individual model to that model's own endpoint.
944
+ * Absent when the route has no fixed endpoint.
945
+ */
946
+ defaultBaseURL?: string;
947
+ /** Environment variables used to construct a provider endpoint; absent when none are reported. */
948
+ baseURLEnv?: readonly string[];
949
+ /** Authentication the adapter performs when the profile names no credential reference; absent when it reports none. */
950
+ nativeAuth?: LlmNativeAuth;
841
951
  /** Configuration diagnostic for repair; unaffected models may remain serviceable. */
842
952
  error?: string;
843
953
  }
954
+ /** Authentication a provider route performs without a credential reference in its profile. */
955
+ interface LlmNativeAuth {
956
+ /** Environment variables the route's authentication reads, in lookup order; empty when it reads none. */
957
+ env: readonly string[];
958
+ /** Whether a stored OAuth sign-in can authenticate the route. */
959
+ oauth: boolean;
960
+ }
844
961
  /**
845
962
  * One interrogation of a provider endpoint that configuration has not stored
846
963
  * yet. Configuration surfaces send the draft a user is still editing, so the
@@ -920,7 +1037,7 @@ interface LlmReasoningEffortInfo {
920
1037
  }
921
1038
  /** Selectable reasoning efforts for one exact provider/model route. */
922
1039
  interface LlmModelReasoningInfo {
923
- /** Supported efforts in adapter-preferred display order. */
1040
+ /** Supported efforts from least to greatest selectable reasoning effort, not predicted token use or latency. */
924
1041
  efforts: readonly LlmReasoningEffortInfo[];
925
1042
  /**
926
1043
  * Adapter-configured default materialized into requests when callers omit
@@ -935,6 +1052,16 @@ interface LlmModelReasoningInfo {
935
1052
  * can follow the cached history instead of rewriting message 0.
936
1053
  */
937
1054
  type SystemPromptUpdate = 'in-history';
1055
+ /**
1056
+ * How a model accepts native tool declarations that change mid-conversation.
1057
+ * `'addition-only'`: the model reads a `tool-addition` block in a later
1058
+ * developer message as activating a tool declared with `deferLoading`, so an
1059
+ * added tool follows the cached history instead of rewriting the declaration
1060
+ * list. `'in-history'`: the model additionally reads `tool-removal` blocks,
1061
+ * so a removed tool keeps its declaration and the removal follows the history.
1062
+ * Absent means every request declares the complete current tool list.
1063
+ */
1064
+ type ToolUpdate = 'in-history' | 'addition-only';
938
1065
  /** Exact-route model metadata resolved by its owning adapter. */
939
1066
  interface LlmResolvedModelInfo extends LlmModelInfo {
940
1067
  /** Provider-owned context capacity when known. */
@@ -945,6 +1072,8 @@ interface LlmResolvedModelInfo extends LlmModelInfo {
945
1072
  reasoning?: LlmModelReasoningInfo;
946
1073
  /** Declared mid-conversation system prompt handling; absent means only a leading system message is read. */
947
1074
  systemPromptUpdate?: SystemPromptUpdate;
1075
+ /** Declared mid-conversation tool declaration handling; absent means every request declares the complete tool list. */
1076
+ toolUpdate?: ToolUpdate;
948
1077
  }
949
1078
  /**
950
1079
  * Adapter-private lossless-JSON state for replaying a successful response,
@@ -1033,6 +1162,18 @@ interface RequestUserInput {
1033
1162
  }
1034
1163
  /** A durable conversation message or a user input used only for one request. */
1035
1164
  type RequestMessage = Message | RequestUserInput;
1165
+ /** Logged tool declarations and update identities since the last declaration reset. */
1166
+ interface ToolHistory {
1167
+ /** Complete active declarations at the start of this history. */
1168
+ readonly tools: readonly ToolSchema[];
1169
+ /** Ordered developer messages, with additions resolved from their historical headers. */
1170
+ readonly updates: readonly {
1171
+ /** Identity used to locate this update in the derived request history. */
1172
+ readonly messageId: MessageId;
1173
+ /** Added definitions resolved from the event's referenced request header. */
1174
+ readonly additions: readonly ToolSchema[];
1175
+ }[];
1176
+ }
1036
1177
  /** A single model request, fully assembled. */
1037
1178
  interface GenerateOptions {
1038
1179
  /** Registered provider route selecting the adapter instance. */
@@ -1054,6 +1195,8 @@ interface GenerateOptions {
1054
1195
  system?: string;
1055
1196
  /** Tool schemas (adapters map to the provider's `tools` field). */
1056
1197
  tools?: ToolSchema[];
1198
+ /** Session-folded tool history used for route projection; omission sends complete declarations without tool updates. */
1199
+ toolHistory?: ToolHistory;
1057
1200
  temperature?: number;
1058
1201
  maxTokens?: number;
1059
1202
  /**
@@ -1070,8 +1213,8 @@ interface GenerateOptions {
1070
1213
  sessionId?: Branded<'SessionId'>;
1071
1214
  /**
1072
1215
  * Provider-neutral classification for an auxiliary model call. Adapters may
1073
- * map the purpose to model-hidden transport metadata or purpose-specific
1074
- * generation policy. Ordinary conversation requests leave it unset.
1216
+ * map the purpose to model-hidden transport metadata. Ordinary conversation
1217
+ * requests leave it unset.
1075
1218
  */
1076
1219
  purpose?: 'compaction' | 'session-title';
1077
1220
  }
@@ -1407,20 +1550,33 @@ interface ResolvedAlwaysRetryPolicy extends ResolvedRetryBackoff {
1407
1550
  type ResolvedRetryPolicy = ResolvedNormalRetryPolicy | ResolvedAlwaysRetryPolicy;
1408
1551
  //#endregion
1409
1552
  //#region ../../node_modules/@deepseek-ai/dsh-llm/lib/types/call-config.d.ts
1553
+ /** Concrete generation settings; omitted controls use the selected route's defaults. */
1554
+ interface LlmCallControls {
1555
+ reasoningEffort?: ReasoningEffortId;
1556
+ temperature?: number;
1557
+ maxTokens?: number;
1558
+ stop?: string[];
1559
+ }
1410
1560
  /**
1411
1561
  * Provider, model, reasoning effort, and sampling scalars of one conversation's
1412
1562
  * requests. Every field maps 1:1 onto the same-named `GenerateOptions` field;
1413
1563
  * the loop builds requests from the logged header rather than accepting these
1414
1564
  * per call.
1415
1565
  */
1416
- interface LlmCallConfig {
1566
+ interface LlmCallConfig extends LlmCallControls {
1417
1567
  provider: string;
1418
1568
  model: string;
1419
- reasoningEffort?: ReasoningEffortId;
1420
- temperature?: number;
1421
- maxTokens?: number;
1422
- stop?: string[];
1423
1569
  }
1570
+ /**
1571
+ * Synchronous, pure configuration of one call before defaults and validation.
1572
+ * Compose functions in the desired order; later writes replace earlier ones.
1573
+ * The returned controls cannot change the captured route. Errors reject
1574
+ * preparation before dispatch; only the resolved configuration is recordable.
1575
+ * @param controls - detached, deeply frozen proposed controls, without defaults.
1576
+ * @param model - detached, deeply frozen metadata from the captured adapter generation.
1577
+ * @returns concrete controls; omitted fields receive the route's defaults.
1578
+ */
1579
+ type ConfigureCall = (controls: Readonly<LlmCallControls>, model: Readonly<LlmResolvedModelInfo>) => LlmCallControls;
1424
1580
  /**
1425
1581
  * Effective config fields supplied by exact-model adapter resolution rather
1426
1582
  * than by the caller's request proposal.
@@ -1463,6 +1619,8 @@ interface PreparedLlmCall {
1463
1619
  readonly inputModalities?: readonly ModelModality[];
1464
1620
  /** Exact model system prompt update mode captured with the adapter dispatch generation. */
1465
1621
  readonly systemPromptUpdate?: SystemPromptUpdate;
1622
+ /** Exact model tool update mode captured with the adapter dispatch generation. */
1623
+ readonly toolUpdate?: ToolUpdate;
1466
1624
  /** Config fields materialized by the captured adapter rather than proposed by the caller. */
1467
1625
  readonly adapterDefaults: LlmCallConfigAdapterDefaults;
1468
1626
  /**
@@ -1510,10 +1668,20 @@ declare abstract class LlmAdapter {
1510
1668
  * @returns route-owned image pricing, or `undefined` when the route declares none.
1511
1669
  */
1512
1670
  imageRequestPricing(_provider: string, _model: string): LlmImageRequestPricing | undefined;
1671
+ /**
1672
+ * Resolve the image dimensions and byte target this adapter sends to the model.
1673
+ * The default preserves normalized images; adapters that resize must override it.
1674
+ * @param _provider - registered provider route.
1675
+ * @param _model - exact model id.
1676
+ * @param image - normalized image whose request projection is being resolved.
1677
+ * @returns the same target used by request serialization, before provider-internal processing.
1678
+ */
1679
+ imageRequestTarget(_provider: string, _model: string, image: ImageAttachmentRef): ImageRequestTarget;
1513
1680
  /**
1514
1681
  * List models this adapter can currently advertise for one owned provider.
1515
- * The result is advisory: an adapter may accept unlisted model ids, and
1516
- * consumers must not turn absence into request rejection.
1682
+ * Core routing accepts unlisted model ids; catalog-driven entry points such
1683
+ * as the GUI may require membership. Adapters used there must advertise
1684
+ * their available models; the base empty catalog offers no GUI selection.
1517
1685
  * @param _provider - one provider route owned by this adapter.
1518
1686
  * @returns discoverable models in adapter-preferred order.
1519
1687
  */
@@ -1690,6 +1858,14 @@ declare class LlmRuntime extends TypertRemoteService {
1690
1858
  * @returns the owning adapter's image pricing for the route, when declared.
1691
1859
  */
1692
1860
  imageRequestPricing(provider: string, model: string): LlmImageRequestPricing | undefined;
1861
+ /**
1862
+ * Resolve the image projection for an exact registered model route.
1863
+ * @param provider - registered provider route.
1864
+ * @param model - exact model id.
1865
+ * @param image - normalized image reference.
1866
+ * @returns dimensions and byte target used by the adapter's request serializer.
1867
+ */
1868
+ imageRequestTarget(provider: string, model: string, image: ImageAttachmentRef): ImageRequestTarget;
1693
1869
  /**
1694
1870
  * Resolve the exact text one durable file occurrence contributes to every
1695
1871
  * provider request in the current execution environment.
@@ -1701,7 +1877,8 @@ declare class LlmRuntime extends TypertRemoteService {
1701
1877
  private detachedModalities;
1702
1878
  /**
1703
1879
  * Discover models advertised by one registered provider. Catalog membership
1704
- * is advisory and never changes routing or request validation.
1880
+ * does not constrain core routing. Catalog-driven entry points may restrict
1881
+ * selection and submission to the advertised models.
1705
1882
  * @param provider - registered provider route to inspect.
1706
1883
  * @returns detached model metadata in adapter-preferred order.
1707
1884
  */
@@ -1737,11 +1914,15 @@ declare class LlmRuntime extends TypertRemoteService {
1737
1914
  * Resolve one call under its current adapter registration. The returned
1738
1915
  * one-shot handle keeps that registration across header logging and dispatch,
1739
1916
  * so HMR cannot combine one adapter's capability result with another adapter.
1740
- * @param config - provider/model route and optional request controls.
1917
+ * An optional synchronous callback selects concrete controls using captured
1918
+ * model metadata. Defaults and validation apply to its result. Callback
1919
+ * failures and cancellation reject preparation before dispatch.
1920
+ * @param config - provider/model route and optional concrete request controls.
1741
1921
  * @param signal - optional cancellation for adapter-owned capability lookup.
1922
+ * @param configure - pure control selection, called once before defaults and validation.
1742
1923
  * @returns a prepared config and its registration-bound stream entry point.
1743
1924
  */
1744
- prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<PreparedLlmCall>;
1925
+ prepareCall(config: LlmCallConfig, signal?: AbortSignal, configure?: ConfigureCall): Promise<PreparedLlmCall>;
1745
1926
  private registration;
1746
1927
  /** Remove replay state whose historical route is owned by another adapter. */
1747
1928
  private forAdapter;
@@ -1,3 +1,3 @@
1
1
  import "./workspace-instructions.js";
2
- import { a as registerWorkspaceInstructionInjector, i as name, n as escapeInstructionFrameBody, o as renderInstructionMessageText, r as inject, s as resolveSnapshotForSession, t as createInstructionMessage } from "../workspace-instruction-context-DzHFTN87.js";
2
+ import { a as registerWorkspaceInstructionInjector, i as name, n as escapeInstructionFrameBody, o as renderInstructionMessageText, r as inject, s as resolveSnapshotForSession, t as createInstructionMessage } from "../workspace-instruction-context-DytAmaPY.js";
3
3
  export { createInstructionMessage, escapeInstructionFrameBody, inject, name, registerWorkspaceInstructionInjector, renderInstructionMessageText, resolveSnapshotForSession };