@revealui/ai 0.2.9 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +200 -26
- package/dist/a2a/handler.d.ts +3 -1
- package/dist/a2a/handler.d.ts.map +1 -1
- package/dist/a2a/handler.js +24 -7
- package/dist/a2a/task-store.d.ts.map +1 -1
- package/dist/a2a/task-store.js +5 -1
- package/dist/client/hooks/useAgentStream.d.ts +84 -1
- package/dist/client/hooks/useAgentStream.d.ts.map +1 -1
- package/dist/client/hooks/useAgentStream.js +106 -10
- package/dist/index.d.ts +4 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -15
- package/dist/ingestion/admin-indexer.js +1 -1
- package/dist/ingestion/pipeline.d.ts +1 -1
- package/dist/ingestion/pipeline.d.ts.map +1 -1
- package/dist/llm/client.d.ts +33 -10
- package/dist/llm/client.d.ts.map +1 -1
- package/dist/llm/client.js +70 -57
- package/dist/llm/key-validator.d.ts.map +1 -1
- package/dist/llm/key-validator.js +0 -27
- package/dist/llm/providers/inference-snaps.d.ts +7 -5
- package/dist/llm/providers/inference-snaps.d.ts.map +1 -1
- package/dist/llm/providers/inference-snaps.js +7 -5
- package/dist/llm/providers/openai-compat.d.ts +1 -1
- package/dist/llm/providers/openai-compat.js +1 -1
- package/dist/llm/response-cache.d.ts +1 -2
- package/dist/llm/response-cache.d.ts.map +1 -1
- package/dist/llm/response-cache.js +1 -2
- package/dist/llm/server.d.ts +1 -1
- package/dist/llm/server.d.ts.map +1 -1
- package/dist/llm/server.js +3 -1
- package/dist/memory/crdt/lww-register.js +1 -1
- package/dist/memory/preferences/user-preferences-manager.d.ts.map +1 -1
- package/dist/memory/preferences/user-preferences-manager.js +1 -1
- package/dist/memory/services/index.d.ts +2 -0
- package/dist/memory/services/index.d.ts.map +1 -1
- package/dist/memory/services/index.js +1 -0
- package/dist/memory/services/reconciliation-service.d.ts +64 -0
- package/dist/memory/services/reconciliation-service.d.ts.map +1 -0
- package/dist/memory/services/reconciliation-service.js +127 -0
- package/dist/memory/stores/working-memory.d.ts.map +1 -1
- package/dist/memory/stores/working-memory.js +1 -1
- package/dist/memory/utils/index.d.ts +1 -2
- package/dist/memory/utils/index.d.ts.map +1 -1
- package/dist/memory/utils/index.js +1 -2
- package/dist/memory/utils/sql-helpers.d.ts +21 -66
- package/dist/memory/utils/sql-helpers.d.ts.map +1 -1
- package/dist/memory/utils/sql-helpers.js +34 -169
- package/dist/memory/vector/vector-memory-service.d.ts.map +1 -1
- package/dist/memory/vector/vector-memory-service.js +6 -1
- package/dist/orchestration/runtime.d.ts +30 -1
- package/dist/orchestration/runtime.d.ts.map +1 -1
- package/dist/orchestration/runtime.js +20 -3
- package/dist/orchestration/streaming-runtime.d.ts +48 -1
- package/dist/orchestration/streaming-runtime.d.ts.map +1 -1
- package/dist/orchestration/ticket-agent.d.ts +10 -1
- package/dist/orchestration/ticket-agent.d.ts.map +1 -1
- package/dist/orchestration/ticket-agent.js +11 -2
- package/dist/skills/catalog/vercel-catalog.d.ts.map +1 -1
- package/dist/skills/catalog/vercel-catalog.js +13 -7
- package/dist/tools/mcp-adapter.d.ts +192 -1
- package/dist/tools/mcp-adapter.d.ts.map +1 -1
- package/dist/tools/mcp-adapter.js +434 -1
- package/dist/tools/mcp-elicitation.d.ts +128 -0
- package/dist/tools/mcp-elicitation.d.ts.map +1 -0
- package/dist/tools/mcp-elicitation.js +121 -0
- package/dist/tools/mcp-events.d.ts +270 -0
- package/dist/tools/mcp-events.d.ts.map +1 -0
- package/dist/tools/mcp-events.js +165 -0
- package/dist/tools/mcp-sampling.d.ts +155 -0
- package/dist/tools/mcp-sampling.d.ts.map +1 -0
- package/dist/tools/mcp-sampling.js +171 -0
- package/dist/tools/ticket-tools.d.ts +35 -2
- package/dist/tools/ticket-tools.d.ts.map +1 -1
- package/dist/tools/ticket-tools.js +32 -2
- package/package.json +38 -12
- package/dist/llm/providers/vultr.d.ts +0 -18
- package/dist/llm/providers/vultr.d.ts.map +0 -1
- package/dist/llm/providers/vultr.js +0 -169
- package/dist/memory/utils/deep-clone.d.ts +0 -10
- package/dist/memory/utils/deep-clone.d.ts.map +0 -1
- package/dist/memory/utils/deep-clone.js +0 -9
package/README.md
CHANGED
|
@@ -8,13 +8,211 @@ AI system for RevealUI - memory, LLM, orchestration, and tools.
|
|
|
8
8
|
## Features
|
|
9
9
|
|
|
10
10
|
- **Memory System**: CRDT-based persistent memory (Working, Episodic, Semantic)
|
|
11
|
-
- **LLM Integration**: Provider abstractions for Anthropic, GROQ, Ollama, and more
|
|
11
|
+
- **LLM Integration**: Provider abstractions for Anthropic, GROQ, Ollama, Canonical Inference Snaps, and more
|
|
12
12
|
- **Agent Orchestration**: Runtime and execution engine for AI agents
|
|
13
|
-
- **Tool Calling**: Tool registry
|
|
13
|
+
- **Tool Calling**: Tool registry + standard-MCP-client integration (Stage 5.1a)
|
|
14
14
|
- **Vector Search**: Semantic search with pgvector
|
|
15
15
|
- **Type-safe**: Full TypeScript support
|
|
16
16
|
- **Performant**: Optimized for low-latency operations
|
|
17
17
|
|
|
18
|
+
## Reference stack (Ubuntu)
|
|
19
|
+
|
|
20
|
+
The recommended on-prem / on-device stack pairs `@revealui/ai` with a
|
|
21
|
+
**Canonical Inference Snap** for local LLM inference:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
@revealui/ai (this package)
|
|
25
|
+
│
|
|
26
|
+
├── agent runtime + tool-calling loop
|
|
27
|
+
│
|
|
28
|
+
├── MCP client → @revealui/mcp/client → tools/resources/prompts from MCP servers
|
|
29
|
+
│
|
|
30
|
+
└── LLM provider → OpenAI-compatible HTTP → localhost:<port>/v1
|
|
31
|
+
▲
|
|
32
|
+
│
|
|
33
|
+
Canonical Inference Snap
|
|
34
|
+
(DeepSeek R1, Qwen 2.5 VL,
|
|
35
|
+
Gemma 3, Nemotron Nano —
|
|
36
|
+
silicon-optimized on Intel/
|
|
37
|
+
Ampere/NVIDIA/NPU)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Quick setup:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
sudo snap install gemma3 # or deepseek-r1, qwen-vl, etc.
|
|
44
|
+
gemma3 set http.port=9090
|
|
45
|
+
gemma3 status # confirms base URL
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { InferenceSnapsProvider, LLMClient } from '@revealui/ai'
|
|
50
|
+
|
|
51
|
+
const provider = new InferenceSnapsProvider({
|
|
52
|
+
baseURL: 'http://localhost:9090/v1',
|
|
53
|
+
model: 'gemma3',
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const client = new LLMClient({ provider })
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Cloud providers (Anthropic, OpenAI, GROQ) remain supported; the local
|
|
60
|
+
inference path is the documented default for self-hosted deployments.
|
|
61
|
+
|
|
62
|
+
## MCP tool integration
|
|
63
|
+
|
|
64
|
+
Standard MCP servers plug into the agent runtime as tool sources. Construct
|
|
65
|
+
an `McpClient` (from `@revealui/mcp/client`), connect it, and pass it to
|
|
66
|
+
`AgentRuntime`:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { McpClient } from '@revealui/mcp/client'
|
|
70
|
+
import { AgentRuntime } from '@revealui/ai'
|
|
71
|
+
|
|
72
|
+
const contentClient = new McpClient({
|
|
73
|
+
clientInfo: { name: 'my-agent', version: '1.0.0' },
|
|
74
|
+
transport: { kind: 'streamable-http', url: 'https://admin.example.com/api/mcp/content' },
|
|
75
|
+
})
|
|
76
|
+
await contentClient.connect()
|
|
77
|
+
|
|
78
|
+
const runtime = new AgentRuntime({
|
|
79
|
+
mcpClients: [{ name: 'content', client: contentClient }],
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Tools from each client are namespaced as `mcp_<name>__<toolName>` so multiple
|
|
84
|
+
clients coexist without collisions.
|
|
85
|
+
|
|
86
|
+
### Resources + prompts (Stage 5.1b)
|
|
87
|
+
|
|
88
|
+
When an MCP client advertises resources and/or prompts, the adapter
|
|
89
|
+
additionally emits per-server meta-tools so the agent can read them on
|
|
90
|
+
demand without any bespoke wiring:
|
|
91
|
+
|
|
92
|
+
| Meta-tool | Calls | Returns |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `mcp_<ns>__list_resources` | `client.listResources()` | `[{ uri, name?, description?, mimeType? }, …]` |
|
|
95
|
+
| `mcp_<ns>__read_resource({ uri })` | `client.readResource(uri)` | resource contents; text parts flattened into the `content` field for token-efficient LLM context |
|
|
96
|
+
| `mcp_<ns>__list_prompts` | `client.listPrompts()` | `[{ name, description?, arguments? }, …]` |
|
|
97
|
+
| `mcp_<ns>__get_prompt({ name, args? })` | `client.getPrompt(name, args)` | `{ description?, messages }`; text messages flattened to `<role>: <text>` |
|
|
98
|
+
|
|
99
|
+
All four are opt-out via `include` on `createToolsFromMcpClient()`. Meta-tools
|
|
100
|
+
are silently skipped when the client doesn't implement the underlying
|
|
101
|
+
method (e.g. servers that don't advertise the resources capability).
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// Include every primitive (default)
|
|
105
|
+
const tools = await createToolsFromMcpClient(client, { namespace: 'content' })
|
|
106
|
+
|
|
107
|
+
// Tools only, skip the resources/prompts meta-tools
|
|
108
|
+
const toolsOnly = await createToolsFromMcpClient(client, {
|
|
109
|
+
namespace: 'content',
|
|
110
|
+
include: { tools: true, resources: false, prompts: false },
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Elicitation, progress, cancellation (Stage 5.3)
|
|
115
|
+
|
|
116
|
+
MCP servers can ask the user for structured input mid-flow
|
|
117
|
+
(`elicitation/create`), report progress on long-running tool calls, and
|
|
118
|
+
respect cancellation. `@revealui/ai` wires all three through consumer
|
|
119
|
+
callbacks — the agent package stays UI-agnostic:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { McpClient } from '@revealui/mcp/client'
|
|
123
|
+
import { createElicitationHandler, createToolsFromMcpClient } from '@revealui/ai'
|
|
124
|
+
|
|
125
|
+
const controller = new AbortController() // wire to a UI cancel button
|
|
126
|
+
|
|
127
|
+
const client = new McpClient({
|
|
128
|
+
clientInfo: { name: 'my-agent', version: '1.0.0' },
|
|
129
|
+
transport: { kind: 'streamable-http', url: '…' },
|
|
130
|
+
elicitationHandler: createElicitationHandler({
|
|
131
|
+
onElicit: async ({ message, requestedSchema }) => {
|
|
132
|
+
const form = await showFormDialog({ title: message, schema: requestedSchema })
|
|
133
|
+
if (!form) return { action: 'cancel' }
|
|
134
|
+
return { action: 'accept', content: form.values }
|
|
135
|
+
},
|
|
136
|
+
timeoutMs: 60_000, // auto-cancel if no response
|
|
137
|
+
// allowUrlMode: false // default — URL-mode auto-declines
|
|
138
|
+
}),
|
|
139
|
+
})
|
|
140
|
+
await client.connect()
|
|
141
|
+
|
|
142
|
+
const tools = await createToolsFromMcpClient(client, {
|
|
143
|
+
namespace: 'content',
|
|
144
|
+
signal: controller.signal, // cancel all MCP RPC when aborted
|
|
145
|
+
onProgress: (event) => { // per-tool-call progress events
|
|
146
|
+
progressBar.update(event.toolName, event.progress.progress, event.progress.total)
|
|
147
|
+
},
|
|
148
|
+
})
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Elicitation safety:**
|
|
152
|
+
- `mode: 'url'` (out-of-band consent) is auto-declined unless
|
|
153
|
+
`allowUrlMode: true`. URL-mode is a phishing vector when users can't
|
|
154
|
+
easily verify the target domain.
|
|
155
|
+
- `timeoutMs` auto-cancels (not declines) when no response arrives.
|
|
156
|
+
Useful in headless automation.
|
|
157
|
+
- Errors thrown inside `onElicit` map to `{ action: 'cancel' }` — the
|
|
158
|
+
server sees a clean non-response, not a crashed protocol.
|
|
159
|
+
|
|
160
|
+
**Progress events** are forwarded from the server's
|
|
161
|
+
`notifications/progress` stream with the namespace + tool name stamped
|
|
162
|
+
for multi-client attribution. Resource + prompt meta-tools also emit
|
|
163
|
+
(via their own `notifications/progress` flows).
|
|
164
|
+
|
|
165
|
+
**Cancellation** uses the MCP spec's `notifications/cancelled` — aborting
|
|
166
|
+
the `AbortSignal` propagates to in-flight RPC calls on the wire, so
|
|
167
|
+
long-running operations stop instead of just being ignored.
|
|
168
|
+
|
|
169
|
+
### Recursive sampling (Stage 5.2)
|
|
170
|
+
|
|
171
|
+
Some MCP servers need LLM capabilities without bundling a provider. The
|
|
172
|
+
spec lets servers issue `sampling/createMessage` requests — the *client*
|
|
173
|
+
runs the inference, keeps cost + context control, and returns the result.
|
|
174
|
+
On the Ubuntu reference stack, that means servers get LLM access via the
|
|
175
|
+
developer's local Canonical Inference Snap — no cloud round-trip required.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
import { McpClient } from '@revealui/mcp/client'
|
|
179
|
+
import { InferenceSnapsProvider, createSamplingHandler } from '@revealui/ai'
|
|
180
|
+
|
|
181
|
+
const llm = new InferenceSnapsProvider({
|
|
182
|
+
baseURL: 'http://localhost:9090/v1',
|
|
183
|
+
model: 'gemma3',
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
const client = new McpClient({
|
|
187
|
+
clientInfo: { name: 'my-agent', version: '1.0.0' },
|
|
188
|
+
transport: { kind: 'streamable-http', url: 'https://example.com/mcp' },
|
|
189
|
+
samplingHandler: createSamplingHandler({
|
|
190
|
+
llm,
|
|
191
|
+
defaultModel: 'gemma3',
|
|
192
|
+
allowedModels: ['gemma3', 'deepseek-r1'], // strongly recommended
|
|
193
|
+
onSamplingRequest: (info) => {
|
|
194
|
+
// metering / audit trail
|
|
195
|
+
metrics.incr('mcp.sampling', { model: info.model })
|
|
196
|
+
},
|
|
197
|
+
}),
|
|
198
|
+
})
|
|
199
|
+
await client.connect()
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`allowedModels` filters `modelPreferences.hints` from the server — hints
|
|
203
|
+
outside the list are ignored so a malicious server can't escalate costs.
|
|
204
|
+
The handler reports the resolved model back in `result.model`.
|
|
205
|
+
|
|
206
|
+
**Scope in 5.2:** text-only messages (non-text content throws with a clear
|
|
207
|
+
error). Multimodal sampling lands with the provider interface's content
|
|
208
|
+
parts extension. `stopSequences` aren't forwarded to the LLM yet (the
|
|
209
|
+
current `LLMChatOptions` doesn't expose that field); this is advisory per
|
|
210
|
+
spec, so omission is compliant.
|
|
211
|
+
|
|
212
|
+
The legacy `mcpToolSource` path (hypervisor-backed) still works and is
|
|
213
|
+
kept for backwards compatibility, but new integrations should prefer the
|
|
214
|
+
`mcpClients` path.
|
|
215
|
+
|
|
18
216
|
## Installation
|
|
19
217
|
|
|
20
218
|
```bash
|
|
@@ -118,30 +316,6 @@ const response = await client.chat([
|
|
|
118
316
|
])
|
|
119
317
|
```
|
|
120
318
|
|
|
121
|
-
#### Vultr (Vultr AI / Serverless Inference)
|
|
122
|
-
|
|
123
|
-
You can configure the client to use Vultr Serverless Inference by setting `LLM_PROVIDER=vultr` and providing the `VULTR_API_KEY` and optionally `VULTR_BASE_URL` env vars. Example:
|
|
124
|
-
|
|
125
|
-
```
|
|
126
|
-
LLM_PROVIDER=vultr
|
|
127
|
-
VULTR_API_KEY=your_inference_api_key
|
|
128
|
-
VULTR_BASE_URL=https://api.vultrinference.com/v1 # optional
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
The `VultrProvider` supports the chat and (if available for the selected model) embeddings endpoints. Streaming is attempted via the chat streaming endpoint when supported by the API.
|
|
132
|
-
|
|
133
|
-
Demo: quick test script
|
|
134
|
-
|
|
135
|
-
You can run a quick demo script that exercises chat and embeddings endpoints. From the repository root:
|
|
136
|
-
|
|
137
|
-
```bash
|
|
138
|
-
# replace with real values
|
|
139
|
-
VULTR_API_KEY=your_key VULTR_MODEL=your-model-id ts-node packages/ai/scripts/test-vultr.ts
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
The script prints the raw API responses and a simplified assistant output.
|
|
143
|
-
|
|
144
|
-
|
|
145
319
|
### Agent Orchestration
|
|
146
320
|
|
|
147
321
|
Agent runtime and execution engine for autonomous agents.
|
package/dist/a2a/handler.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ declare const RPC_INVALID_REQUEST = -32600;
|
|
|
21
21
|
* @param agentId - Optional agent ID (from X-Agent-ID header)
|
|
22
22
|
* @param llmClient - Optional LLM client for real inference (BYOK)
|
|
23
23
|
*/
|
|
24
|
-
export declare function handleA2AJsonRpc(req: A2AJsonRpcRequest, agentId?: string, llmClient?: LLMClient
|
|
24
|
+
export declare function handleA2AJsonRpc(req: A2AJsonRpcRequest, agentId?: string, llmClient?: LLMClient, options?: {
|
|
25
|
+
paymentVerified?: boolean;
|
|
26
|
+
}): Promise<A2AJsonRpcResponse>;
|
|
25
27
|
export { RPC_INVALID_REQUEST, RPC_PARSE_ERROR };
|
|
26
28
|
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/a2a/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAGnB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAgBlD,QAAA,MAAM,eAAe,SAAS,CAAC;AAC/B,QAAA,MAAM,mBAAmB,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/a2a/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAGnB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAgBlD,QAAA,MAAM,eAAe,SAAS,CAAC;AAC/B,QAAA,MAAM,mBAAmB,SAAS,CAAC;AA6KnC;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,iBAAiB,EACtB,OAAO,CAAC,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,SAAS,EACrB,OAAO,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,GACtC,OAAO,CAAC,kBAAkB,CAAC,CAoB7B;AAED,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,CAAC"}
|
package/dist/a2a/handler.js
CHANGED
|
@@ -36,7 +36,7 @@ function err(id, code, message, data) {
|
|
|
36
36
|
// =============================================================================
|
|
37
37
|
// Method handlers
|
|
38
38
|
// =============================================================================
|
|
39
|
-
async function handleTasksSend(id, params, agentId, llmClient) {
|
|
39
|
+
async function handleTasksSend(id, params, agentId, llmClient, options) {
|
|
40
40
|
const parsed = A2ASendTaskParamsSchema.safeParse(params);
|
|
41
41
|
if (!parsed.success) {
|
|
42
42
|
return err(id, RPC_INVALID_PARAMS, 'Invalid tasks/send params', parsed.error.issues);
|
|
@@ -46,6 +46,26 @@ async function handleTasksSend(id, params, agentId, llmClient) {
|
|
|
46
46
|
if (agentId && !agentCardRegistry.has(agentId)) {
|
|
47
47
|
return err(id, RPC_AGENT_NOT_FOUND, `Agent '${agentId}' not found`);
|
|
48
48
|
}
|
|
49
|
+
// Resolve agent definition early — needed for the pricing gate below AND
|
|
50
|
+
// for execution context (system prompt, capabilities) further down.
|
|
51
|
+
const agentDef = agentId
|
|
52
|
+
? agentCardRegistry.getDef(agentId)
|
|
53
|
+
: agentCardRegistry.getDef('revealui-creator');
|
|
54
|
+
// x402 payment gate: when an agent has pricing AND payment was not
|
|
55
|
+
// verified upstream by the route, emit a 'pending-payment' task. The
|
|
56
|
+
// route layer translates this state into HTTP 402 with a fresh
|
|
57
|
+
// X-PAYMENT-REQUIRED header. Pricing rides in task.metadata so the
|
|
58
|
+
// route can build the requirements without re-querying the registry.
|
|
59
|
+
if (agentDef?.pricing && !options?.paymentVerified) {
|
|
60
|
+
const task = createTask({
|
|
61
|
+
id: p.id,
|
|
62
|
+
sessionId: p.sessionId,
|
|
63
|
+
message: p.message,
|
|
64
|
+
metadata: { ...p.metadata, pricing: agentDef.pricing },
|
|
65
|
+
});
|
|
66
|
+
const pending = updateTaskState(task.id, 'pending-payment');
|
|
67
|
+
return ok(id, pending);
|
|
68
|
+
}
|
|
49
69
|
// Create task in submitted state
|
|
50
70
|
const task = createTask({
|
|
51
71
|
id: p.id,
|
|
@@ -59,9 +79,6 @@ async function handleTasksSend(id, params, agentId, llmClient) {
|
|
|
59
79
|
updateTaskState(task.id, 'working');
|
|
60
80
|
// Execute via orchestration - for now, produce a direct text response.
|
|
61
81
|
// Full AgentRuntime integration wires in when an LLM provider is configured.
|
|
62
|
-
const agentDef = agentId
|
|
63
|
-
? agentCardRegistry.getDef(agentId)
|
|
64
|
-
: agentCardRegistry.getDef('revealui-creator');
|
|
65
82
|
if (signal?.aborted) {
|
|
66
83
|
return ok(id, getTask(task.id));
|
|
67
84
|
}
|
|
@@ -151,18 +168,18 @@ function handleTasksCancel(id, params) {
|
|
|
151
168
|
* @param agentId - Optional agent ID (from X-Agent-ID header)
|
|
152
169
|
* @param llmClient - Optional LLM client for real inference (BYOK)
|
|
153
170
|
*/
|
|
154
|
-
export async function handleA2AJsonRpc(req, agentId, llmClient) {
|
|
171
|
+
export async function handleA2AJsonRpc(req, agentId, llmClient, options) {
|
|
155
172
|
const { id, method, params } = req;
|
|
156
173
|
switch (method) {
|
|
157
174
|
case 'tasks/send':
|
|
158
|
-
return handleTasksSend(id, params, agentId, llmClient);
|
|
175
|
+
return handleTasksSend(id, params, agentId, llmClient, options);
|
|
159
176
|
case 'tasks/get':
|
|
160
177
|
return handleTasksGet(id, params);
|
|
161
178
|
case 'tasks/cancel':
|
|
162
179
|
return handleTasksCancel(id, params);
|
|
163
180
|
case 'tasks/sendSubscribe':
|
|
164
181
|
// SSE streaming is handled at the Hono route level; return a reference task here
|
|
165
|
-
return handleTasksSend(id, params, agentId, llmClient);
|
|
182
|
+
return handleTasksSend(id, params, agentId, llmClient, options);
|
|
166
183
|
default:
|
|
167
184
|
return err(id, RPC_METHOD_NOT_FOUND, `Method '${method}' not found`);
|
|
168
185
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../../src/a2a/task-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAU1F;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,UAAU,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GAAG,OAAO,CAeV;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAElD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,GAAG,IAAI,CAehB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,GAAG,IAAI,CAUhF;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../../src/a2a/task-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAU1F;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,UAAU,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GAAG,OAAO,CAeV;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAElD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,GAAG,IAAI,CAehB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,GAAG,IAAI,CAUhF;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAiB9C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAE5D;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAG1C"}
|
package/dist/a2a/task-store.js
CHANGED
|
@@ -76,7 +76,11 @@ export function cancelTask(id) {
|
|
|
76
76
|
const task = _tasks.get(id);
|
|
77
77
|
if (!task)
|
|
78
78
|
return false;
|
|
79
|
-
|
|
79
|
+
// 'pending-payment' is cancelable so a requester who decides not to pay
|
|
80
|
+
// can release the task slot rather than leaving it dangling.
|
|
81
|
+
const cancelable = task.status.state === 'submitted' ||
|
|
82
|
+
task.status.state === 'working' ||
|
|
83
|
+
task.status.state === 'pending-payment';
|
|
80
84
|
if (!cancelable)
|
|
81
85
|
return false;
|
|
82
86
|
// Signal abort to any running execution
|
|
@@ -7,10 +7,20 @@
|
|
|
7
7
|
* - chunks: all received chunks in order
|
|
8
8
|
* - isStreaming: true while the stream is open
|
|
9
9
|
* - error: last error message, if any
|
|
10
|
+
* - sessionId: agent-run session id from the leading session_info chunk (A.2b)
|
|
11
|
+
* - pendingElicitations: outstanding form-mode elicitation requests by id (A.2b)
|
|
12
|
+
* - submitElicitation: POST a response to /api/agent-stream/elicit (A.2b)
|
|
10
13
|
* - abort(): cancels the stream
|
|
11
14
|
*/
|
|
15
|
+
/**
|
|
16
|
+
* Mirror of `AgentStreamChunk` in
|
|
17
|
+
* `@revealui/ai/orchestration/streaming-runtime`. Duplicated here (rather
|
|
18
|
+
* than imported) so client bundles don't pull in server-only orchestration
|
|
19
|
+
* code through the type graph. Keep these in lockstep — when a new chunk
|
|
20
|
+
* type lands in the runtime, mirror it here.
|
|
21
|
+
*/
|
|
12
22
|
export interface AgentStreamChunk {
|
|
13
|
-
type: 'text' | 'tool_call_start' | 'tool_call_result' | 'error' | 'done';
|
|
23
|
+
type: 'text' | 'tool_call_start' | 'tool_call_result' | 'error' | 'done' | 'session_info' | 'sampling_request' | 'elicitation_request';
|
|
14
24
|
content?: string;
|
|
15
25
|
toolCall?: {
|
|
16
26
|
name: string;
|
|
@@ -22,23 +32,96 @@ export interface AgentStreamChunk {
|
|
|
22
32
|
tokensUsed?: number;
|
|
23
33
|
executionTime?: number;
|
|
24
34
|
};
|
|
35
|
+
/** Present on session_info / sampling_request / elicitation_request. */
|
|
36
|
+
sessionId?: string;
|
|
37
|
+
/** MCP server id. Present on sampling_request / elicitation_request. */
|
|
38
|
+
namespace?: string;
|
|
39
|
+
/** Present on sampling_request — the request shape sent to the LLM. */
|
|
40
|
+
sampling?: {
|
|
41
|
+
model: string;
|
|
42
|
+
messageCount: number;
|
|
43
|
+
maxTokens: number;
|
|
44
|
+
};
|
|
45
|
+
/** Present on elicitation_request — the form payload for the UI. */
|
|
46
|
+
elicitation?: {
|
|
47
|
+
elicitationId: string;
|
|
48
|
+
requestedSchema: unknown;
|
|
49
|
+
message?: string;
|
|
50
|
+
};
|
|
25
51
|
}
|
|
26
52
|
export interface AgentStreamRequest {
|
|
27
53
|
instruction: string;
|
|
28
54
|
boardId?: string;
|
|
29
55
|
workspaceId?: string;
|
|
30
56
|
priority?: 'low' | 'medium' | 'high' | 'critical';
|
|
57
|
+
/**
|
|
58
|
+
* Agent mode. `'admin'` (default) gives the agent the admin-tools surface;
|
|
59
|
+
* `'coding'` adds file-read/write/grep + shell + git tools (Pro-tier;
|
|
60
|
+
* free-tier sees read-only subset). Mirrors `mode?` in the `/api/agent-stream`
|
|
61
|
+
* request schema.
|
|
62
|
+
*/
|
|
63
|
+
mode?: 'admin' | 'coding';
|
|
31
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Outstanding elicitation request awaiting a UI response. The hook keeps
|
|
67
|
+
* one entry per `elicitationId`; entries are removed when
|
|
68
|
+
* `submitElicitation()` lands successfully or when the stream ends.
|
|
69
|
+
*/
|
|
70
|
+
export interface PendingElicitation {
|
|
71
|
+
elicitationId: string;
|
|
72
|
+
namespace: string;
|
|
73
|
+
requestedSchema: unknown;
|
|
74
|
+
message?: string;
|
|
75
|
+
}
|
|
76
|
+
export type ElicitationAction = 'accept' | 'decline' | 'cancel';
|
|
77
|
+
/**
|
|
78
|
+
* Permitted primitive value types per the MCP spec (`ElicitResult.content`).
|
|
79
|
+
* The hook is a thin transport — the server-side zod schema at
|
|
80
|
+
* `apps/server/src/routes/agent-stream-elicit.ts` is the authoritative
|
|
81
|
+
* validator. We accept a wider input shape so callers can pass
|
|
82
|
+
* `Record<string, unknown>` from generic form serializers without
|
|
83
|
+
* up-front narrowing; non-primitive values get rejected by the server.
|
|
84
|
+
*/
|
|
85
|
+
export type ElicitationContentValue = string | number | boolean | string[];
|
|
86
|
+
export type ElicitationContent = Record<string, unknown>;
|
|
32
87
|
export interface UseAgentStreamState {
|
|
33
88
|
text: string;
|
|
34
89
|
chunks: AgentStreamChunk[];
|
|
35
90
|
isStreaming: boolean;
|
|
36
91
|
error: string | null;
|
|
92
|
+
/** Set when the leading `session_info` chunk arrives. Null otherwise. */
|
|
93
|
+
sessionId: string | null;
|
|
94
|
+
/** Map of pending elicitations keyed by elicitationId. */
|
|
95
|
+
pendingElicitations: PendingElicitation[];
|
|
37
96
|
}
|
|
38
97
|
export interface UseAgentStreamReturn extends UseAgentStreamState {
|
|
39
98
|
start: (request: AgentStreamRequest, apiBase?: string) => Promise<void>;
|
|
40
99
|
abort: () => void;
|
|
41
100
|
reset: () => void;
|
|
101
|
+
/**
|
|
102
|
+
* POST an elicitation response to `/api/agent-stream/elicit` and remove
|
|
103
|
+
* the entry from `pendingElicitations` on success. Throws if the network
|
|
104
|
+
* call fails or the server returns a non-2xx status.
|
|
105
|
+
*/
|
|
106
|
+
submitElicitation: (elicitationId: string, action: ElicitationAction, content?: ElicitationContent) => Promise<void>;
|
|
42
107
|
}
|
|
43
108
|
export declare function useAgentStream(): UseAgentStreamReturn;
|
|
109
|
+
/**
|
|
110
|
+
* Pure reducer applying one SSE chunk to the hook's state. Extracted for
|
|
111
|
+
* test-friendliness — call sites can simulate `start()` by feeding chunks
|
|
112
|
+
* to `applyChunk()` against `INITIAL_STATE`.
|
|
113
|
+
*
|
|
114
|
+
* @internal
|
|
115
|
+
*/
|
|
116
|
+
declare function applyChunk(s: UseAgentStreamState, chunk: AgentStreamChunk): UseAgentStreamState;
|
|
117
|
+
/**
|
|
118
|
+
* Pure reducer used internally to apply a chunk to state. Exported for
|
|
119
|
+
* unit tests so the chunk → state mapping can be verified without
|
|
120
|
+
* mounting a React component or stubbing `fetch`.
|
|
121
|
+
*
|
|
122
|
+
* @internal
|
|
123
|
+
*/
|
|
124
|
+
export declare const _applyChunkForTesting: typeof applyChunk;
|
|
125
|
+
export declare const _initialStateForTesting: UseAgentStreamState;
|
|
126
|
+
export {};
|
|
44
127
|
//# sourceMappingURL=useAgentStream.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAgentStream.d.ts","sourceRoot":"","sources":["../../../src/client/hooks/useAgentStream.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"useAgentStream.d.ts","sourceRoot":"","sources":["../../../src/client/hooks/useAgentStream.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* - chunks: all received chunks in order
|
|
8
8
|
* - isStreaming: true while the stream is open
|
|
9
9
|
* - error: last error message, if any
|
|
10
|
+
* - sessionId: agent-run session id from the leading session_info chunk (A.2b)
|
|
11
|
+
* - pendingElicitations: outstanding form-mode elicitation requests by id (A.2b)
|
|
12
|
+
* - submitElicitation: POST a response to /api/agent-stream/elicit (A.2b)
|
|
10
13
|
* - abort(): cancels the stream
|
|
11
14
|
*/
|
|
12
15
|
'use client';
|
|
@@ -16,6 +19,8 @@ const INITIAL_STATE = {
|
|
|
16
19
|
chunks: [],
|
|
17
20
|
isStreaming: false,
|
|
18
21
|
error: null,
|
|
22
|
+
sessionId: null,
|
|
23
|
+
pendingElicitations: [],
|
|
19
24
|
};
|
|
20
25
|
export function useAgentStream() {
|
|
21
26
|
const [state, setState] = useState(INITIAL_STATE);
|
|
@@ -32,12 +37,13 @@ export function useAgentStream() {
|
|
|
32
37
|
controllerRef.current?.abort();
|
|
33
38
|
const controller = new AbortController();
|
|
34
39
|
controllerRef.current = controller;
|
|
35
|
-
setState({
|
|
40
|
+
setState({ ...INITIAL_STATE, isStreaming: true });
|
|
36
41
|
try {
|
|
37
42
|
const response = await fetch(apiBase, {
|
|
38
43
|
method: 'POST',
|
|
39
44
|
headers: { 'Content-Type': 'application/json' },
|
|
40
45
|
body: JSON.stringify(request),
|
|
46
|
+
credentials: 'include',
|
|
41
47
|
signal: controller.signal,
|
|
42
48
|
});
|
|
43
49
|
if (!response.ok) {
|
|
@@ -69,19 +75,15 @@ export function useAgentStream() {
|
|
|
69
75
|
if (!dataLine)
|
|
70
76
|
continue;
|
|
71
77
|
const jsonStr = dataLine.slice(6);
|
|
78
|
+
let chunk;
|
|
72
79
|
try {
|
|
73
|
-
|
|
74
|
-
setState((s) => ({
|
|
75
|
-
...s,
|
|
76
|
-
chunks: [...s.chunks, chunk],
|
|
77
|
-
text: chunk.type === 'text' ? s.text + (chunk.content ?? '') : s.text,
|
|
78
|
-
error: chunk.type === 'error' ? (chunk.error ?? 'Unknown error') : s.error,
|
|
79
|
-
isStreaming: chunk.type !== 'done' && chunk.type !== 'error',
|
|
80
|
-
}));
|
|
80
|
+
chunk = JSON.parse(jsonStr);
|
|
81
81
|
}
|
|
82
82
|
catch {
|
|
83
83
|
// Malformed SSE data - skip
|
|
84
|
+
continue;
|
|
84
85
|
}
|
|
86
|
+
setState((s) => applyChunk(s, chunk));
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
}
|
|
@@ -97,5 +99,99 @@ export function useAgentStream() {
|
|
|
97
99
|
}));
|
|
98
100
|
}
|
|
99
101
|
}, []);
|
|
100
|
-
|
|
102
|
+
const submitElicitation = useCallback(async (elicitationId, action, content) => {
|
|
103
|
+
const sessionId = stateRef.current.sessionId;
|
|
104
|
+
if (!sessionId) {
|
|
105
|
+
throw new Error('useAgentStream: no sessionId — start() must run before submitElicitation()');
|
|
106
|
+
}
|
|
107
|
+
const body = { sessionId, elicitationId, action };
|
|
108
|
+
if (action === 'accept' && content)
|
|
109
|
+
body.content = content;
|
|
110
|
+
const response = await fetch('/api/agent-stream/elicit', {
|
|
111
|
+
method: 'POST',
|
|
112
|
+
headers: { 'Content-Type': 'application/json' },
|
|
113
|
+
body: JSON.stringify(body),
|
|
114
|
+
credentials: 'include',
|
|
115
|
+
});
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
const errText = await response.text();
|
|
118
|
+
throw new Error(`elicit response failed: HTTP ${response.status}: ${errText}`);
|
|
119
|
+
}
|
|
120
|
+
// Remove the resolved entry from pendingElicitations
|
|
121
|
+
setState((s) => ({
|
|
122
|
+
...s,
|
|
123
|
+
pendingElicitations: s.pendingElicitations.filter((p) => p.elicitationId !== elicitationId),
|
|
124
|
+
}));
|
|
125
|
+
}, []);
|
|
126
|
+
// submitElicitation needs the *current* sessionId at call time, so we
|
|
127
|
+
// mirror state into a ref. Avoids stale-closure bugs without making the
|
|
128
|
+
// callback's dependencies churn on every chunk.
|
|
129
|
+
const stateRef = useRef(state);
|
|
130
|
+
stateRef.current = state;
|
|
131
|
+
return { ...state, start, abort, reset, submitElicitation };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Pure reducer applying one SSE chunk to the hook's state. Extracted for
|
|
135
|
+
* test-friendliness — call sites can simulate `start()` by feeding chunks
|
|
136
|
+
* to `applyChunk()` against `INITIAL_STATE`.
|
|
137
|
+
*
|
|
138
|
+
* @internal
|
|
139
|
+
*/
|
|
140
|
+
function applyChunk(s, chunk) {
|
|
141
|
+
const next = {
|
|
142
|
+
...s,
|
|
143
|
+
chunks: [...s.chunks, chunk],
|
|
144
|
+
};
|
|
145
|
+
switch (chunk.type) {
|
|
146
|
+
case 'session_info':
|
|
147
|
+
if (chunk.sessionId)
|
|
148
|
+
next.sessionId = chunk.sessionId;
|
|
149
|
+
break;
|
|
150
|
+
case 'text':
|
|
151
|
+
next.text = s.text + (chunk.content ?? '');
|
|
152
|
+
break;
|
|
153
|
+
case 'error':
|
|
154
|
+
next.error = chunk.error ?? 'Unknown error';
|
|
155
|
+
next.isStreaming = false;
|
|
156
|
+
// Cancel all pending elicitations on stream error — the server-side
|
|
157
|
+
// session is being torn down so any open form is no longer
|
|
158
|
+
// resolvable.
|
|
159
|
+
next.pendingElicitations = [];
|
|
160
|
+
break;
|
|
161
|
+
case 'done':
|
|
162
|
+
next.isStreaming = false;
|
|
163
|
+
next.pendingElicitations = [];
|
|
164
|
+
break;
|
|
165
|
+
case 'elicitation_request':
|
|
166
|
+
if (chunk.elicitation && chunk.namespace) {
|
|
167
|
+
next.pendingElicitations = [
|
|
168
|
+
...s.pendingElicitations,
|
|
169
|
+
{
|
|
170
|
+
elicitationId: chunk.elicitation.elicitationId,
|
|
171
|
+
namespace: chunk.namespace,
|
|
172
|
+
requestedSchema: chunk.elicitation.requestedSchema,
|
|
173
|
+
...(chunk.elicitation.message ? { message: chunk.elicitation.message } : {}),
|
|
174
|
+
},
|
|
175
|
+
];
|
|
176
|
+
}
|
|
177
|
+
break;
|
|
178
|
+
// tool_call_start / tool_call_result / sampling_request: no state
|
|
179
|
+
// mutation beyond appending to `chunks`. Consumers render from the
|
|
180
|
+
// chunk list directly.
|
|
181
|
+
default:
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
return next;
|
|
101
185
|
}
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
// Test-only export
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
/**
|
|
190
|
+
* Pure reducer used internally to apply a chunk to state. Exported for
|
|
191
|
+
* unit tests so the chunk → state mapping can be verified without
|
|
192
|
+
* mounting a React component or stubbing `fetch`.
|
|
193
|
+
*
|
|
194
|
+
* @internal
|
|
195
|
+
*/
|
|
196
|
+
export const _applyChunkForTesting = applyChunk;
|
|
197
|
+
export const _initialStateForTesting = INITIAL_STATE;
|
package/dist/index.d.ts
CHANGED
|
@@ -45,12 +45,6 @@
|
|
|
45
45
|
*
|
|
46
46
|
* @packageDocumentation
|
|
47
47
|
*/
|
|
48
|
-
/**
|
|
49
|
-
* Check if the AI package is licensed for use.
|
|
50
|
-
* Returns false with a warning log if no Pro/Enterprise license is active.
|
|
51
|
-
* Call at application startup to verify licensing.
|
|
52
|
-
*/
|
|
53
|
-
export declare function checkAiLicense(): boolean;
|
|
54
48
|
export * from './a2a/index.js';
|
|
55
49
|
export * from './audit/index.js';
|
|
56
50
|
export * from './embeddings/index.js';
|
|
@@ -58,6 +52,7 @@ export * from './ingestion/index.js';
|
|
|
58
52
|
export * from './llm/client.js';
|
|
59
53
|
export * from './llm/provider-health.js';
|
|
60
54
|
export * from './llm/providers/base.js';
|
|
55
|
+
export * from './llm/providers/inference-snaps.js';
|
|
61
56
|
export * from './llm/providers/openai-compat.js';
|
|
62
57
|
export * from './llm/token-counter.js';
|
|
63
58
|
export * from './llm/workspace-provider-config.js';
|
|
@@ -75,6 +70,9 @@ export * from './tools/base.js';
|
|
|
75
70
|
export * from './tools/deduplicator.js';
|
|
76
71
|
export * from './tools/document-summarizer.js';
|
|
77
72
|
export * from './tools/mcp-adapter.js';
|
|
73
|
+
export * from './tools/mcp-elicitation.js';
|
|
74
|
+
export * from './tools/mcp-events.js';
|
|
75
|
+
export * from './tools/mcp-sampling.js';
|
|
78
76
|
export * from './tools/memory/index.js';
|
|
79
77
|
export * from './tools/registry.js';
|
|
80
78
|
export * from './tools/ticket-tools.js';
|