@nxisai/agent-harness 0.4.1 → 0.6.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 (137) hide show
  1. package/README.md +25 -2
  2. package/dist/handoff/a2a.d.ts +152 -0
  3. package/dist/handoff/a2a.d.ts.map +1 -0
  4. package/dist/handoff/a2a.js +227 -0
  5. package/dist/handoff/a2a.js.map +1 -0
  6. package/dist/handoff/controller.d.ts +181 -0
  7. package/dist/handoff/controller.d.ts.map +1 -0
  8. package/dist/handoff/controller.js +543 -0
  9. package/dist/handoff/controller.js.map +1 -0
  10. package/dist/handoff/envelope.d.ts +130 -0
  11. package/dist/handoff/envelope.d.ts.map +1 -0
  12. package/dist/handoff/envelope.js +178 -0
  13. package/dist/handoff/envelope.js.map +1 -0
  14. package/dist/handoff/index.d.ts +16 -0
  15. package/dist/handoff/index.d.ts.map +1 -0
  16. package/dist/handoff/index.js +12 -0
  17. package/dist/handoff/index.js.map +1 -0
  18. package/dist/handoff/tools.d.ts +36 -0
  19. package/dist/handoff/tools.d.ts.map +1 -0
  20. package/dist/handoff/tools.js +148 -0
  21. package/dist/handoff/tools.js.map +1 -0
  22. package/dist/harness/harness.d.ts +4 -2
  23. package/dist/harness/harness.d.ts.map +1 -1
  24. package/dist/harness/harness.js +61 -16
  25. package/dist/harness/harness.js.map +1 -1
  26. package/dist/harness/router/coordinator.d.ts +13 -0
  27. package/dist/harness/router/coordinator.d.ts.map +1 -1
  28. package/dist/harness/router/coordinator.js +7 -0
  29. package/dist/harness/router/coordinator.js.map +1 -1
  30. package/dist/harness/tools/action-tools.d.ts.map +1 -1
  31. package/dist/harness/tools/action-tools.js +7 -5
  32. package/dist/harness/tools/action-tools.js.map +1 -1
  33. package/dist/harness/tools/bridge.d.ts.map +1 -1
  34. package/dist/harness/tools/bridge.js +33 -12
  35. package/dist/harness/tools/bridge.js.map +1 -1
  36. package/dist/harness/tools/discovery.d.ts +12 -1
  37. package/dist/harness/tools/discovery.d.ts.map +1 -1
  38. package/dist/harness/tools/discovery.js +19 -1
  39. package/dist/harness/tools/discovery.js.map +1 -1
  40. package/dist/harness/tools/local-context.d.ts +49 -9
  41. package/dist/harness/tools/local-context.d.ts.map +1 -1
  42. package/dist/harness/tools/local-context.js +377 -22
  43. package/dist/harness/tools/local-context.js.map +1 -1
  44. package/dist/harness/tools/open-page.d.ts +16 -0
  45. package/dist/harness/tools/open-page.d.ts.map +1 -1
  46. package/dist/harness/tools/open-page.js +45 -9
  47. package/dist/harness/tools/open-page.js.map +1 -1
  48. package/dist/harness/tools/retry.d.ts +53 -0
  49. package/dist/harness/tools/retry.d.ts.map +1 -0
  50. package/dist/harness/tools/retry.js +96 -0
  51. package/dist/harness/tools/retry.js.map +1 -0
  52. package/dist/harness/tools/schema.d.ts +18 -0
  53. package/dist/harness/tools/schema.d.ts.map +1 -1
  54. package/dist/harness/tools/schema.js +38 -0
  55. package/dist/harness/tools/schema.js.map +1 -1
  56. package/dist/harness/tools/site-actions.d.ts.map +1 -1
  57. package/dist/harness/tools/site-actions.js +3 -2
  58. package/dist/harness/tools/site-actions.js.map +1 -1
  59. package/dist/harness/tools/site-nav.d.ts +168 -0
  60. package/dist/harness/tools/site-nav.d.ts.map +1 -0
  61. package/dist/harness/tools/site-nav.js +316 -0
  62. package/dist/harness/tools/site-nav.js.map +1 -0
  63. package/dist/harness/tools/site-pages.d.ts +4 -0
  64. package/dist/harness/tools/site-pages.d.ts.map +1 -1
  65. package/dist/harness/tools/site-pages.js +19 -3
  66. package/dist/harness/tools/site-pages.js.map +1 -1
  67. package/dist/harness/tools/source.d.ts +9 -0
  68. package/dist/harness/tools/source.d.ts.map +1 -1
  69. package/dist/harness/tools/source.js +54 -14
  70. package/dist/harness/tools/source.js.map +1 -1
  71. package/dist/harness/tools/webmcp.d.ts +39 -3
  72. package/dist/harness/tools/webmcp.d.ts.map +1 -1
  73. package/dist/harness/tools/webmcp.js +93 -14
  74. package/dist/harness/tools/webmcp.js.map +1 -1
  75. package/dist/index.d.ts +6 -1
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +5 -1
  78. package/dist/index.js.map +1 -1
  79. package/dist/types.d.ts +2 -0
  80. package/dist/types.d.ts.map +1 -1
  81. package/dist/ui/agent-fullscreen.d.ts.map +1 -1
  82. package/dist/ui/agent-fullscreen.js +276 -2
  83. package/dist/ui/agent-fullscreen.js.map +1 -1
  84. package/dist/ui/tailwind.generated.d.ts +1 -1
  85. package/dist/ui/tailwind.generated.d.ts.map +1 -1
  86. package/dist/ui/tailwind.generated.js +1 -1
  87. package/dist/ui/tailwind.generated.js.map +1 -1
  88. package/package.json +2 -1
  89. package/src/a2ui/cards.ts +177 -0
  90. package/src/a2ui/emit.ts +147 -0
  91. package/src/a2ui/form.ts +117 -0
  92. package/src/a2ui/host.ts +73 -0
  93. package/src/a2ui/index.ts +22 -0
  94. package/src/a2ui/stepper.ts +211 -0
  95. package/src/a2ui/theme.ts +134 -0
  96. package/src/a2ui/to-v09.ts +118 -0
  97. package/src/engine.ts +701 -0
  98. package/src/handoff/a2a.ts +326 -0
  99. package/src/handoff/controller.ts +661 -0
  100. package/src/handoff/envelope.ts +216 -0
  101. package/src/handoff/index.ts +53 -0
  102. package/src/handoff/tools.ts +178 -0
  103. package/src/harness/harness.ts +981 -0
  104. package/src/harness/plan.ts +91 -0
  105. package/src/harness/policy.ts +59 -0
  106. package/src/harness/renderer.ts +208 -0
  107. package/src/harness/router/coordinator.ts +241 -0
  108. package/src/harness/router/rules.ts +65 -0
  109. package/src/harness/slots.ts +117 -0
  110. package/src/harness/steps.ts +156 -0
  111. package/src/harness/tools/action-tools.ts +870 -0
  112. package/src/harness/tools/bridge.ts +393 -0
  113. package/src/harness/tools/client.ts +26 -0
  114. package/src/harness/tools/discovery.ts +73 -0
  115. package/src/harness/tools/local-context.ts +480 -0
  116. package/src/harness/tools/open-page.ts +273 -0
  117. package/src/harness/tools/pending-action.ts +232 -0
  118. package/src/harness/tools/resume.ts +134 -0
  119. package/src/harness/tools/retry.ts +117 -0
  120. package/src/harness/tools/schema.ts +245 -0
  121. package/src/harness/tools/site-actions.ts +356 -0
  122. package/src/harness/tools/site-nav.ts +432 -0
  123. package/src/harness/tools/site-pages.ts +288 -0
  124. package/src/harness/tools/source.ts +353 -0
  125. package/src/harness/tools/webmcp.ts +200 -0
  126. package/src/index.ts +196 -0
  127. package/src/providers/capability.ts +167 -0
  128. package/src/providers/proxy.ts +191 -0
  129. package/src/providers/types.ts +150 -0
  130. package/src/types.ts +258 -0
  131. package/src/ui/agent-fullscreen.ts +4262 -0
  132. package/src/ui/agent.tw.css +49 -0
  133. package/src/ui/config.ts +138 -0
  134. package/src/ui/index.ts +32 -0
  135. package/src/ui/journey.ts +57 -0
  136. package/src/ui/tailwind.generated.ts +2 -0
  137. package/src/webmcp-contract.ts +65 -0
