@rebon/cli-darwin-arm64 0.17.3 → 0.19.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.
Files changed (82) hide show
  1. package/bin/compose-runtime/package.json +17 -0
  2. package/bin/compose-runtime/payload/compose/credentials-runtime.js +38 -0
  3. package/bin/compose-runtime/payload/compose/llm-runtime.js +330 -0
  4. package/bin/compose-runtime/payload/compose/loop-assembly.js +149 -0
  5. package/bin/compose-runtime/payload/compose/serve-dispatch.js +82 -0
  6. package/bin/compose-runtime/payload/compose/shims/dsh-anonymous-user-id.js +10 -0
  7. package/bin/compose-runtime/payload/compose/shims/dsh-credentials.js +13 -0
  8. package/bin/compose-runtime/payload/compose/shims/dsh-launch-environment.js +21 -0
  9. package/bin/compose-runtime/payload/compose/shims/dsh-llm.js +313 -0
  10. package/bin/compose-runtime/payload/compose/shims/dsh-settings.js +43 -0
  11. package/bin/compose-runtime/payload/compose/shims/dsh-tools.js +20 -0
  12. package/bin/compose-runtime/payload/compose/shims/dsh-web.js +10 -0
  13. package/bin/compose-runtime/payload/compose/shims/llm-adapter.js +32 -0
  14. package/bin/compose-runtime/payload/compose/shims/zod-lite.js +49 -0
  15. package/bin/compose-runtime/payload/compose/systemprompt-runtime.js +74 -0
  16. package/bin/compose-runtime/payload/compose/tools-runtime.js +228 -0
  17. package/bin/compose-runtime/payload/compose/web-runtime.js +204 -0
  18. package/bin/compose-runtime/payload/vendor/cordis/index.js +1530 -0
  19. package/bin/compose-runtime/payload/vendor/cosmokit/LICENSE +21 -0
  20. package/bin/compose-runtime/payload/vendor/cosmokit/index.mjs +357 -0
  21. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-agent +21 -0
  22. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-agent-loop +21 -0
  23. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-llm-core +21 -0
  24. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-llm-deepseek +21 -0
  25. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-logger-console +21 -0
  26. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-scope +21 -0
  27. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-session +21 -0
  28. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-system-prompt +21 -0
  29. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-timeout +21 -0
  30. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-timer +21 -0
  31. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-tool-todo +21 -0
  32. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-tool-web +21 -0
  33. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-tools-schema +21 -0
  34. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-web-search-exa +21 -0
  35. package/bin/compose-runtime/payload/vendor/dsh/agent-loop.js +1193 -0
  36. package/bin/compose-runtime/payload/vendor/dsh/agent.js +714 -0
  37. package/bin/compose-runtime/payload/vendor/dsh/llm-core.js +269 -0
  38. package/bin/compose-runtime/payload/vendor/dsh/llm-deepseek.js +672 -0
  39. package/bin/compose-runtime/payload/vendor/dsh/logger-console.js +83 -0
  40. package/bin/compose-runtime/payload/vendor/dsh/scope.js +287 -0
  41. package/bin/compose-runtime/payload/vendor/dsh/session.js +1668 -0
  42. package/bin/compose-runtime/payload/vendor/dsh/system-prompt.js +309 -0
  43. package/bin/compose-runtime/payload/vendor/dsh/timeout.js +100 -0
  44. package/bin/compose-runtime/payload/vendor/dsh/timer.js +128 -0
  45. package/bin/compose-runtime/payload/vendor/dsh/tool-todo.js +143 -0
  46. package/bin/compose-runtime/payload/vendor/dsh/tool-web.js +1527 -0
  47. package/bin/compose-runtime/payload/vendor/dsh/tools-schema.js +849 -0
  48. package/bin/compose-runtime/payload/vendor/dsh/web-search-exa.js +122 -0
  49. package/bin/compose-runtime/payload/vendor/eventsource-parser/LICENSE +21 -0
  50. package/bin/compose-runtime/payload/vendor/eventsource-parser/index.js +177 -0
  51. package/bin/compose-runtime/payload/vendor/eventsource-parser/stream.js +48 -0
  52. package/bin/compose-runtime/payload/vendor/schemastery/index.mjs +656 -0
  53. package/bin/compose-runtime/payload-manifests.json +48 -0
  54. package/bin/compose-runtime/src/bridge.mjs +142 -0
  55. package/bin/compose-runtime/src/credentials-runtime.mjs +50 -0
  56. package/bin/compose-runtime/src/eventsource-stream.mjs +48 -0
  57. package/bin/compose-runtime/src/index.mjs +7 -0
  58. package/bin/compose-runtime/src/llm-runtime.mjs +326 -0
  59. package/bin/compose-runtime/src/loader.mjs +78 -0
  60. package/bin/compose-runtime/src/loop-assembly.mjs +237 -0
  61. package/bin/compose-runtime/src/payload.mjs +49 -0
  62. package/bin/compose-runtime/src/plugin.mjs +59 -0
  63. package/bin/compose-runtime/src/realm.mjs +223 -0
  64. package/bin/compose-runtime/src/registry.mjs +191 -0
  65. package/bin/compose-runtime/src/resolve.mjs +115 -0
  66. package/bin/compose-runtime/src/systemprompt-runtime.mjs +67 -0
  67. package/bin/compose-runtime/src/tools-runtime.mjs +239 -0
  68. package/bin/compose-runtime/src/web-runtime.mjs +209 -0
  69. package/bin/plugin-host/package.json +11 -0
  70. package/bin/plugin-host/src/bridge.mjs +64 -0
  71. package/bin/plugin-host/src/cli.mjs +105 -0
  72. package/bin/plugin-host/src/framing.mjs +113 -0
  73. package/bin/plugin-host/src/host.mjs +624 -0
  74. package/bin/plugin-host/src/json.mjs +196 -0
  75. package/bin/plugin-host/src/lifecycle.mjs +114 -0
  76. package/bin/plugin-host/src/loader.mjs +142 -0
  77. package/bin/plugin-host/src/methods.mjs +256 -0
  78. package/bin/plugin-host/src/protocol.mjs +129 -0
  79. package/bin/plugin-host/src/sdk.mjs +7 -0
  80. package/bin/rebon +0 -0
  81. package/bin/rebon-boa-helper +0 -0
  82. package/package.json +4 -1
