@ryuhq/sdk 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +179 -0
- package/README.md +31 -0
- package/dist/agent.cjs +761 -0
- package/dist/agent.d.cts +3 -0
- package/dist/agent.d.ts +3 -0
- package/dist/agent.js +23 -0
- package/dist/chunk-GXHL5CO7.js +353 -0
- package/dist/chunk-KPKMMGVC.js +671 -0
- package/dist/chunk-ODFEUVPW.js +100 -0
- package/dist/cli.cjs +858 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +454 -0
- package/dist/index-CEbS1SlS.d.cts +988 -0
- package/dist/index-DAxq7Y0R.d.ts +988 -0
- package/dist/index.cjs +1900 -0
- package/dist/index.d.cts +759 -0
- package/dist/index.d.ts +759 -0
- package/dist/index.js +771 -0
- package/dist/manifest.cjs +399 -0
- package/dist/manifest.d.cts +355 -0
- package/dist/manifest.d.ts +355 -0
- package/dist/manifest.js +38 -0
- package/package.json +56 -0
- package/src/agent/agent.ts +208 -0
- package/src/agent/index.ts +51 -0
- package/src/agent/loop.test.ts +261 -0
- package/src/agent/loop.ts +259 -0
- package/src/agent/model-call.ts +190 -0
- package/src/agent/query.ts +40 -0
- package/src/agent/tools.ts +295 -0
- package/src/builder.ts +473 -0
- package/src/cli/dev.test.ts +178 -0
- package/src/cli/dev.ts +425 -0
- package/src/cli.ts +390 -0
- package/src/contracts-lockstep.test.ts +77 -0
- package/src/generated/plugin-manifest.ts +1121 -0
- package/src/index.ts +141 -0
- package/src/manifest.test.ts +610 -0
- package/src/manifest.ts +589 -0
- package/src/mcp/bridge.test.ts +196 -0
- package/src/mcp/client.ts +253 -0
- package/src/mcp/fixture-server.ts +23 -0
- package/src/mcp/server.ts +351 -0
- package/src/model/client.test.ts +107 -0
- package/src/model/client.ts +179 -0
- package/src/model/gateway.ts +41 -0
- package/src/plugin/ryu-plugin.ts +191 -0
- package/src/runnable/agent.ts +338 -0
- package/src/runnable/app.ts +233 -0
- package/src/runnable/index.ts +61 -0
- package/src/runnable/primitives-hostapi.test.ts +73 -0
- package/src/runnable/primitives.test.ts +286 -0
- package/src/runnable/primitives.ts +610 -0
- package/src/runnable/runnable-types.ts +113 -0
- package/src/runnable/runnable.test.ts +397 -0
- package/src/runnable/skill.ts +60 -0
- package/src/runnable/tool.ts +260 -0
- package/src/runnable/turn-hook.test.ts +81 -0
- package/src/runnable/turn-hook.ts +191 -0
- package/src/runnable/workflow.ts +76 -0
|
@@ -0,0 +1,988 @@
|
|
|
1
|
+
import { RunnableMeta } from './manifest.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Native tool-calling model call for the Ryu agent runtime.
|
|
5
|
+
*
|
|
6
|
+
* The gateway-mandatory `ModelClient` (packages/sdk/src/model/client.ts) only
|
|
7
|
+
* exposes text `chat`/`stream` — it cannot pass a `tools` array or receive
|
|
8
|
+
* `tool_calls`. The autonomous agent loop needs both, so this module makes a
|
|
9
|
+
* direct request to the node's gateway `POST /v1/chat/completions` endpoint
|
|
10
|
+
* with the caller's own `tools` and reads back `message.tool_calls`.
|
|
11
|
+
*
|
|
12
|
+
* The no-direct-provider guarantee is preserved: `assertAllowedEgressUrl` (the
|
|
13
|
+
* same Rust-cored blocklist the ModelClient uses) is called before every fetch,
|
|
14
|
+
* so a direct provider base URL throws exactly as it would elsewhere in the SDK.
|
|
15
|
+
*
|
|
16
|
+
* `x-ryu-raw-tools: on` is always sent. On a plain gateway it is a harmless
|
|
17
|
+
* no-op; on a Composio-on managed node it forces the plain completion branch so
|
|
18
|
+
* the caller's `tool_calls` are returned verbatim instead of being intercepted
|
|
19
|
+
* and executed by Core's own tool loop (see apps/gateway/src/pipeline/mod.rs).
|
|
20
|
+
*/
|
|
21
|
+
/** An OpenAI function tool definition passed to the model. */
|
|
22
|
+
interface ToolFunctionDef {
|
|
23
|
+
function: {
|
|
24
|
+
description?: string;
|
|
25
|
+
name: string;
|
|
26
|
+
/** JSON Schema for the function's arguments. */
|
|
27
|
+
parameters: Record<string, unknown>;
|
|
28
|
+
};
|
|
29
|
+
type: "function";
|
|
30
|
+
}
|
|
31
|
+
/** A single tool call emitted by the model. */
|
|
32
|
+
interface ToolCall {
|
|
33
|
+
function: {
|
|
34
|
+
/** JSON-encoded arguments string (per the OpenAI wire format). */
|
|
35
|
+
arguments: string;
|
|
36
|
+
name: string;
|
|
37
|
+
};
|
|
38
|
+
id: string;
|
|
39
|
+
type: "function";
|
|
40
|
+
}
|
|
41
|
+
/** An assistant turn — may carry text, tool calls, or both. */
|
|
42
|
+
interface AssistantMessage {
|
|
43
|
+
content: string | null;
|
|
44
|
+
role: "assistant";
|
|
45
|
+
tool_calls?: ToolCall[];
|
|
46
|
+
}
|
|
47
|
+
/** A message in the loop's running transcript. */
|
|
48
|
+
type LoopMessage = {
|
|
49
|
+
content: string;
|
|
50
|
+
role: "system" | "user";
|
|
51
|
+
} | AssistantMessage | {
|
|
52
|
+
content: string;
|
|
53
|
+
role: "tool";
|
|
54
|
+
tool_call_id: string;
|
|
55
|
+
};
|
|
56
|
+
/** Token usage as reported by the gateway (optional — gateway may omit). */
|
|
57
|
+
interface ModelUsage {
|
|
58
|
+
completionTokens: number;
|
|
59
|
+
promptTokens: number;
|
|
60
|
+
totalTokens: number;
|
|
61
|
+
}
|
|
62
|
+
/** Options for a single native tool-calling completion. */
|
|
63
|
+
interface ModelCallOptions {
|
|
64
|
+
/** Gateway base URL (no trailing `/v1`). */
|
|
65
|
+
baseUrl: string;
|
|
66
|
+
/** Running transcript. */
|
|
67
|
+
messages: LoopMessage[];
|
|
68
|
+
/** Model id routed by the gateway (provider is derived from the id). */
|
|
69
|
+
model: string;
|
|
70
|
+
/** Abort signal for cancellation. */
|
|
71
|
+
signal?: AbortSignal;
|
|
72
|
+
/** Bearer token forwarded to the gateway (never a provider key). */
|
|
73
|
+
token?: string;
|
|
74
|
+
/** How the model should choose tools; defaults to gateway/provider default. */
|
|
75
|
+
toolChoice?: "auto" | "none" | "required";
|
|
76
|
+
/** Function tool definitions the model may call. */
|
|
77
|
+
tools?: ToolFunctionDef[];
|
|
78
|
+
}
|
|
79
|
+
/** Result of a single completion. */
|
|
80
|
+
interface ModelCallResult {
|
|
81
|
+
finishReason: string | null;
|
|
82
|
+
message: AssistantMessage;
|
|
83
|
+
usage?: ModelUsage;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Call the node's gateway with the caller's own tools and return the first
|
|
87
|
+
* choice, including any `tool_calls`.
|
|
88
|
+
*
|
|
89
|
+
* Throws when the base URL is a direct provider (egress enforcement) or when
|
|
90
|
+
* the gateway returns a non-2xx status.
|
|
91
|
+
*/
|
|
92
|
+
declare function callModelWithTools(options: ModelCallOptions): Promise<ModelCallResult>;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Gateway-mandatory model client for the Ryu SDK.
|
|
96
|
+
*
|
|
97
|
+
* Rust-cored: this is a thin TypeScript wrapper over the `@ryuhq/sdk-native`
|
|
98
|
+
* addon's `ModelClient`, which is the `crates/ryu-sdk` Rust core. Every model
|
|
99
|
+
* call is routed by the Rust core to the Ryu gateway's OpenAI-compatible
|
|
100
|
+
* `/v1/chat/completions` endpoint; direct-provider base URLs are rejected at
|
|
101
|
+
* construction. No provider SDK or base URL is ever imported here.
|
|
102
|
+
*
|
|
103
|
+
* Usage:
|
|
104
|
+
*
|
|
105
|
+
* const model = defineModel("gpt-4o");
|
|
106
|
+
* const reply = await model.chat([{ role: "user", content: "hello" }]);
|
|
107
|
+
* for await (const delta of model.stream([{ role: "user", content: "hi" }])) {
|
|
108
|
+
* process.stdout.write(delta.content);
|
|
109
|
+
* }
|
|
110
|
+
*/
|
|
111
|
+
/** A single message in a chat conversation. */
|
|
112
|
+
interface ChatMessage {
|
|
113
|
+
/** The message text. */
|
|
114
|
+
content: string;
|
|
115
|
+
/** The speaker: "system", "user", or "assistant". */
|
|
116
|
+
role: "system" | "user" | "assistant";
|
|
117
|
+
}
|
|
118
|
+
/** A streaming chat completion delta. */
|
|
119
|
+
interface ChatDelta {
|
|
120
|
+
/** Incremental text fragment from the model. */
|
|
121
|
+
content: string | null;
|
|
122
|
+
/** Non-null on the final chunk when `finish_reason` is set. */
|
|
123
|
+
finishReason: string | null;
|
|
124
|
+
}
|
|
125
|
+
/** Non-streaming chat completion result. */
|
|
126
|
+
interface ChatResult {
|
|
127
|
+
/** The full assistant reply text. */
|
|
128
|
+
content: string;
|
|
129
|
+
/** The gateway/model-reported finish reason. */
|
|
130
|
+
finishReason: string | null;
|
|
131
|
+
/** Usage stats as reported by the gateway (optional — gateway may omit). */
|
|
132
|
+
usage?: {
|
|
133
|
+
promptTokens: number;
|
|
134
|
+
completionTokens: number;
|
|
135
|
+
totalTokens: number;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/** Options accepted by `defineModel`. */
|
|
139
|
+
interface ModelClientOptions {
|
|
140
|
+
/**
|
|
141
|
+
* Gateway base URL (no trailing `/v1`). Defaults to `RYU_GATEWAY_URL` then
|
|
142
|
+
* the Rust core's default. Direct provider URLs are rejected at construction.
|
|
143
|
+
*/
|
|
144
|
+
baseUrl?: string;
|
|
145
|
+
/**
|
|
146
|
+
* Bearer token forwarded to the gateway. Defaults to `RYU_GATEWAY_TOKEN`.
|
|
147
|
+
* This is the gateway token, never a provider API key.
|
|
148
|
+
*/
|
|
149
|
+
token?: string;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* A gateway-mandatory model client backed by the Rust core.
|
|
153
|
+
*
|
|
154
|
+
* All calls go through the native `ModelClient`; if the configured base URL is a
|
|
155
|
+
* direct provider, construction throws.
|
|
156
|
+
*/
|
|
157
|
+
declare class ModelClient {
|
|
158
|
+
readonly model: string;
|
|
159
|
+
readonly baseUrl: string;
|
|
160
|
+
private readonly native;
|
|
161
|
+
constructor(model: string, options?: ModelClientOptions);
|
|
162
|
+
/** Send a non-streaming chat completion request to the gateway. */
|
|
163
|
+
chat(messages: ChatMessage[]): Promise<ChatResult>;
|
|
164
|
+
/** Send a streaming chat completion request, yielding deltas as they arrive. */
|
|
165
|
+
stream(messages: ChatMessage[]): AsyncGenerator<ChatDelta>;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Create a gateway-mandatory model client for `modelId`.
|
|
169
|
+
*
|
|
170
|
+
* const model = defineModel("gpt-4o");
|
|
171
|
+
* const model = defineModel("claude-3-5-sonnet", { baseUrl: "http://my-gateway:7981" });
|
|
172
|
+
*
|
|
173
|
+
* A direct provider URL throws immediately (egress enforcement in the Rust core).
|
|
174
|
+
*/
|
|
175
|
+
declare function defineModel(modelId: string, options?: ModelClientOptions): ModelClient;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Composable primitive-client surface for the Ryu SDK runtime context.
|
|
179
|
+
*
|
|
180
|
+
* Now that each capability is its own crate with a clean trait
|
|
181
|
+
* (`crates/ryu-rag`, `crates/ryu-memory`, `crates/ryu-realtime`,
|
|
182
|
+
* `crates/ryu-durable`, `crates/ryu-engines`, `crates/ryu-tts`,
|
|
183
|
+
* `crates/ryu-stt`, `crates/ryu-image`), the SDK exposes each as a typed,
|
|
184
|
+
* **gateway-mandatory** client on `RunnableContext`: `ctx.rag.retrieve()`,
|
|
185
|
+
* `ctx.memory.recall()`, `ctx.engines.complete()`, and so on. An agent composes
|
|
186
|
+
* the same building blocks a developer does — the DX payoff of decomposition
|
|
187
|
+
* (program §6b).
|
|
188
|
+
*
|
|
189
|
+
* These clients are **thin typed wrappers** over the EXISTING host transport
|
|
190
|
+
* families — they invent NO new backend endpoints. The method names and grants
|
|
191
|
+
* mirror the canonical vocabulary in
|
|
192
|
+
* `packages/app-host/src/rpc.ts` (`METHOD_CAPABILITY` / `GRANT_CAPABILITY`); the
|
|
193
|
+
* arg/result shapes mirror the `RpcServices` signatures there. We MIRROR that
|
|
194
|
+
* vocabulary rather than importing it: `@ryuhq/sdk` is a published package and
|
|
195
|
+
* `@ryu/app-host` is a desktop-host package in a disjoint lane.
|
|
196
|
+
*
|
|
197
|
+
* Three real transport shapes exist in `rpc.ts`, so the transport exposes three
|
|
198
|
+
* ops (all reach a Core node the host holds the token for):
|
|
199
|
+
* - `bridge` → the `PluginHookBridge` families (`POST /api/plugins/:id/host`,
|
|
200
|
+
* `{ method, args }`) — e.g. `model.complete` → `host.sideModel`.
|
|
201
|
+
* - `direct` → host-direct Core data-path calls the host makes on the frame's
|
|
202
|
+
* behalf (`POST /api/images/generate`, `/api/voice/speak`,
|
|
203
|
+
* `/api/voice/transcribe`).
|
|
204
|
+
* - `capability` → the capability broker (`POST /api/host/capability/:cap`) for
|
|
205
|
+
* caps that have no rpc family yet (rag/memory/realtime/durable/
|
|
206
|
+
* engines.embed). These are marked `@requires-grant`: the caller
|
|
207
|
+
* must DECLARE the edge in `requires.capabilities` and hold the
|
|
208
|
+
* bound provider's grant, else Core fails closed (404/403).
|
|
209
|
+
*/
|
|
210
|
+
/**
|
|
211
|
+
* Low-level dispatcher a primitive client uses to reach a Core node. Injected
|
|
212
|
+
* so the SDK stays transport-agnostic (the same seam as `ctx.gateway`); a
|
|
213
|
+
* default HTTP implementation is {@link httpPrimitiveTransport}.
|
|
214
|
+
*/
|
|
215
|
+
interface PrimitiveTransport {
|
|
216
|
+
/**
|
|
217
|
+
* Invoke a `PluginHookBridge` family method (`POST /api/plugins/:id/host`).
|
|
218
|
+
* `method` is the exact `rpc.ts` `METHOD_CAPABILITY` key (e.g.
|
|
219
|
+
* `"model.complete"`); the host maps it to the closed `host.*` bridge path.
|
|
220
|
+
*/
|
|
221
|
+
bridge(method: string, args: unknown): Promise<unknown>;
|
|
222
|
+
/**
|
|
223
|
+
* Invoke an abstract capability through the broker
|
|
224
|
+
* (`POST /api/host/capability/:cap`). `@requires-grant`: the caller must have
|
|
225
|
+
* declared `requires.capabilities: [{ capability: cap }]` and hold the bound
|
|
226
|
+
* provider's grant, or Core fails closed.
|
|
227
|
+
*/
|
|
228
|
+
capability(cap: string, body: unknown): Promise<unknown>;
|
|
229
|
+
/**
|
|
230
|
+
* Invoke a host-direct Core data-path endpoint (`POST {path}`). Used for the
|
|
231
|
+
* media families the host reaches directly (`/api/images/generate`,
|
|
232
|
+
* `/api/voice/speak`, `/api/voice/transcribe`) rather than via the bridge.
|
|
233
|
+
*
|
|
234
|
+
* The two voice endpoints do NOT speak plain JSON-in/JSON-out — a real Core
|
|
235
|
+
* node requires a multipart `file` upload for transcription and streams raw
|
|
236
|
+
* `audio/wav` bytes back from synthesis. The default {@link httpPrimitiveTransport}
|
|
237
|
+
* therefore reshapes those two calls (mirroring the desktop host's `rpc.ts`):
|
|
238
|
+
* - `/api/voice/transcribe` — `{ audio: data-URL, filename? }` → multipart
|
|
239
|
+
* `file` upload; resolves to the transcript `string`.
|
|
240
|
+
* - `/api/voice/speak` — JSON in; resolves to a renderable `data:` audio URL
|
|
241
|
+
* built from the returned `audio/wav` bytes.
|
|
242
|
+
* Every other path is a straight JSON round-trip.
|
|
243
|
+
*/
|
|
244
|
+
direct(path: string, body: unknown): Promise<unknown>;
|
|
245
|
+
}
|
|
246
|
+
/** Options for {@link httpPrimitiveTransport}. */
|
|
247
|
+
interface HttpPrimitiveTransportOptions {
|
|
248
|
+
/** Injectable `fetch` (defaults to the global). */
|
|
249
|
+
fetchImpl?: typeof fetch;
|
|
250
|
+
/**
|
|
251
|
+
* Core **node** base URL (no trailing slash) — NEVER a direct provider. The
|
|
252
|
+
* URL is validated against the direct-provider egress blocklist so every
|
|
253
|
+
* primitive call stays governed (the gateway-mandatory rule).
|
|
254
|
+
*/
|
|
255
|
+
nodeUrl: string;
|
|
256
|
+
/**
|
|
257
|
+
* The calling plugin's reverse-domain id. REQUIRED for the `bridge` op —
|
|
258
|
+
* `/api/plugins/:id/host` authenticates this id and gates on its
|
|
259
|
+
* Gateway-approved grants. Omit only if the caller never uses bridge-backed
|
|
260
|
+
* primitives (`ctx.engines.complete`).
|
|
261
|
+
*/
|
|
262
|
+
pluginId?: string;
|
|
263
|
+
/** Node bearer token forwarded on every call. */
|
|
264
|
+
token?: string;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* A default HTTP {@link PrimitiveTransport} targeting a Core node. Validates
|
|
268
|
+
* `nodeUrl` against the direct-provider egress blocklist at construction, so a
|
|
269
|
+
* mis-pointed transport can never route a primitive call at a raw provider.
|
|
270
|
+
*/
|
|
271
|
+
declare function httpPrimitiveTransport(options: HttpPrimitiveTransportOptions): PrimitiveTransport;
|
|
272
|
+
/**
|
|
273
|
+
* How one primitive method reaches Core. This is the SDK's single mirror of the
|
|
274
|
+
* `rpc.ts` `METHOD_CAPABILITY` / `GRANT_CAPABILITY` maps — keep it in lockstep
|
|
275
|
+
* with that canonical source. `bridge`/`direct` families exist today;
|
|
276
|
+
* `broker` families are `@requires-grant` (declared capability edge).
|
|
277
|
+
*/
|
|
278
|
+
type PrimitiveBinding = {
|
|
279
|
+
readonly transport: "bridge";
|
|
280
|
+
/** Exact `rpc.ts` method key (e.g. `"model.complete"`). */
|
|
281
|
+
readonly method: string;
|
|
282
|
+
/** Gateway grant that unlocks it (`GRANT_CAPABILITY` inverse). */
|
|
283
|
+
readonly grant: string;
|
|
284
|
+
} | {
|
|
285
|
+
readonly transport: "direct";
|
|
286
|
+
/** Core data-path endpoint the host calls directly. */
|
|
287
|
+
readonly path: string;
|
|
288
|
+
/** Gateway grant that unlocks it. */
|
|
289
|
+
readonly grant: string;
|
|
290
|
+
} | {
|
|
291
|
+
readonly transport: "broker";
|
|
292
|
+
/** Abstract capability name (the broker `:cap` segment + the
|
|
293
|
+
* `requires.capabilities` edge the caller must declare). */
|
|
294
|
+
readonly capability: string;
|
|
295
|
+
};
|
|
296
|
+
/**
|
|
297
|
+
* The binding for every primitive method, keyed `"<namespace>.<method>"`.
|
|
298
|
+
* Mirrors `rpc.ts`; the ONLY place primitive→endpoint knowledge lives.
|
|
299
|
+
*/
|
|
300
|
+
declare const PRIMITIVE_BINDINGS: Record<string, PrimitiveBinding>;
|
|
301
|
+
/** One retrieved chunk (`crates/ryu-rag` `RagChunk`). */
|
|
302
|
+
interface RagChunk {
|
|
303
|
+
id: string;
|
|
304
|
+
metadata?: Record<string, unknown>;
|
|
305
|
+
score: number;
|
|
306
|
+
source?: string;
|
|
307
|
+
text: string;
|
|
308
|
+
}
|
|
309
|
+
/** One rerank result (`crates/ryu-rag` reranker trait). */
|
|
310
|
+
interface RagRerankResult {
|
|
311
|
+
document: string;
|
|
312
|
+
index: number;
|
|
313
|
+
score: number;
|
|
314
|
+
}
|
|
315
|
+
/** RAG primitive — retrieval, embedding, reranking (`crates/ryu-rag`). */
|
|
316
|
+
interface RagClient {
|
|
317
|
+
/** Embed text into vectors. `@requires-grant rag`. */
|
|
318
|
+
embed(input: {
|
|
319
|
+
input: string | string[];
|
|
320
|
+
model?: string;
|
|
321
|
+
}): Promise<number[][]>;
|
|
322
|
+
/** Rerank `documents` against `query`. `@requires-grant rag`. */
|
|
323
|
+
rerank(input: {
|
|
324
|
+
query: string;
|
|
325
|
+
documents: string[];
|
|
326
|
+
topK?: number;
|
|
327
|
+
model?: string;
|
|
328
|
+
}): Promise<RagRerankResult[]>;
|
|
329
|
+
/** Vector/GraphRAG retrieval for `query`. `@requires-grant rag`. */
|
|
330
|
+
retrieve(input: {
|
|
331
|
+
query: string;
|
|
332
|
+
topK?: number;
|
|
333
|
+
spaceId?: string;
|
|
334
|
+
filter?: Record<string, unknown>;
|
|
335
|
+
}): Promise<RagChunk[]>;
|
|
336
|
+
}
|
|
337
|
+
/** One recalled memory (`crates/ryu-memory` `MemoryItem`). */
|
|
338
|
+
interface MemoryItem {
|
|
339
|
+
category?: string;
|
|
340
|
+
content: string;
|
|
341
|
+
id: string;
|
|
342
|
+
importance?: number;
|
|
343
|
+
level?: string;
|
|
344
|
+
score?: number;
|
|
345
|
+
tags?: string[];
|
|
346
|
+
}
|
|
347
|
+
/** Memory primitive — recall + store (`crates/ryu-memory`). */
|
|
348
|
+
interface MemoryClient {
|
|
349
|
+
/** Semantic recall across the readable scope levels. `@requires-grant memory`. */
|
|
350
|
+
recall(input: {
|
|
351
|
+
query: string;
|
|
352
|
+
levels?: string[];
|
|
353
|
+
limit?: number;
|
|
354
|
+
}): Promise<MemoryItem[]>;
|
|
355
|
+
/** Persist a memory. `@requires-grant memory`. */
|
|
356
|
+
store(input: {
|
|
357
|
+
content: string;
|
|
358
|
+
level?: string;
|
|
359
|
+
category?: string;
|
|
360
|
+
importance?: number;
|
|
361
|
+
tags?: string[];
|
|
362
|
+
}): Promise<{
|
|
363
|
+
id: string;
|
|
364
|
+
}>;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* A realtime subscription handle. The broker is a unary POST, so `subscribe`
|
|
368
|
+
* cannot stream today — it returns a handle (the crate's typed event contract
|
|
369
|
+
* grows a live channel later). Honest shape over a promised-but-unbacked stream.
|
|
370
|
+
*/
|
|
371
|
+
interface RealtimeSubscription {
|
|
372
|
+
room: string;
|
|
373
|
+
subscriptionId: string;
|
|
374
|
+
}
|
|
375
|
+
/** Realtime primitive — typed room events (`crates/ryu-realtime`). */
|
|
376
|
+
interface RealtimeClient {
|
|
377
|
+
/** Broadcast a typed event to a room. `@requires-grant realtime`. */
|
|
378
|
+
broadcast(input: {
|
|
379
|
+
room: string;
|
|
380
|
+
event: string;
|
|
381
|
+
payload?: unknown;
|
|
382
|
+
}): Promise<void>;
|
|
383
|
+
/** Open a subscription handle for a room. `@requires-grant realtime`. */
|
|
384
|
+
subscribe(input: {
|
|
385
|
+
room: string;
|
|
386
|
+
}): Promise<RealtimeSubscription>;
|
|
387
|
+
}
|
|
388
|
+
/** Durable primitive — checkpoint + resume (`crates/ryu-durable`). */
|
|
389
|
+
interface DurableClient {
|
|
390
|
+
/** Persist a checkpoint; returns a resume token. `@requires-grant durable`. */
|
|
391
|
+
checkpoint(input: {
|
|
392
|
+
key: string;
|
|
393
|
+
state: unknown;
|
|
394
|
+
}): Promise<{
|
|
395
|
+
token: string;
|
|
396
|
+
}>;
|
|
397
|
+
/** Resume from a checkpoint token (`null` when unknown). `@requires-grant durable`. */
|
|
398
|
+
resume(input: {
|
|
399
|
+
token: string;
|
|
400
|
+
}): Promise<{
|
|
401
|
+
state: unknown;
|
|
402
|
+
} | null>;
|
|
403
|
+
}
|
|
404
|
+
/** Engines primitive — completion + embedding (`crates/ryu-engines`). */
|
|
405
|
+
interface EnginesClient {
|
|
406
|
+
/**
|
|
407
|
+
* Tool-less one-shot completion (bridge `model.complete` → `host.sideModel`,
|
|
408
|
+
* Gateway-routed). Grant `hook:side-model`.
|
|
409
|
+
*/
|
|
410
|
+
complete(input: {
|
|
411
|
+
prompt: string;
|
|
412
|
+
system?: string;
|
|
413
|
+
model?: string;
|
|
414
|
+
modelPrefKey?: string;
|
|
415
|
+
effort?: string;
|
|
416
|
+
}): Promise<string>;
|
|
417
|
+
/** Embed text into vectors. `@requires-grant engines`. */
|
|
418
|
+
embed(input: {
|
|
419
|
+
input: string | string[];
|
|
420
|
+
model?: string;
|
|
421
|
+
}): Promise<number[][]>;
|
|
422
|
+
}
|
|
423
|
+
/** TTS primitive — speech synthesis (`crates/ryu-tts`). */
|
|
424
|
+
interface TtsClient {
|
|
425
|
+
/**
|
|
426
|
+
* Synthesize speech (host-direct `/api/voice/speak`, Gateway-governed).
|
|
427
|
+
* Returns a renderable `data:` audio URL. Grant `media:generate`.
|
|
428
|
+
*/
|
|
429
|
+
speak(input: {
|
|
430
|
+
text: string;
|
|
431
|
+
engine?: string;
|
|
432
|
+
voice?: string;
|
|
433
|
+
speed?: number;
|
|
434
|
+
language?: string;
|
|
435
|
+
}): Promise<string>;
|
|
436
|
+
}
|
|
437
|
+
/** STT primitive — transcription (`crates/ryu-stt`). */
|
|
438
|
+
interface SttClient {
|
|
439
|
+
/**
|
|
440
|
+
* Transcribe an audio `data:` URL (host-direct `/api/voice/transcribe`).
|
|
441
|
+
* Returns the text. Grant `media:transcribe`.
|
|
442
|
+
*/
|
|
443
|
+
transcribe(input: {
|
|
444
|
+
audio: string;
|
|
445
|
+
filename?: string;
|
|
446
|
+
}): Promise<string>;
|
|
447
|
+
}
|
|
448
|
+
/** Image primitive — generation (`crates/ryu-image`). */
|
|
449
|
+
interface ImageClient {
|
|
450
|
+
/**
|
|
451
|
+
* Generate image(s) from a prompt (host-direct `/api/images/generate`,
|
|
452
|
+
* Gateway-governed). Returns renderable `data:` URLs. Grant `media:generate`.
|
|
453
|
+
*/
|
|
454
|
+
generate(input: {
|
|
455
|
+
prompt: string;
|
|
456
|
+
count?: number;
|
|
457
|
+
size?: string;
|
|
458
|
+
provider?: string;
|
|
459
|
+
model?: string;
|
|
460
|
+
}): Promise<string[]>;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* The composable primitive bundle mounted on {@link RunnableContext}. Every
|
|
464
|
+
* client routes through the Gateway (bridge/direct/broker all reach a governed
|
|
465
|
+
* Core node) — the same "what runs vs what is allowed" split as `ctx.gateway`.
|
|
466
|
+
*/
|
|
467
|
+
interface RyuPrimitives {
|
|
468
|
+
durable: DurableClient;
|
|
469
|
+
engines: EnginesClient;
|
|
470
|
+
image: ImageClient;
|
|
471
|
+
memory: MemoryClient;
|
|
472
|
+
rag: RagClient;
|
|
473
|
+
realtime: RealtimeClient;
|
|
474
|
+
stt: SttClient;
|
|
475
|
+
tts: TtsClient;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Build the typed {@link RyuPrimitives} bundle over a {@link PrimitiveTransport}.
|
|
479
|
+
* Each method is a thin wrapper: bridge/direct families forward to the existing
|
|
480
|
+
* endpoints; broker families POST to `/api/host/capability/:cap` (`@requires-grant`).
|
|
481
|
+
*/
|
|
482
|
+
declare function createPrimitives(transport: PrimitiveTransport): RyuPrimitives;
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Shared Runnable interface and context types.
|
|
486
|
+
*
|
|
487
|
+
* Every factory (defineAgent, defineWorkflow, defineTool, defineSkill) returns
|
|
488
|
+
* a value that satisfies the `Runnable` interface. This keeps the contract
|
|
489
|
+
* in one place and avoids circular imports between the four factory modules.
|
|
490
|
+
*/
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Thin client over the Ryu gateway `POST /v1/chat/completions` endpoint.
|
|
494
|
+
*
|
|
495
|
+
* This is the ONLY way a Runnable may invoke a model. Injected via
|
|
496
|
+
* `RunnableContext.gateway` so every call is gateway-mandatory — matching the
|
|
497
|
+
* Core-vs-Gateway rule (the SDK decides what runs; the gateway decides what is
|
|
498
|
+
* allowed/measured/paid).
|
|
499
|
+
*
|
|
500
|
+
* Mirrors the interface specified in packages/sdk/README.md §2 and the
|
|
501
|
+
* `ModelClient` shape in `packages/sdk/src/model/client.ts`.
|
|
502
|
+
*/
|
|
503
|
+
interface GatewayClient {
|
|
504
|
+
/** POST /v1/chat/completions (non-streaming). */
|
|
505
|
+
chat(messages: ChatMessage[]): Promise<ChatResult>;
|
|
506
|
+
/** POST /v1/chat/completions (streaming SSE). */
|
|
507
|
+
stream(messages: ChatMessage[]): AsyncGenerator<ChatDelta>;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Context injected into every `Runnable.run()` call.
|
|
511
|
+
*
|
|
512
|
+
* The gateway client is always present (fail-closed): a missing gateway throws
|
|
513
|
+
* at construction time via `defineModel`, never at run time.
|
|
514
|
+
*/
|
|
515
|
+
interface RunnableContext {
|
|
516
|
+
/** Durable primitive: checkpoint · resume (`crates/ryu-durable`). */
|
|
517
|
+
durable?: DurableClient;
|
|
518
|
+
/** Engines primitive: complete · embed (`crates/ryu-engines`). */
|
|
519
|
+
engines?: EnginesClient;
|
|
520
|
+
/**
|
|
521
|
+
* Gateway client — the single allowed path for model calls.
|
|
522
|
+
* Never null; a Runnable that needs a model must use this.
|
|
523
|
+
*/
|
|
524
|
+
gateway: GatewayClient;
|
|
525
|
+
/** Image primitive: generate (`crates/ryu-image`). */
|
|
526
|
+
image?: ImageClient;
|
|
527
|
+
/** Memory primitive: recall · store (`crates/ryu-memory`). */
|
|
528
|
+
memory?: MemoryClient;
|
|
529
|
+
/** RAG primitive: retrieve · embed · rerank (`crates/ryu-rag`). */
|
|
530
|
+
rag?: RagClient;
|
|
531
|
+
/** Realtime primitive: broadcast · subscribe (`crates/ryu-realtime`). */
|
|
532
|
+
realtime?: RealtimeClient;
|
|
533
|
+
/** Optional session id for stateful runs (Core session). */
|
|
534
|
+
sessionId?: string;
|
|
535
|
+
/** Signal to abort a long-running run. */
|
|
536
|
+
signal?: AbortSignal;
|
|
537
|
+
/** STT primitive: transcribe (`crates/ryu-stt`). */
|
|
538
|
+
stt?: SttClient;
|
|
539
|
+
/** TTS primitive: speak (`crates/ryu-tts`). */
|
|
540
|
+
tts?: TtsClient;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* The single contract for everything that can run in Ryu:
|
|
544
|
+
* Agent | Workflow | Tool | Skill.
|
|
545
|
+
*
|
|
546
|
+
* Typed over `TInput` (what the caller passes) and `TOutput` (what run()
|
|
547
|
+
* returns). The `kind` field narrows the discriminated union.
|
|
548
|
+
*/
|
|
549
|
+
interface Runnable<TInput = unknown, TOutput = unknown> {
|
|
550
|
+
/** Stable unique identifier (e.g. "agent-researcher"). */
|
|
551
|
+
readonly id: string;
|
|
552
|
+
/** Kind tag — narrows the discriminated union. */
|
|
553
|
+
readonly kind: "agent" | "workflow" | "tool" | "skill";
|
|
554
|
+
/** Human-readable name. */
|
|
555
|
+
readonly name: string;
|
|
556
|
+
/**
|
|
557
|
+
* Execute this Runnable.
|
|
558
|
+
*
|
|
559
|
+
* Every model call MUST go through `ctx.gateway`. Direct provider imports
|
|
560
|
+
* are forbidden by the SDK's egress enforcement (assertAllowedEgressUrl).
|
|
561
|
+
*/
|
|
562
|
+
run(input: TInput, ctx: RunnableContext): Promise<TOutput>;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* defineTool — factory for Runnable tools.
|
|
567
|
+
*
|
|
568
|
+
* A tool is a stateless function invoked by an agent or workflow step.
|
|
569
|
+
* It accepts a typed schema (Zod-style field definitions) that is converted
|
|
570
|
+
* to a JSON Schema object compatible with Core's `ToolInfo.schema` shape
|
|
571
|
+
* (apps/core/src/sidecar/adapters/mod.rs:66-71).
|
|
572
|
+
*
|
|
573
|
+
* Input is validated against the schema at run() time; invalid input throws
|
|
574
|
+
* a descriptive Error before the tool body executes.
|
|
575
|
+
*
|
|
576
|
+
* Tools do NOT require model calls and therefore do not need `ctx.gateway`
|
|
577
|
+
* to be present, but the context is still injected so tools can optionally
|
|
578
|
+
* call gateway.chat() when they need model assistance.
|
|
579
|
+
*/
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* A single JSON Schema property descriptor — the subset required by Core's
|
|
583
|
+
* `ToolInfo.schema` field and the OpenAI function-calling format.
|
|
584
|
+
*/
|
|
585
|
+
interface JsonSchemaProperty {
|
|
586
|
+
/** Human-readable description surfaced to the model. */
|
|
587
|
+
description?: string;
|
|
588
|
+
/** Allowed enum values. */
|
|
589
|
+
enum?: unknown[];
|
|
590
|
+
/** Array item schema (required when type is "array"). */
|
|
591
|
+
items?: JsonSchemaProperty;
|
|
592
|
+
/** Nested object properties (used when type is "object"). */
|
|
593
|
+
properties?: Record<string, JsonSchemaProperty>;
|
|
594
|
+
/** Required keys list (used when type is "object"). */
|
|
595
|
+
required?: string[];
|
|
596
|
+
/** JSON Schema type string. */
|
|
597
|
+
type: "string" | "number" | "integer" | "boolean" | "array" | "object";
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Zod-style schema descriptor for a tool's input.
|
|
601
|
+
*
|
|
602
|
+
* Keys are field names; values describe their JSON Schema shape. Required
|
|
603
|
+
* fields are listed separately under `required`.
|
|
604
|
+
*
|
|
605
|
+
* This intentionally mirrors the shape that `ToolInfo.schema` expects in
|
|
606
|
+
* `apps/core/src/sidecar/adapters/mod.rs` so a `defineTool` output can be
|
|
607
|
+
* forwarded to Core without transformation.
|
|
608
|
+
*/
|
|
609
|
+
interface ToolSchema {
|
|
610
|
+
/** Field definitions. */
|
|
611
|
+
properties: Record<string, JsonSchemaProperty>;
|
|
612
|
+
/** Names of fields that must be present in the input. */
|
|
613
|
+
required?: string[];
|
|
614
|
+
/** Type is always "object" for a tool's top-level input schema. */
|
|
615
|
+
type: "object";
|
|
616
|
+
}
|
|
617
|
+
/** Options accepted by `defineTool`. */
|
|
618
|
+
interface ToolOptions<TInput extends Record<string, unknown>, TOutput> {
|
|
619
|
+
/** Stable unique identifier (e.g. "tool-web-search"). */
|
|
620
|
+
id: string;
|
|
621
|
+
/** Human-readable display name. */
|
|
622
|
+
name: string;
|
|
623
|
+
/**
|
|
624
|
+
* The tool's run implementation.
|
|
625
|
+
*
|
|
626
|
+
* Called only after input validation passes. Model calls are optional but
|
|
627
|
+
* must go through `ctx.gateway` if used.
|
|
628
|
+
*/
|
|
629
|
+
run(input: TInput, ctx: RunnableContext): Promise<TOutput>;
|
|
630
|
+
/** JSON Schema describing the tool's input — used for input validation and Core ToolInfo. */
|
|
631
|
+
schema: ToolSchema;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* A `Runnable` with an extra `schema` field exposing the tool's JSON Schema.
|
|
635
|
+
*
|
|
636
|
+
* The `schema` is compatible with Core's `ToolInfo.schema` shape so it can
|
|
637
|
+
* be forwarded verbatim to Core's MCP/ACP layer.
|
|
638
|
+
*/
|
|
639
|
+
interface ToolRunnable<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput = unknown> extends Runnable<TInput, TOutput> {
|
|
640
|
+
readonly kind: "tool";
|
|
641
|
+
/** JSON Schema for this tool's input — compatible with Core's ToolInfo.schema. */
|
|
642
|
+
readonly schema: ToolSchema;
|
|
643
|
+
/**
|
|
644
|
+
* The `run` body serialized for Core's `inline_deno` tool backend — the exact
|
|
645
|
+
* same technique `defineTurnHook` uses for its `code`. This is what makes a
|
|
646
|
+
* `defineTool` **shippable**: bundled into a plugin manifest (see
|
|
647
|
+
* {@link inlineToolRunnable} / `definePlugin({ tools })`), Core runs it in the
|
|
648
|
+
* Deno sandbox, so the tool ships NEW behavior instead of only aliasing an
|
|
649
|
+
* existing tool.
|
|
650
|
+
*
|
|
651
|
+
* IMPORTANT: like a hook body, the serialized function is **self-contained** —
|
|
652
|
+
* it runs in a fresh sandbox with only `input` (the call arguments) and `host`
|
|
653
|
+
* (the capability bridge: `host.sideModel` / `host.storage` / `host.log`, each
|
|
654
|
+
* gated by the plugin's grants) in scope. It cannot capture outer variables,
|
|
655
|
+
* imports, or closures, and `ctx.gateway` is **not** available in the sandbox
|
|
656
|
+
* — a shipped tool reaches models through `host.sideModel`. When run in-process
|
|
657
|
+
* via {@link ToolRunnable.run} the normal `(input, ctx)` contract still holds;
|
|
658
|
+
* the sandbox form is the second parameter aliased to `host`.
|
|
659
|
+
*/
|
|
660
|
+
readonly code: string;
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Create a Runnable tool with input validation.
|
|
664
|
+
*
|
|
665
|
+
* The returned value satisfies `ToolRunnable<TInput, TOutput>` (which extends
|
|
666
|
+
* `Runnable`) with `kind = "tool"`. The `schema` property is the JSON Schema
|
|
667
|
+
* descriptor passed in options — forwarding it to Core's `ToolInfo.schema`
|
|
668
|
+
* requires no transformation.
|
|
669
|
+
*
|
|
670
|
+
* Input is validated at `run()` time: missing required fields or type
|
|
671
|
+
* mismatches throw before the tool body executes.
|
|
672
|
+
*
|
|
673
|
+
* @example
|
|
674
|
+
* ```ts
|
|
675
|
+
* const searchTool = defineTool({
|
|
676
|
+
* id: "tool-web-search",
|
|
677
|
+
* name: "Web Search",
|
|
678
|
+
* schema: {
|
|
679
|
+
* type: "object",
|
|
680
|
+
* properties: { query: { type: "string", description: "Search query" } },
|
|
681
|
+
* required: ["query"],
|
|
682
|
+
* },
|
|
683
|
+
* async run({ query }, _ctx) {
|
|
684
|
+
* return { results: [`Result for: ${query}`] };
|
|
685
|
+
* },
|
|
686
|
+
* });
|
|
687
|
+
* // schema is Core-compatible:
|
|
688
|
+
* console.log(searchTool.schema); // { type: "object", properties: { query: ... }, required: [...] }
|
|
689
|
+
* ```
|
|
690
|
+
*/
|
|
691
|
+
declare function defineTool<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput = unknown>(options: ToolOptions<TInput, TOutput>): ToolRunnable<TInput, TOutput>;
|
|
692
|
+
/**
|
|
693
|
+
* Convert a {@link ToolRunnable} into a `plugin.json` `kind:"tool"` runnable that
|
|
694
|
+
* ships its `run` body as Core's `inline_deno` backend. The emitted config
|
|
695
|
+
* mirrors Core's `ToolConfig` (`apps/core/src/plugin_manifest/schema.rs`):
|
|
696
|
+
* `{ slug, backend:"inline_deno", code, description?, input_schema }`. Core
|
|
697
|
+
* registers it as `app__<slug>` — discoverable via `/api/tools/search` and
|
|
698
|
+
* executed in the grant-gated sandbox.
|
|
699
|
+
*
|
|
700
|
+
* The plugin must declare the `tool:execute` grant (see `definePlugin`).
|
|
701
|
+
*/
|
|
702
|
+
declare function inlineToolRunnable(tool: ToolRunnable, options?: {
|
|
703
|
+
description?: string;
|
|
704
|
+
}): RunnableMeta;
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Tool resolution + execution for the Ryu agent runtime.
|
|
708
|
+
*
|
|
709
|
+
* Two kinds of tool feed the same model `tools[]` array:
|
|
710
|
+
*
|
|
711
|
+
* - **Local** tools are `ToolRunnable`s from `defineTool` — they run in-process
|
|
712
|
+
* via their `run(input, ctx)` implementation.
|
|
713
|
+
* - **Remote** tools are references to existing Ryu tools (e.g.
|
|
714
|
+
* `composio__GMAIL_SEARCH_EMAILS`) created with `ryuTool(id)`. Their schema is
|
|
715
|
+
* lazily fetched from Core `GET /api/tools/describe` and they execute through
|
|
716
|
+
* Core `POST /api/mcp/tools/call`, which enforces the agent's allowlist and
|
|
717
|
+
* selects the Composio connected-account entity via `user_id`.
|
|
718
|
+
*
|
|
719
|
+
* The model-facing function name is the **config key** the developer chose (e.g.
|
|
720
|
+
* `gmailSearch`), not the raw Composio slug — internals stay hidden and names
|
|
721
|
+
* stay OpenAI-safe.
|
|
722
|
+
*/
|
|
723
|
+
|
|
724
|
+
/** A reference to an existing Ryu tool, resolved + executed via Core. */
|
|
725
|
+
interface RemoteToolRef {
|
|
726
|
+
/** One-line description shown to the model (overrides Core's describe). */
|
|
727
|
+
description?: string;
|
|
728
|
+
/** Fully-qualified Ryu tool id, e.g. `composio__GMAIL_SEARCH_EMAILS`. */
|
|
729
|
+
id: string;
|
|
730
|
+
readonly kind: "remote";
|
|
731
|
+
/**
|
|
732
|
+
* JSON Schema for the tool's arguments. Optional: when omitted, a permissive
|
|
733
|
+
* open-object schema is used (Composio `describe` is shallow), so supplying
|
|
734
|
+
* this materially improves the model's tool-call accuracy.
|
|
735
|
+
*/
|
|
736
|
+
parameters?: Record<string, unknown>;
|
|
737
|
+
}
|
|
738
|
+
/** Options accepted by `ryuTool`. */
|
|
739
|
+
interface RyuToolOptions {
|
|
740
|
+
description?: string;
|
|
741
|
+
parameters?: Record<string, unknown>;
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Reference an existing Ryu tool by id so an `Agent` can call it.
|
|
745
|
+
*
|
|
746
|
+
* @example
|
|
747
|
+
* ```ts
|
|
748
|
+
* ryuTool("composio__GMAIL_SEARCH_EMAILS", {
|
|
749
|
+
* description: "Search the user's Gmail",
|
|
750
|
+
* parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
|
751
|
+
* });
|
|
752
|
+
* ```
|
|
753
|
+
*/
|
|
754
|
+
declare function ryuTool(id: string, opts?: RyuToolOptions): RemoteToolRef;
|
|
755
|
+
/** A tool an `Agent` can expose to the model: local runnable or remote ref. */
|
|
756
|
+
type AgentTool = RemoteToolRef | ToolRunnable;
|
|
757
|
+
/** Everything the tool layer needs to resolve schemas + execute calls. */
|
|
758
|
+
interface ToolExecContext {
|
|
759
|
+
/** Core agent id — REQUIRED for remote tools (governs execution). */
|
|
760
|
+
agentId?: string;
|
|
761
|
+
/** Core base URL (no trailing `/api`). */
|
|
762
|
+
coreBaseUrl: string;
|
|
763
|
+
/** Bearer token for Core (`RYU_TOKEN`); may be undefined on loopback dev. */
|
|
764
|
+
coreToken?: string;
|
|
765
|
+
/** RunnableContext handed to local tools so they may call the gateway. */
|
|
766
|
+
runnableContext: RunnableContext;
|
|
767
|
+
/** Abort signal. */
|
|
768
|
+
signal?: AbortSignal;
|
|
769
|
+
/** Composio connected-account entity selector. */
|
|
770
|
+
userId?: string;
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Build the OpenAI `tools[]` array the model sees, keyed by the developer's
|
|
774
|
+
* config names. Remote tools without an explicit `parameters` schema are
|
|
775
|
+
* described from Core (`describe` is shallow) and given a permissive schema.
|
|
776
|
+
*/
|
|
777
|
+
declare function resolveToolDefs(tools: Record<string, AgentTool>, ctx: ToolExecContext): Promise<ToolFunctionDef[]>;
|
|
778
|
+
/** Result of executing one tool call. */
|
|
779
|
+
interface ToolExecResult {
|
|
780
|
+
/** Raw tool output (already JSON-parsed when the tool returned JSON). */
|
|
781
|
+
output: unknown;
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Execute a single model tool call by config `name`, dispatching to the local
|
|
785
|
+
* runnable or the Core `/api/mcp/tools/call` endpoint.
|
|
786
|
+
*
|
|
787
|
+
* `argsJson` is the raw JSON string from `tool_call.function.arguments`.
|
|
788
|
+
*/
|
|
789
|
+
declare function executeTool(name: string, argsJson: string, tools: Record<string, AgentTool>, ctx: ToolExecContext): Promise<ToolExecResult>;
|
|
790
|
+
/** The connect-required envelope Core returns when an account isn't linked. */
|
|
791
|
+
interface Elicitation {
|
|
792
|
+
kind?: string;
|
|
793
|
+
message?: string;
|
|
794
|
+
url?: string;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Detect Ryu's connection-required envelope in a tool output. Mirrors Core's
|
|
798
|
+
* `detect_elicitation` (apps/core/src/sidecar/mcp/composio.rs): the first Gmail
|
|
799
|
+
* call for an unconnected account returns `{ "__ryu_elicitation__": { url } }`.
|
|
800
|
+
*/
|
|
801
|
+
declare function detectElicitation(output: unknown): Elicitation | null;
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* The autonomous agent loop for the Ryu SDK runtime.
|
|
805
|
+
*
|
|
806
|
+
* This is what `defineAgent` never had: a real multi-turn tool-calling loop that
|
|
807
|
+
* runs in TypeScript. Each round calls the node's gateway with the resolved
|
|
808
|
+
* tool definitions; if the model emits `tool_calls`, each is executed (local
|
|
809
|
+
* runnable or Core `/api/mcp/tools/call`), results are fed back, and the loop
|
|
810
|
+
* repeats until the model stops calling tools or `maxSteps` is reached.
|
|
811
|
+
*
|
|
812
|
+
* Emitted events mirror Core's `AcpEvent` categories (see cli/dev.ts) plus an
|
|
813
|
+
* `auth_required` pause — when a remote tool returns Ryu's connection-required
|
|
814
|
+
* envelope (first-run Gmail OAuth), the loop surfaces the connect URL and stops
|
|
815
|
+
* instead of feeding the envelope back as a normal tool result.
|
|
816
|
+
*/
|
|
817
|
+
|
|
818
|
+
/** A streamed text fragment from the assistant. */
|
|
819
|
+
interface AgentEventText {
|
|
820
|
+
content: string;
|
|
821
|
+
type: "text";
|
|
822
|
+
}
|
|
823
|
+
/** The model initiated a tool call. */
|
|
824
|
+
interface AgentEventToolCall {
|
|
825
|
+
id: string;
|
|
826
|
+
input: unknown;
|
|
827
|
+
name: string;
|
|
828
|
+
type: "tool_call";
|
|
829
|
+
}
|
|
830
|
+
/** A tool finished (or failed with an error output the model can recover from). */
|
|
831
|
+
interface AgentEventToolResult {
|
|
832
|
+
id: string;
|
|
833
|
+
name: string;
|
|
834
|
+
output: unknown;
|
|
835
|
+
type: "tool_result";
|
|
836
|
+
}
|
|
837
|
+
/** A remote tool needs an account connection — the loop paused. */
|
|
838
|
+
interface AgentEventAuthRequired {
|
|
839
|
+
message?: string;
|
|
840
|
+
tool: string;
|
|
841
|
+
type: "auth_required";
|
|
842
|
+
url?: string;
|
|
843
|
+
}
|
|
844
|
+
/** A fatal loop error — the stream ends after this. */
|
|
845
|
+
interface AgentEventError {
|
|
846
|
+
message: string;
|
|
847
|
+
type: "error";
|
|
848
|
+
}
|
|
849
|
+
/** Terminal event carrying the final text, step count, and aggregate usage. */
|
|
850
|
+
interface AgentEventResult {
|
|
851
|
+
steps: number;
|
|
852
|
+
text: string;
|
|
853
|
+
type: "result";
|
|
854
|
+
usage?: ModelUsage;
|
|
855
|
+
}
|
|
856
|
+
/** Union of everything the loop yields. */
|
|
857
|
+
type AgentEvent = AgentEventAuthRequired | AgentEventError | AgentEventResult | AgentEventText | AgentEventToolCall | AgentEventToolResult;
|
|
858
|
+
/** Inputs for a single loop run. */
|
|
859
|
+
interface LoopConfig {
|
|
860
|
+
/** Gateway base URL for inference (the target node). */
|
|
861
|
+
gatewayBaseUrl: string;
|
|
862
|
+
/** Gateway bearer token. */
|
|
863
|
+
gatewayToken?: string;
|
|
864
|
+
/** Hard ceiling on model→tool rounds. */
|
|
865
|
+
maxSteps: number;
|
|
866
|
+
/** Seed transcript (system + user messages already assembled). */
|
|
867
|
+
messages: LoopMessage[];
|
|
868
|
+
/** Model id routed by the gateway. */
|
|
869
|
+
model: string;
|
|
870
|
+
/** Abort signal. */
|
|
871
|
+
signal?: AbortSignal;
|
|
872
|
+
/** Context for resolving + executing tools. */
|
|
873
|
+
toolCtx: ToolExecContext;
|
|
874
|
+
/** Tools keyed by model-facing name. */
|
|
875
|
+
tools: Record<string, AgentTool>;
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Run the autonomous loop, yielding events as they occur. The generator returns
|
|
879
|
+
* after a terminal `result`, `auth_required`, or `error` event.
|
|
880
|
+
*/
|
|
881
|
+
declare function runAgentLoop(config: LoopConfig): AsyncGenerator<AgentEvent>;
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* `Agent` — the declarative agent runtime for `@ryu/sdk`.
|
|
885
|
+
*
|
|
886
|
+
* Unlike `defineAgent` (a declaration wrapper whose `run()` you write by hand),
|
|
887
|
+
* `Agent` OWNS the loop: give it instructions, a model, a target node, and a
|
|
888
|
+
* set of tools, then call `generate()` / `stream()`. Inference is pointed at the
|
|
889
|
+
* node's gateway; tool calls resolve to local `defineTool` runnables or existing
|
|
890
|
+
* Ryu tools (`ryuTool`) executed through Core.
|
|
891
|
+
*
|
|
892
|
+
* Mirrors Mastra's config-object + `.generate()`/`.stream()` shape; `query()`
|
|
893
|
+
* (see query.ts) wraps the same runtime in a Claude-Agent-SDK-style streaming
|
|
894
|
+
* call.
|
|
895
|
+
*/
|
|
896
|
+
|
|
897
|
+
/** A target node for inference / tool execution: a base URL + optional token. */
|
|
898
|
+
interface Endpoint {
|
|
899
|
+
baseUrl?: string;
|
|
900
|
+
token?: string;
|
|
901
|
+
}
|
|
902
|
+
/** Declarative configuration for an `Agent`. */
|
|
903
|
+
interface AgentConfig {
|
|
904
|
+
/** Core agent id — REQUIRED when using `ryuTool` remote tools (governance). */
|
|
905
|
+
agentId?: string;
|
|
906
|
+
/** Core endpoint for tool discovery/execution. Defaults to env/localhost. */
|
|
907
|
+
core?: Endpoint;
|
|
908
|
+
/** System prompt / persona. */
|
|
909
|
+
instructions?: string;
|
|
910
|
+
/** Hard ceiling on model→tool rounds (default 10). */
|
|
911
|
+
maxSteps?: number;
|
|
912
|
+
/** Model id routed by the node's gateway. */
|
|
913
|
+
model: string;
|
|
914
|
+
/** Display name. */
|
|
915
|
+
name: string;
|
|
916
|
+
/** Target node for inference. Defaults to the local gateway. */
|
|
917
|
+
node?: Endpoint;
|
|
918
|
+
/**
|
|
919
|
+
* Reverse-domain plugin id. When set, the composable primitive surface
|
|
920
|
+
* (`ctx.rag`, `ctx.memory`, `ctx.engines`, …) is mounted on the run context,
|
|
921
|
+
* routed through this node via the governed host bridge / capability broker.
|
|
922
|
+
* Omitted = no primitives mounted (the bridge families authenticate a plugin
|
|
923
|
+
* id, so a half-wired transport is never attached).
|
|
924
|
+
*/
|
|
925
|
+
pluginId?: string;
|
|
926
|
+
/** Tools keyed by the model-facing name. */
|
|
927
|
+
tools?: Record<string, AgentTool>;
|
|
928
|
+
/** Composio connected-account entity selector. */
|
|
929
|
+
userId?: string;
|
|
930
|
+
}
|
|
931
|
+
/** Result of a non-streaming `generate()`. */
|
|
932
|
+
interface GenerateResult {
|
|
933
|
+
/** Present when the run paused for an account connection. */
|
|
934
|
+
authRequired?: AgentEventAuthRequired;
|
|
935
|
+
/** Number of model→tool rounds taken. */
|
|
936
|
+
steps: number;
|
|
937
|
+
/** Final assistant text. */
|
|
938
|
+
text: string;
|
|
939
|
+
/** Aggregate token usage across all rounds (when reported). */
|
|
940
|
+
usage?: ModelUsage;
|
|
941
|
+
}
|
|
942
|
+
/** A declarative, loop-owning agent. */
|
|
943
|
+
declare class Agent {
|
|
944
|
+
readonly config: AgentConfig;
|
|
945
|
+
constructor(config: AgentConfig);
|
|
946
|
+
/** Assemble the loop config for a given prompt. */
|
|
947
|
+
private buildLoopConfig;
|
|
948
|
+
/** Stream loop events (text / tool_call / tool_result / auth_required / …). */
|
|
949
|
+
stream(prompt: string, signal?: AbortSignal): AsyncGenerator<AgentEvent>;
|
|
950
|
+
/** Run to completion and return the final text, step count, and usage. */
|
|
951
|
+
generate(prompt: string, signal?: AbortSignal): Promise<GenerateResult>;
|
|
952
|
+
}
|
|
953
|
+
/** Factory alias for `new Agent(config)`. */
|
|
954
|
+
declare function createAgent(config: AgentConfig): Agent;
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* `query()` — Claude-Agent-SDK-style streaming entry over the same `Agent`.
|
|
958
|
+
*
|
|
959
|
+
* Where `Agent` gives you Mastra's config-object + method shape, `query()` gives
|
|
960
|
+
* you the `for await (const msg of query({ prompt, options }))` ergonomics of
|
|
961
|
+
* the Claude Agent SDK. Both drive the identical loop; this is a thin wrapper.
|
|
962
|
+
*/
|
|
963
|
+
|
|
964
|
+
/** Options accepted by `query` — an `AgentConfig` with an optional `name`. */
|
|
965
|
+
type QueryOptions = Omit<AgentConfig, "name"> & {
|
|
966
|
+
name?: string;
|
|
967
|
+
};
|
|
968
|
+
/** Input to `query`: a prompt plus agent options. */
|
|
969
|
+
interface QueryInput {
|
|
970
|
+
options: QueryOptions;
|
|
971
|
+
prompt: string;
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Run an agent for a single prompt and stream its events.
|
|
975
|
+
*
|
|
976
|
+
* @example
|
|
977
|
+
* ```ts
|
|
978
|
+
* for await (const msg of query({
|
|
979
|
+
* prompt: "Find my expenses from last month.",
|
|
980
|
+
* options: { model: "gpt-4o", agentId: "agent-expense", tools: { gmailSearch } },
|
|
981
|
+
* })) {
|
|
982
|
+
* if (msg.type === "result") console.log(msg.text);
|
|
983
|
+
* }
|
|
984
|
+
* ```
|
|
985
|
+
*/
|
|
986
|
+
declare function query(input: QueryInput): AsyncGenerator<AgentEvent>;
|
|
987
|
+
|
|
988
|
+
export { type Elicitation as $, Agent as A, defineModel as B, type ChatDelta as C, type DurableClient as D, type Endpoint as E, defineTool as F, type GatewayClient as G, type HttpPrimitiveTransportOptions as H, type ImageClient as I, type JsonSchemaProperty as J, httpPrimitiveTransport as K, inlineToolRunnable as L, type MemoryClient as M, query as N, ryuTool as O, PRIMITIVE_BINDINGS as P, type QueryInput as Q, type Runnable as R, type SttClient as S, type ToolRunnable as T, type AgentEventAuthRequired as U, type AgentEventError as V, type AgentEventResult as W, type AgentEventText as X, type AgentEventToolCall as Y, type AgentEventToolResult as Z, type AssistantMessage as _, type RunnableContext as a, type LoopConfig as a0, type LoopMessage as a1, type ModelCallOptions as a2, type ModelCallResult as a3, type ModelUsage as a4, type RyuToolOptions as a5, type ToolCall as a6, type ToolExecContext as a7, type ToolExecResult as a8, type ToolFunctionDef as a9, callModelWithTools as aa, detectElicitation as ab, executeTool as ac, resolveToolDefs as ad, runAgentLoop as ae, type AgentConfig as b, type AgentEvent as c, type AgentTool as d, type ChatMessage as e, type ChatResult as f, type EnginesClient as g, type GenerateResult as h, type MemoryItem as i, ModelClient as j, type ModelClientOptions as k, type PrimitiveBinding as l, type PrimitiveTransport as m, type QueryOptions as n, type RagChunk as o, type RagClient as p, type RagRerankResult as q, type RealtimeClient as r, type RealtimeSubscription as s, type RemoteToolRef as t, type RyuPrimitives as u, type ToolOptions as v, type ToolSchema as w, type TtsClient as x, createAgent as y, createPrimitives as z };
|