@@ -0,0 +1,191 @@
1
+ /**
2
+ * A {@link ModelProvider} that forwards completions to an endpoint you host.
3
+ *
4
+ * The browser never holds a vendor credential: the page posts to your own
5
+ * origin, and your endpoint calls the model with a key only it can see. This is
6
+ * the client half of that arrangement. The server half is yours to write, and
7
+ * `docs/remote-providers.md` documents the contract it has to satisfy.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { createProxyProvider } from '@nxisai/agent-harness';
12
+ *
13
+ * const agent = document.querySelector('nxis-agent-fullscreen');
14
+ * agent.modelProvider = createProxyProvider({ endpoint: '/api/agent' });
15
+ * ```
16
+ *
17
+ * @module
18
+ */
19
+ import { toolSchema } from '../harness/tools/schema.js';
20
+ import type { ModelContextToolInfo, ParsedToolCall } from '../types.js';
21
+ import type { Completion, ModelProvider } from './types.js';
22
+
23
+ /** Options for {@link createProxyProvider}. */
24
+ export interface ProxyProviderOptions {
25
+ /**
26
+ * Where completions are requested from.
27
+ *
28
+ * A same-origin path such as `/api/agent` keeps the request under the page's
29
+ * own origin, which is what allows `credentials: 'same-origin'` to carry an
30
+ * existing session and lets the endpoint answer "who is allowed to ask".
31
+ */
32
+ endpoint: string;
33
+ /**
34
+ * Identifies the provider in logs and debug output, not to end users.
35
+ * Defaults to `'proxy'`.
36
+ */
37
+ name?: string;
38
+ /**
39
+ * The model's name as a visitor should read it — `'Claude'`. Shown beside
40
+ * the input. Worth setting: without it the visitor sees {@link name}.
41
+ */
42
+ label?: string;
43
+ /** Extra request headers, for an endpoint that expects its own auth. */
44
+ headers?: Record<string, string>;
45
+ /**
46
+ * `fetch` implementation to use. Defaults to the global.
47
+ *
48
+ * Present so the provider can be tested without a network, and so a host can
49
+ * wrap requests with its own retry or instrumentation.
50
+ */
51
+ fetchImpl?: typeof fetch;
52
+ }
53
+
54
+ /**
55
+ * What an endpoint may return.
56
+ *
57
+ * `calls` is not part of the contract, but mapping a vendor response tends to
58
+ * produce an array (Anthropic returns content blocks, and filtering them for
59
+ * tool uses yields one), so it is accepted and reduced rather than ignored.
60
+ */
61
+ interface ProxyResponse {
62
+ text?: unknown;
63
+ call?: ParsedToolCall;
64
+ calls?: ParsedToolCall[];
65
+ usage?: { promptTokens?: number; outputTokens?: number };
66
+ model?: unknown;
67
+ }
68
+
69
+ /**
70
+ * Reduce the page's tools to plain, serialisable data.
71
+ *
72
+ * `discoverTools` returns whatever the WebMCP implementation holds, and a
73
+ * native registry returns live objects that reference the page: passing those
74
+ * to `JSON.stringify` throws "Converting circular structure to JSON, starting
75
+ * at object with constructor 'Window'". The local provider never hit this
76
+ * because it reads the same three fields out before building its prompt.
77
+ *
78
+ * Normalising the schema here also spares every endpoint the job: WebMCP
79
+ * carries it under four different keys and sometimes as a JSON string, and
80
+ * `inputSchema` is the one the contract documents.
81
+ */
82
+ function wireTools(tools: ModelContextToolInfo[]) {
83
+ return tools.map((tool) => ({
84
+ name: tool.name,
85
+ description: tool.description ?? '',
86
+ inputSchema: toolSchema(tool),
87
+ }));
88
+ }
89
+
90
+ /**
91
+ * Reduce a response to the single call the harness can act on.
92
+ *
93
+ * The harness runs one tool per turn. An endpoint that returns several would
94
+ * otherwise have all but the first silently dropped, so the reduction happens
95
+ * here, once, with a warning naming what was discarded.
96
+ */
97
+ function oneCall(body: ProxyResponse, name: string): ParsedToolCall | undefined {
98
+ const many = Array.isArray(body.calls) ? body.calls : [];
99
+ const all = body.call ? [body.call, ...many] : many;
100
+ if (all.length > 1) {
101
+ console.warn(
102
+ `[${name}] endpoint returned ${all.length} tool calls; using "${all[0]?.name}" and ` +
103
+ `discarding ${all.slice(1).map((c) => c?.name).join(', ')}. ` +
104
+ 'The harness runs one tool per turn.',
105
+ );
106
+ }
107
+ return all[0];
108
+ }
109
+
110
+ /**
111
+ * Build a provider that posts completions to your own endpoint.
112
+ *
113
+ * The returned provider deliberately has no `load()`. Its absence is what tells
114
+ * the UI there is nothing to download, so the progress ring, byte counts and
115
+ * stage labels are skipped, and it is why a remote provider works on devices
116
+ * that cannot hold a local model. `countTokens` is likewise absent: token
117
+ * metrics hide themselves rather than reporting zeroes.
118
+ *
119
+ * @param opts - The endpoint, and optionally a name, extra headers, or a
120
+ * `fetch` implementation.
121
+ * @returns A provider ready to pass as `modelProvider` or {@link HarnessOptions.provider}.
122
+ * @throws From `complete()`, if the endpoint answers with a non-2xx status. An
123
+ * aborted request rejects with the `AbortError` from `fetch`, which the harness
124
+ * treats as a cancellation rather than a failure.
125
+ */
126
+ export function createProxyProvider(opts: ProxyProviderOptions): ModelProvider {
127
+ const { endpoint, name = 'proxy', label, headers, fetchImpl } = opts;
128
+ /** The model the endpoint last said it used. Wins over the configured label. */
129
+ let served: string | undefined;
130
+
131
+ return {
132
+ name,
133
+ /**
134
+ * What the visitor sees beside the input: the model the endpoint last
135
+ * reported, or the label it was configured with until one has answered.
136
+ */
137
+ get label() {
138
+ return served ?? label;
139
+ },
140
+ /** Inference happens off-device, which the UI surfaces to the visitor. */
141
+ local: false,
142
+ ready: true,
143
+ loading: false,
144
+
145
+ async complete({ messages, tools, maxTokens, signal, cache }) {
146
+ const send = fetchImpl ?? fetch;
147
+ const res = await send(endpoint, {
148
+ method: 'POST',
149
+ headers: {
150
+ 'Content-Type': 'application/json',
151
+ // Declares which response shape this provider understands. A future
152
+ // streaming provider asks for text/event-stream instead, so an
153
+ // endpoint can offer both and this one keeps working unchanged.
154
+ Accept: 'application/json',
155
+ ...headers,
156
+ },
157
+ credentials: 'same-origin',
158
+ // Without this the stop control, Escape and edit-and-resend are all
159
+ // inert: they abort the turn through this signal and nothing else.
160
+ signal,
161
+ body: JSON.stringify({ messages, tools: wireTools(tools), maxTokens, cache }),
162
+ });
163
+
164
+ if (!res.ok) {
165
+ throw new Error(`Agent endpoint ${endpoint} returned ${res.status} ${res.statusText}`);
166
+ }
167
+
168
+ const body = (await res.json()) as ProxyResponse;
169
+ const completion: Completion = {
170
+ // Rendered verbatim, so an endpoint that strips its model's protocol
171
+ // markers is the only thing standing between them and the visitor.
172
+ text: typeof body.text === 'string' ? body.text : '',
173
+ };
174
+ const call = oneCall(body, name);
175
+ if (call) completion.call = call;
176
+ if (body.usage) {
177
+ completion.usage = {
178
+ promptTokens: body.usage.promptTokens ?? 0,
179
+ outputTokens: body.usage.outputTokens ?? 0,
180
+ };
181
+ }
182
+ // Which model answered, when the endpoint says. An endpoint may route
183
+ // between models, so this is the only place the true name comes from.
184
+ if (typeof body.model === 'string' && body.model.trim()) {
185
+ served = body.model.trim();
186
+ completion.model = served;
187
+ }
188
+ return completion;
189
+ },
190
+ };
191
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * The model provider interface.
3
+ *
4
+ * A provider supplies one completion at a time. Everything else the harness
5
+ * does (discovering tools, reading their schemas, deciding whether to ask,
6
+ * confirm or run) is the same whichever model answers.
7
+ *
8
+ * Completions are structured rather than plain text: a provider reports a tool
9
+ * call as {@link Completion.call}, whether its model expresses that as a native
10
+ * tool-use block or as markers in the text it generates.
11
+ *
12
+ * @see The bundled {@link LocalGemmaProvider}, and `docs/remote-providers.md`
13
+ * for a worked remote implementation.
14
+ *
15
+ * @module
16
+ */
17
+ import type {
18
+ ChatMessage,
19
+ GenerationUsage,
20
+ ModelContextToolInfo,
21
+ ParsedToolCall,
22
+ PreloadMode,
23
+ } from '../types.js';
24
+
25
+ /**
26
+ * What the harness knows about which parts of a request will repeat.
27
+ *
28
+ * Advisory. The harness caches nothing itself; acting on the hint is a
29
+ * provider's decision, and only worthwhile where the backend supports prompt
30
+ * caching. Both fields go false when a handoff between agents changes the
31
+ * system prompt and the tool set.
32
+ */
33
+ export interface CacheHint {
34
+ /** How many leading messages will be identical on the next request. */
35
+ stableMessages: number;
36
+ /** Whether the tool definitions are unchanged since the previous request. */
37
+ toolsStable: boolean;
38
+ }
39
+
40
+ /** One request for a completion. */
41
+ export interface CompletionRequest {
42
+ /** The conversation so far, including the system prompt. */
43
+ messages: ChatMessage[];
44
+ /**
45
+ * The tools available for this turn, as raw WebMCP entries.
46
+ *
47
+ * Providers translate these into whatever their model expects.
48
+ */
49
+ tools: ModelContextToolInfo[];
50
+ /** Ceiling on generated tokens. Providers may apply their own default. */
51
+ maxTokens?: number;
52
+ /**
53
+ * Aborts when the user stops the turn, closes the panel, or starts another.
54
+ *
55
+ * Honour it: pass it to `fetch`, or interrupt local generation. Rejecting
56
+ * with an `AbortError` is correct, and the harness treats it as a
57
+ * cancellation rather than a failure.
58
+ */
59
+ signal?: AbortSignal;
60
+ /**
61
+ * Called as text arrives, with the whole answer so far rather than the newest
62
+ * fragment. Optional: a provider that does not stream simply omits it.
63
+ */
64
+ onToken?(textSoFar: string): void;
65
+ /** What the harness knows about reuse. Advisory; see {@link CacheHint}. */
66
+ cache?: CacheHint;
67
+ }
68
+
69
+ /** What a provider returns for one request. */
70
+ export interface Completion {
71
+ /**
72
+ * Text to show the user, rendered verbatim. Strip any protocol markers in the
73
+ * provider: the harness does no parsing of its own.
74
+ */
75
+ text: string;
76
+ /**
77
+ * The tool the model asked for, if any. At most one per completion; a model
78
+ * that returns several calls should report the first and drop the rest.
79
+ */
80
+ call?: ParsedToolCall;
81
+ /** Token counts, when the provider can report them. */
82
+ usage?: GenerationUsage;
83
+ /**
84
+ * The model that produced this completion, as a visitor should read it.
85
+ *
86
+ * For a provider that cannot know in advance — an endpoint that routes
87
+ * between models, or falls back to a cheaper one under load — this is how
88
+ * the name shown beside the input stays true.
89
+ */
90
+ model?: string;
91
+ }
92
+
93
+ /**
94
+ * A source of model completions.
95
+ *
96
+ * Implement `complete` and the four readonly fields; everything else is
97
+ * optional. See `docs/remote-providers.md` for a worked example.
98
+ */
99
+ export interface ModelProvider {
100
+ /** Identifies the provider in logs and debug output, not to end users. */
101
+ readonly name: string;
102
+ /**
103
+ * The model's name as a visitor should read it — `'Gemma 4 E2B'`.
104
+ *
105
+ * Shown beside the input, so someone can see what is answering them.
106
+ * Falls back to {@link name} when absent.
107
+ */
108
+ readonly label?: string;
109
+ /**
110
+ * Whether inference happens on the user's device.
111
+ *
112
+ * Surfaced in the UI, so that a page sending conversations to a remote
113
+ * service says so.
114
+ */
115
+ readonly local: boolean;
116
+
117
+ /** Whether the provider can answer a request right now. */
118
+ readonly ready: boolean;
119
+ /** Whether a load is in flight. Always false when there is nothing to load. */
120
+ readonly loading: boolean;
121
+
122
+ /**
123
+ * Prepare the model, resolving once it is ready.
124
+ *
125
+ * Omit it entirely when there is nothing to prepare: its absence is what
126
+ * tells the UI to skip the download progress treatment.
127
+ */
128
+ load?(): Promise<void>;
129
+ /**
130
+ * Begin loading ahead of the first turn.
131
+ *
132
+ * @param mode - How eagerly to load, from the element's `preload` attribute.
133
+ */
134
+ preload?(mode: Exclude<PreloadMode, 'none'>): void;
135
+
136
+ /**
137
+ * Produce one completion.
138
+ *
139
+ * @param req - The conversation, the available tools, and the abort signal.
140
+ * @returns The generated text and the tool call it implies, if any.
141
+ */
142
+ complete(req: CompletionRequest): Promise<Completion>;
143
+ /**
144
+ * Count tokens in a string.
145
+ *
146
+ * Optional. A provider that cannot count causes token metrics to be hidden
147
+ * rather than estimated.
148
+ */
149
+ countTokens?(text: string): Promise<number>;
150
+ }
package/src/types.ts ADDED
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Shared types: the WebMCP surface (`document.modelContext`), MCP discovery
3
+ * (catalogs and server cards), and the model and tool-call shapes.
4
+ *
5
+ * @module
6
+ */
7
+
8
+ // ── WebMCP (W3C draft ModelContext API) ──────────────────────────────────────
9
+
10
+ /** A JSON Schema fragment, as carried by a tool's input schema. */
11
+ export interface JsonSchema {
12
+ type?: string;
13
+ properties?: Record<string, JsonSchema>;
14
+ required?: string[];
15
+ enum?: unknown[];
16
+ items?: JsonSchema;
17
+ format?: string;
18
+ description?: string;
19
+ default?: unknown;
20
+ [key: string]: unknown;
21
+ }
22
+
23
+ /** Behavioural hints a tool declares about itself. */
24
+ export interface ModelContextToolAnnotations {
25
+ /** True when calling the tool has no side effects. Drives {@link policyFor}. */
26
+ readOnlyHint?: boolean;
27
+ [key: string]: unknown;
28
+ }
29
+
30
+ /** One block of a tool response. */
31
+ export interface ModelContextContent {
32
+ /** The block's kind, commonly `'text'`. */
33
+ type: string;
34
+ /** The block's text, when it has one. */
35
+ text?: string;
36
+ [key: string]: unknown;
37
+ }
38
+
39
+ /** What a tool returns. */
40
+ export interface ModelContextToolResponse {
41
+ /** The response blocks, in order. */
42
+ content: ModelContextContent[];
43
+ /** True when the tool reports a failure. */
44
+ isError?: boolean;
45
+ /** A parsed form of the response, preferred over the text when present. */
46
+ structuredContent?: Record<string, unknown>;
47
+ }
48
+
49
+ /**
50
+ * A tool as reported by `getTools()`.
51
+ *
52
+ * Implementations differ over where the schema lives and whether it arrives
53
+ * parsed or as a JSON string, so several keys are accepted. Use
54
+ * {@link toolSchema} to read it rather than these fields directly.
55
+ */
56
+ export interface ModelContextToolInfo {
57
+ /** The tool's unique name, as used to call it. */
58
+ name: string;
59
+ /** What the tool does, shown to the model. */
60
+ description?: string;
61
+ /** The input schema. See also the aliases below. */
62
+ inputSchema?: JsonSchema | string;
63
+ /** Alias for {@link ModelContextToolInfo.inputSchema}. */
64
+ input_schema?: JsonSchema | string;
65
+ /** Alias for {@link ModelContextToolInfo.inputSchema}. */
66
+ parameters?: JsonSchema | string;
67
+ /** Alias for {@link ModelContextToolInfo.inputSchema}. */
68
+ schema?: JsonSchema | string;
69
+ /** Behavioural hints, notably `readOnlyHint`. */
70
+ annotations?: ModelContextToolAnnotations;
71
+ /** Where the tool came from, when the registry records it. */
72
+ origin?: string;
73
+ }
74
+
75
+ /** A tool as passed to `registerTool()`. */
76
+ export interface ModelContextTool {
77
+ /** The tool's unique name. */
78
+ name: string;
79
+ /** A human-readable name. `getTools` reports `""` when absent. */
80
+ title?: string;
81
+ /** What the tool does, shown to the model. */
82
+ description?: string;
83
+ /** The parsed input schema. */
84
+ inputSchema?: JsonSchema;
85
+ /** Behavioural hints, notably `readOnlyHint`. */
86
+ annotations?: ModelContextToolAnnotations;
87
+ /** Called to run the tool. */
88
+ execute(
89
+ input: Record<string, unknown>,
90
+ context?: unknown,
91
+ ): ModelContextToolResponse | Promise<ModelContextToolResponse>;
92
+ }
93
+
94
+ /**
95
+ * The page's model context, per the W3C draft ModelContext API.
96
+ *
97
+ * Emits `toolchange` when the set of registered tools changes.
98
+ */
99
+ export interface ModelContext extends EventTarget {
100
+ /**
101
+ * Add a tool to the page's registry.
102
+ *
103
+ * Native implementations may return a promise, which rejects on a duplicate
104
+ * name; the polyfill returns nothing and replaces the existing tool.
105
+ */
106
+ registerTool(tool: ModelContextTool, options?: unknown): void | Promise<void>;
107
+ /** Supply page context to the registry, where the implementation supports it. */
108
+ provideContext?(context?: unknown): void;
109
+ /** Call a tool, by registered object or by name. */
110
+ executeTool(
111
+ nameOrTool: string | object,
112
+ input?: string | Record<string, unknown> | null,
113
+ options?: unknown,
114
+ ): Promise<ModelContextToolResponse>;
115
+ /** Every tool currently registered on the page. */
116
+ getTools(options?: unknown): Promise<ModelContextToolInfo[]>;
117
+ }
118
+
119
+ // ── MCP discovery (.well-known catalogs, then a server card) ─────────────────
120
+
121
+ /** One entry in a discovery catalog. */
122
+ export interface CatalogEntry {
123
+ identifier?: string;
124
+ displayName?: string;
125
+ mediaType?: string;
126
+ type?: string;
127
+ url?: string;
128
+ }
129
+
130
+ /** A `.well-known` discovery catalog. */
131
+ export interface CatalogFile {
132
+ /** The catalog spec version the file claims. */
133
+ specVersion?: string;
134
+ /** The catalog's entries. */
135
+ entries?: CatalogEntry[];
136
+ }
137
+
138
+ /** A header a remote expects on each request, such as an API key. */
139
+ export interface ServerCardRemoteHeader {
140
+ name: string;
141
+ description?: string;
142
+ isSecret?: boolean;
143
+ isRequired?: boolean;
144
+ format?: string;
145
+ }
146
+
147
+ /** One way of reaching an MCP server. */
148
+ export interface ServerCardRemote {
149
+ type?: string;
150
+ url?: string;
151
+ headers?: ServerCardRemoteHeader[];
152
+ supportedProtocolVersions?: string[];
153
+ }
154
+
155
+ /** An MCP server's published description. */
156
+ export interface ServerCard {
157
+ name?: string;
158
+ title?: string;
159
+ description?: string;
160
+ websiteUrl?: string;
161
+ remotes?: ServerCardRemote[];
162
+ _meta?: Record<string, unknown>;
163
+ }
164
+
165
+ /** The connection details an MCP client needs, distilled from a server card. */
166
+ export interface McpServerInfo {
167
+ /** The server's Streamable HTTP endpoint. */
168
+ endpoint: string;
169
+ /** Header carrying the API key, when one is required. */
170
+ authHeaderName?: string;
171
+ /** Whether calls need an API key. */
172
+ authRequired: boolean;
173
+ /** Tools callable without authentication. */
174
+ publicTools: string[];
175
+ /** Protocol versions the server accepts. */
176
+ protocolVersions: string[];
177
+ /** Where the card was fetched from. */
178
+ cardUrl: string;
179
+ /** The server's display title. */
180
+ title?: string;
181
+ }
182
+
183
+ // ── Page config ──────────────────────────────────────────────────────────────
184
+
185
+ /** The `window.__NXIS_CONFIG__` global, when the host page defines one. */
186
+ export interface NxisConfig {
187
+ /** Organisation identifier. */
188
+ org_id?: string;
189
+ /** Site identifier. */
190
+ site_id?: string;
191
+ /** Subscription identifier. */
192
+ sub_id?: string;
193
+ /** WebMCP settings for the page. */
194
+ webmcp?: { mode?: string };
195
+ }
196
+
197
+ declare global {
198
+ interface Document {
199
+ modelContext?: ModelContext;
200
+ }
201
+ interface Navigator {
202
+ modelContext?: ModelContext;
203
+ }
204
+ interface Window {
205
+ __NXIS_CONFIG__?: NxisConfig;
206
+ }
207
+ }
208
+
209
+ // ── Model and tool-call shapes ───────────────────────────────────────────────
210
+
211
+
212
+ /** One message in a conversation. */
213
+ export interface ChatMessage {
214
+ /** Who produced the message. */
215
+ role: 'system' | 'user' | 'assistant';
216
+ /** The message text. */
217
+ content: string;
218
+ }
219
+
220
+ /** A tool definition in the form a chat template expects. */
221
+ export interface ToolDef {
222
+ /** Always `'function'`. */
223
+ type: 'function';
224
+ /** The callable's name, description and parameter schema. */
225
+ function: { name: string; description?: string; parameters: JsonSchema };
226
+ }
227
+
228
+ /** A tool call the model asked for. */
229
+ export interface ParsedToolCall {
230
+ /** The tool's name. */
231
+ name: string;
232
+ /** The arguments, as supplied by the model. */
233
+ args: Record<string, unknown>;
234
+ }
235
+
236
+ /** Token counts for one generation. */
237
+ export interface GenerationUsage {
238
+ /** Tokens in the prompt, including tools and history. */
239
+ promptTokens: number;
240
+ /** Tokens generated. */
241
+ outputTokens: number;
242
+ }
243
+
244
+ /**
245
+ * Byte counts behind a load percentage.
246
+ *
247
+ * `total` covers only the files announced so far and grows as further shards
248
+ * register, which is why progress is reported per stage and left unclamped.
249
+ */
250
+ export interface ProgressDetail {
251
+ /** Bytes received so far in this stage. */
252
+ loaded: number;
253
+ /** Bytes expected, across the files announced so far. */
254
+ total: number;
255
+ }
256
+
257
+ /** How eagerly to fetch model weights. */
258
+ export type PreloadMode = 'eager' | 'idle' | 'none';