@@ -0,0 +1,228 @@
1
+ // Minimal dsh-compatible `ctx.tools` seat for the composition host (P1.6,
2
+ // docs/dsh-tools-assembly.md).
3
+ //
4
+ // API shape follows dsh packages/core/tools: plugins call
5
+ // `ctx.tools.register(defineTool({...}))` and get back the exact disposer.
6
+ // Definitions arrive with `parameters` already compiled to JSON Schema and
7
+ // argument validation baked into `execute` (the vendored dsh-tools schema
8
+ // layer does both). Each registration is mirrored into the kernel's
9
+ // process-level `tool-registry` service — that call rides the bridge effect
10
+ // ledger, so a hard-killed composition can never leave the kernel table
11
+ // dirty — and execution requests come back over the shared serve pump
12
+ // (serve-dispatch.js, target = the tool name), running the registered body
13
+ // in this isolate.
14
+ //
15
+ // Deliberately NOT provided (v1 honesty boundary, see the assembly doc):
16
+ // `restrict`/`guard` (dsh agent-scope concepts), code mode, output-schema
17
+ // enforcement of the canonical value, presenter projections. `exec` carries
18
+ // the identity fields plus an `agent.session.append` bridge that lands as
19
+ // the kernel event `compose:session/append` — observable, but not a claim
20
+ // of rebon transcript persistence (that is the P2.9 session seat).
21
+ import { Service } from 'cordis';
22
+ import { callService, emit, invokeTool } from 'rebon';
23
+ import { TOOL_RUNTIME_SCHEDULER } from '@deepseek-ai/dsh-tools';
24
+ import { ensureServePump, registerServeTarget } from './serve-dispatch.js';
25
+
26
+ /** Extract a `[BRACKET_CODE]` prefix from a seat error message, if any. */
27
+ function bracketCode(message) {
28
+ const match = /^\[([A-Z_]+)\]/.exec(message);
29
+ return match ? match[1] : undefined;
30
+ }
31
+
32
+ /** Per-run context/conclusion collection installed by scheduler.prepare. */
33
+ const runStates = new WeakMap();
34
+
35
+ export default class RebonToolsRuntime extends Service {
36
+ constructor(ctx) {
37
+ super(ctx, 'tools');
38
+ this.names = new Set();
39
+ // Local definition table for the scheduler's in-composition dispatch
40
+ // (register() below keeps it in step with the kernel mirror).
41
+ this.defs = new Map();
42
+ ensureServePump();
43
+
44
+ // ---- dsh scheduler contract (loop round) ----
45
+ //
46
+ // agent-loop's tool-calls scheduler drives tools through this
47
+ // symbol-keyed view (dsh's private loop↔tools seam, contract
48
+ // transcribed from packages/core/tools/src/index.ts:451). Dispatch
49
+ // resolution: a composition-registered definition runs in-isolate;
50
+ // anything else goes to the R5 core seat, whose refusals (stable
51
+ // bracketed codes) come back as error RESULTS for the model, not
52
+ // crashes. v1 honesty boundary: no pre/post-execute pipeline —
53
+ // prepare always dispatches, finalize/finish pass through.
54
+ const runtime = this;
55
+ this[TOOL_RUNTIME_SCHEDULER] = {
56
+ async prepare(exec) {
57
+ const state = { contexts: [], concluded: false };
58
+ const run = {
59
+ ...exec,
60
+ rootCallId: exec.rootCallId ?? exec.callId,
61
+ deferContext(message) {
62
+ state.contexts.push(message);
63
+ },
64
+ concludeTurn() {
65
+ state.concluded = true;
66
+ },
67
+ };
68
+ runStates.set(run, state);
69
+ return { kind: 'dispatch', exec: run };
70
+ },
71
+ async dispatch(exec) {
72
+ const state = runStates.get(exec) ?? { contexts: [], concluded: false };
73
+ try {
74
+ const def = runtime.defs.get(exec.name);
75
+ let content;
76
+ if (def !== undefined) {
77
+ const value = await def.execute(exec.arguments, exec);
78
+ content = def.output.render(exec.arguments, value);
79
+ } else {
80
+ const value = await invokeTool(exec.name, exec.arguments ?? {});
81
+ content = [{
82
+ type: 'text',
83
+ text: typeof value === 'string' ? value : JSON.stringify(value),
84
+ }];
85
+ }
86
+ return {
87
+ kind: 'final-result',
88
+ result: {
89
+ content,
90
+ isError: false,
91
+ ...(state.contexts.length > 0 ? { additionalContexts: state.contexts } : {}),
92
+ ...(state.concluded ? { concludesTurn: true } : {}),
93
+ },
94
+ };
95
+ } catch (error) {
96
+ const message = String(error?.message ?? error);
97
+ return {
98
+ kind: 'final-result',
99
+ result: {
100
+ content: [{ type: 'text', text: `Error: ${message}` }],
101
+ isError: true,
102
+ error: {
103
+ message,
104
+ info: {
105
+ name: error?.name ?? 'Error',
106
+ code: bracketCode(message) ?? 'TOOL_FAILED',
107
+ },
108
+ },
109
+ },
110
+ };
111
+ }
112
+ },
113
+ async finalize(_exec, result) {
114
+ return result;
115
+ },
116
+ finish(_exec, result) {
117
+ return result;
118
+ },
119
+ };
120
+ }
121
+
122
+ /**
123
+ * dsh concurrency classification. v1: everything is exclusive — strictly
124
+ * serial scheduling is always semantically correct, and it sidesteps the
125
+ * parallel pool until `isConcurrencySafe` mapping is wired.
126
+ */
127
+ executionMode(_exec) {
128
+ return { kind: 'exclusive' };
129
+ }
130
+
131
+ /**
132
+ * Register one dsh-shaped tool definition. Mirrors dsh
133
+ * `ToolRuntime.register` validation; returns the exact disposer.
134
+ */
135
+ register(definition) {
136
+ const name = definition?.name;
137
+ if (typeof name !== 'string' || name.trim().length === 0) {
138
+ throw new TypeError('tools.register: definition.name must be a non-empty string');
139
+ }
140
+ if (name.startsWith('web:')) {
141
+ throw new Error(`tool name "${name}" uses the reserved web-provider serve namespace`);
142
+ }
143
+ const output = definition.output;
144
+ if (
145
+ output === undefined || typeof output !== 'object'
146
+ || typeof output.render !== 'function'
147
+ ) {
148
+ throw new TypeError(`tool "${name}" must declare output { schema, render }`);
149
+ }
150
+ if (typeof definition.execute !== 'function') {
151
+ throw new TypeError(`tool "${name}" must declare execute()`);
152
+ }
153
+ const timeoutMs = definition.timeoutMs;
154
+ if (timeoutMs !== undefined && (!Number.isFinite(timeoutMs) || timeoutMs <= 0)) {
155
+ throw new TypeError(`tool "${name}" timeoutMs must be a positive finite number`);
156
+ }
157
+ if (this.names.has(name)) {
158
+ throw new Error(`tool "${name}" is already registered in this scope`);
159
+ }
160
+ const runtime = this;
161
+ // `this.ctx` is the CALLER's context through the Service proxy, so the
162
+ // effect (and with it the kernel mirror) unwinds when the registering
163
+ // plugin disposes — cordis RAII, llm-runtime same pattern. ctx.effect
164
+ // returns the disposer, which dsh's register contract hands back.
165
+ // Ledger attribution: the composition-entry id, captured synchronously
166
+ // (never ambient — async interleavings must not mis-attribute).
167
+ const owner = this.ctx[Symbol.for('rebon.composeOwner')] ?? '';
168
+ return this.ctx.effect(() => {
169
+ const registered = callService('tool-registry', 'register', {
170
+ name,
171
+ description: String(definition.description ?? ''),
172
+ inputSchema: definition.parameters ?? { type: 'object' },
173
+ }, owner);
174
+ if (registered?.shadowed) {
175
+ // Same rule as R3: builtin always wins at dispatch; loud, not
176
+ // fatal — and best-effort, because the Rust registry already
177
+ // logged it and a missing logger seat must not fail registration.
178
+ const message = `composition tool "${name}" shares a builtin tool's name; the builtin wins`;
179
+ try {
180
+ callService('logger', 'warn', { message });
181
+ } catch {
182
+ console.warn(message);
183
+ }
184
+ }
185
+ runtime.names.add(name);
186
+ runtime.defs.set(name, definition);
187
+ const disposeServe = registerServeTarget(name, (input, signal, id) =>
188
+ runtime._execute(definition, input, signal, id),
189
+ );
190
+ const token = registered?.token;
191
+ return () => {
192
+ disposeServe();
193
+ runtime.names.delete(name);
194
+ runtime.defs.delete(name);
195
+ try {
196
+ callService('tool-registry', 'unregister', { name, token });
197
+ } catch {}
198
+ };
199
+ });
200
+ }
201
+
202
+ async _execute(definition, input, signal, id) {
203
+ const callId = `compose:${id}`;
204
+ const tool = definition.name;
205
+ const exec = {
206
+ callId,
207
+ rootCallId: callId,
208
+ name: tool,
209
+ arguments: input,
210
+ signal,
211
+ // v1 bridge: dsh tools write per-session facts through
212
+ // `exec.agent.session.append`; here that lands as a kernel event
213
+ // the embedder can observe. No persistence claim.
214
+ agent: {
215
+ session: {
216
+ append(type, data) {
217
+ emit('compose:session/append', { type, data, tool, callId });
218
+ },
219
+ },
220
+ },
221
+ };
222
+ // defineTool's execute validates arguments first (ToolArgsError with
223
+ // INVALID_ARGS semantics flows into the err envelope's message).
224
+ const value = await definition.execute(input, exec);
225
+ const content = definition.output.render(input, value);
226
+ return { content, isError: false };
227
+ }
228
+ }
@@ -0,0 +1,204 @@
1
+ // Minimal dsh-compatible `ctx.web` seat for the composition host (P2.7,
2
+ // docs/dsh-web-assembly.md).
3
+ //
4
+ // API shape follows dsh packages/web/web verbatim: providers register with
5
+ // `ctx.web.registerSearchProvider/registerFetchProvider` (duplicate ids
6
+ // throw WEB_DUPLICATE_PROVIDER; the exact disposer comes back), and
7
+ // `search`/`fetch` resolve their provider at EXECUTION time with dsh's
8
+ // five-state rules — a configured id wins or fails loudly (never falls
9
+ // back), otherwise exactly one usable provider auto-selects. The seat
10
+ // enforces `maxResults` truncation on search results (capSources).
11
+ //
12
+ // Deployment default: the `rebon` builtin provider (backed by the R5
13
+ // invokeTool bridge onto rebon's own WebSearch/WebFetch) is always
14
+ // registered, and the configured id DEFAULTS to `"rebon"` — feeding the
15
+ // same config field dsh uses (`bootConfig().web.searchProvider` /
16
+ // `.fetchProvider`, i.e. config.json's `web` section), never a hidden
17
+ // priority chain. So plugin providers take over only when configured, and
18
+ // the multi-provider AMBIGUOUS state is unreachable unless the default is
19
+ // explicitly cleared.
20
+ //
21
+ // Registrations are mirrored to the Rust seat via
22
+ // `web/provider-registered` / `web/provider-unregistered` kernel events
23
+ // (`available` is a registration-time snapshot — a static composition's
24
+ // config does not change under it; documented v1 semantics), and each
25
+ // plugin provider is served back to Rust dispatch through the shared serve
26
+ // pump under the reserved target `web:<kind>:<id>`.
27
+ import { Service } from 'cordis';
28
+ import { bootConfig, emit, invokeTool } from 'rebon';
29
+ import { WebError } from './shims/dsh-web.js';
30
+ import { ensureServePump, registerServeTarget } from './serve-dispatch.js';
31
+
32
+ /** dsh WebRuntime.resolveProvider, transcribed. */
33
+ function resolveProvider(configuredId, providers) {
34
+ if (configuredId !== undefined) {
35
+ const provider = providers.get(configuredId);
36
+ if (!provider) {
37
+ throw new WebError(
38
+ `configured web provider "${configuredId}" is not registered`,
39
+ 'WEB_PROVIDER_CONFIGURED_MISSING',
40
+ );
41
+ }
42
+ if (!provider.available()) {
43
+ throw new WebError(
44
+ `configured web provider "${configuredId}" is registered but unavailable`,
45
+ 'WEB_PROVIDER_CONFIGURED_UNAVAILABLE',
46
+ );
47
+ }
48
+ return provider;
49
+ }
50
+ const usable = [...providers.values()].filter((provider) => {
51
+ try {
52
+ return !!provider.available();
53
+ } catch {
54
+ return false;
55
+ }
56
+ });
57
+ if (usable.length === 0) {
58
+ throw new WebError('no usable web provider is registered', 'WEB_PROVIDER_UNAVAILABLE');
59
+ }
60
+ if (usable.length > 1) {
61
+ const ids = usable.map((provider) => provider.id).join(', ');
62
+ throw new WebError(
63
+ `multiple usable web providers are registered (${ids}); configure one explicitly`,
64
+ 'WEB_PROVIDER_AMBIGUOUS',
65
+ );
66
+ }
67
+ return usable[0];
68
+ }
69
+
70
+ /** dsh capSources: enforce `maxResults` on the way back. */
71
+ function capSources(result, maxResults) {
72
+ if (maxResults === undefined || (result.sources?.length ?? 0) <= maxResults) return result;
73
+ return { ...result, sources: result.sources.slice(0, maxResults), truncated: true };
74
+ }
75
+
76
+ /**
77
+ * The rebon builtin search provider: rides the R5 invokeTool bridge onto
78
+ * rebon's WebSearch tool (read-only, so no grant is needed) and maps the
79
+ * `{query, answer, results}` contract into dsh's result vocabulary.
80
+ */
81
+ const rebonBuiltinSearch = {
82
+ id: 'rebon',
83
+ available: () => true,
84
+ async search(request, _signal) {
85
+ const out = await invokeTool('WebSearch', { query: String(request?.query ?? '') });
86
+ const sources = (out?.results ?? [])
87
+ .map((entry) => ({
88
+ url: String(entry?.url ?? ''),
89
+ ...(entry?.title ? { title: String(entry.title) } : {}),
90
+ ...(entry?.snippet ? { snippet: String(entry.snippet) } : {}),
91
+ }))
92
+ .filter((source) => source.url.length > 0);
93
+ return {
94
+ ...(out?.answer ? { content: String(out.answer) } : {}),
95
+ sources,
96
+ truncated: false,
97
+ };
98
+ },
99
+ };
100
+
101
+ /**
102
+ * The rebon builtin fetch provider over rebon's WebFetch tool. The tool
103
+ * returns extracted text, not raw transport facts, so `statusCode` is the
104
+ * 200 the extraction implies and the body is `text` — a documented v1
105
+ * approximation, not a transport claim.
106
+ */
107
+ const rebonBuiltinFetch = {
108
+ id: 'rebon',
109
+ available: () => true,
110
+ async fetch(request, _signal) {
111
+ const out = await invokeTool('WebFetch', { url: String(request?.url ?? '') });
112
+ const content =
113
+ typeof out === 'string' ? out : String(out?.content ?? out?.text ?? JSON.stringify(out));
114
+ return {
115
+ url: String(request?.url ?? ''),
116
+ statusCode: 200,
117
+ body: { kind: 'text', content },
118
+ truncated: false,
119
+ };
120
+ },
121
+ };
122
+
123
+ export default class RebonWebRuntime extends Service {
124
+ constructor(ctx, options) {
125
+ super(ctx, 'web');
126
+ // Per-loop hosts keep provider registrations off the process event
127
+ // plane (llm-runtime same rule).
128
+ this.announce = options?.announce !== false;
129
+ const config = bootConfig()?.web ?? {};
130
+ const configured = (value) =>
131
+ typeof value === 'string' && value.trim().length > 0 ? value.trim() : 'rebon';
132
+ this.searchProviderId = configured(config.searchProvider);
133
+ this.fetchProviderId = configured(config.fetchProvider);
134
+ this.searchProviders = new Map([['rebon', rebonBuiltinSearch]]);
135
+ this.fetchProviders = new Map([['rebon', rebonBuiltinFetch]]);
136
+ ensureServePump();
137
+ }
138
+
139
+ registerSearchProvider(provider) {
140
+ return this._register(this.searchProviders, 'search', provider);
141
+ }
142
+
143
+ registerFetchProvider(provider) {
144
+ return this._register(this.fetchProviders, 'fetch', provider);
145
+ }
146
+
147
+ _register(store, kind, provider) {
148
+ if (typeof provider?.id !== 'string' || provider.id.length === 0) {
149
+ throw new TypeError('a web provider must declare a non-empty string `id`');
150
+ }
151
+ if (store.has(provider.id)) {
152
+ throw new WebError(
153
+ `a web provider with id "${provider.id}" is already registered`,
154
+ 'WEB_DUPLICATE_PROVIDER',
155
+ );
156
+ }
157
+ const runtime = this;
158
+ // Caller-fork RAII (Service proxy binds this.ctx to the registrant):
159
+ // plugin disposal removes the provider, the kernel mirror, and the
160
+ // serve target together.
161
+ return this.ctx.effect(() => {
162
+ store.set(provider.id, provider);
163
+ let available = false;
164
+ try {
165
+ available = !!provider.available();
166
+ } catch {}
167
+ const disposeServe = registerServeTarget(
168
+ `web:${kind}:${provider.id}`,
169
+ (input, signal) => runtime._serve(kind, provider, input, signal),
170
+ );
171
+ if (runtime.announce) emit('web/provider-registered', { kind, id: provider.id, available });
172
+ return () => {
173
+ disposeServe();
174
+ store.delete(provider.id);
175
+ if (runtime.announce) emit('web/provider-unregistered', { kind, id: provider.id });
176
+ };
177
+ });
178
+ }
179
+
180
+ /** Rust-dispatched execution of one plugin provider (seat rules applied). */
181
+ async _serve(kind, provider, input, signal) {
182
+ if (kind === 'search') {
183
+ const request = {
184
+ query: String(input?.query ?? ''),
185
+ ...(input?.maxResults !== undefined ? { maxResults: input.maxResults } : {}),
186
+ };
187
+ return capSources(await provider.search(request, signal), request.maxResults);
188
+ }
189
+ return provider.fetch({ url: String(input?.url ?? '') }, signal);
190
+ }
191
+
192
+ /** dsh consumer API: run one search through the selected provider. */
193
+ async search(request, signal) {
194
+ const provider = resolveProvider(this.searchProviderId, this.searchProviders);
195
+ const result = await provider.search(request, signal);
196
+ return capSources(result, request?.maxResults);
197
+ }
198
+
199
+ /** dsh consumer API: retrieve one URL through the selected provider. */
200
+ async fetch(request, signal) {
201
+ const provider = resolveProvider(this.fetchProviderId, this.fetchProviders);
202
+ return provider.fetch(request, signal);
203
+ }
204
+ }