@ryuhq/sdk 0.1.14 → 0.2.2
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 +69 -7
- package/dist/action.cjs +839 -0
- package/dist/action.d.cts +88 -0
- package/dist/action.d.ts +88 -0
- package/dist/action.js +8 -0
- package/dist/agent-plugin.cjs +46 -0
- package/dist/agent-plugin.d.cts +43 -33
- package/dist/agent-plugin.d.ts +43 -33
- package/dist/agent-plugin.js +1 -1
- package/dist/agent.cjs +16 -0
- package/dist/agent.d.cts +377 -1
- package/dist/agent.d.ts +377 -1
- package/dist/agent.js +4 -2
- package/dist/app-B0Z9Ew_R.d.cts +141 -0
- package/dist/app-C-BDJwfG.d.ts +141 -0
- package/dist/builder.cjs +1194 -0
- package/dist/builder.d.cts +217 -0
- package/dist/builder.d.ts +217 -0
- package/dist/builder.js +29 -0
- package/dist/chunk-4TPUZDTI.js +94 -0
- package/dist/chunk-BC3A7HMO.js +164 -0
- package/dist/chunk-FZSFZOIN.js +200 -0
- package/dist/{chunk-ODFEUVPW.js → chunk-HACAGK65.js} +1 -18
- package/dist/chunk-HLKJZAFK.js +361 -0
- package/dist/chunk-IKEDLLFY.js +19 -0
- package/dist/{chunk-G6FLVEC4.js → chunk-JX6DDRXV.js} +46 -0
- package/dist/{chunk-AO2KJRDD.js → chunk-NZKVOSC2.js} +192 -3
- package/dist/chunk-QYFUNJOH.js +83 -0
- package/dist/chunk-SN2QBJUF.js +148 -0
- package/dist/chunk-TLDPEGC7.js +21 -0
- package/dist/chunk-TXSHHZF2.js +0 -0
- package/dist/chunk-VLIRNNAE.js +154 -0
- package/dist/{chunk-MTUBUPIV.js → chunk-Z57QDDJR.js} +8 -227
- package/dist/chunk-ZTJWBRUL.js +282 -0
- package/dist/cli.cjs +347 -18
- package/dist/cli.js +128 -23
- package/dist/client-D5U6ssPc.d.cts +84 -0
- package/dist/client-D5U6ssPc.d.ts +84 -0
- package/dist/index.cjs +810 -86
- package/dist/index.d.cts +19 -634
- package/dist/index.d.ts +19 -634
- package/dist/index.js +82 -699
- package/dist/manifest.cjs +200 -4
- package/dist/manifest.d.cts +260 -5
- package/dist/manifest.d.ts +260 -5
- package/dist/manifest.js +15 -1
- package/dist/mcp/client.cjs +180 -0
- package/dist/mcp/client.d.cts +49 -0
- package/dist/mcp/client.d.ts +49 -0
- package/dist/mcp/client.js +10 -0
- package/dist/mcp/server.cjs +370 -0
- package/dist/mcp/server.d.cts +127 -0
- package/dist/mcp/server.d.ts +127 -0
- package/dist/mcp/server.js +9 -0
- package/dist/mcp.cjs +376 -0
- package/dist/mcp.d.cts +2 -0
- package/dist/mcp.d.ts +2 -0
- package/dist/mcp.js +17 -0
- package/dist/model.cjs +141 -0
- package/dist/model.d.cts +33 -0
- package/dist/model.d.ts +33 -0
- package/dist/model.js +18 -0
- package/dist/plugin.cjs +46 -0
- package/dist/plugin.d.cts +215 -0
- package/dist/plugin.d.ts +215 -0
- package/dist/plugin.js +8 -0
- package/dist/runnable.cjs +1438 -0
- package/dist/runnable.d.cts +274 -0
- package/dist/runnable.d.ts +274 -0
- package/dist/runnable.js +34 -0
- package/dist/{index-B6SkaAjJ.d.ts → tool-AjkdFvhE.d.ts} +88 -461
- package/dist/{index-BvAB5eMk.d.cts → tool-CgzW92O_.d.cts} +88 -461
- package/package.json +48 -3
- package/src/agent/loop.test.ts +4 -4
- package/src/agent/tools.ts +3 -3
- package/src/agent-plugin.test.ts +58 -0
- package/src/agent-plugin.ts +109 -36
- package/src/builder.ts +9 -0
- package/src/cli/dev.test.ts +26 -47
- package/src/cli/dev.ts +10 -2
- package/src/cli-security.test.ts +109 -0
- package/src/cli.ts +141 -25
- package/src/contracts-lockstep.test.ts +16 -2
- package/src/exports-lockstep.test.ts +93 -0
- package/src/generated/plugin-manifest.ts +322 -29
- package/src/index.ts +53 -0
- package/src/manifest-schema.test.ts +30 -1
- package/src/manifest.fixtures.test.ts +22 -6
- package/src/manifest.test.ts +165 -10
- package/src/manifest.ts +454 -188
- package/src/mcp/index.ts +18 -0
- package/src/mcp/server.ts +2 -1
- package/src/model/index.ts +22 -0
- package/src/plugin/ryu-plugin.ts +82 -0
- package/src/runnable/action.test.ts +128 -0
- package/src/runnable/action.ts +202 -0
- package/src/runnable/app.test.ts +2 -0
- package/src/runnable/app.ts +50 -14
- package/src/runnable/index.ts +20 -3
- package/src/runnable/primitives.test.ts +34 -0
- package/src/runnable/primitives.ts +116 -0
- package/src/runnable/runnable-types.ts +3 -0
- package/src/runnable/tool.ts +36 -5
- package/src/runnable/turn-hook.ts +8 -3
- package/src/slash-command.test.ts +69 -0
package/src/runnable/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Runnable — the
|
|
3
|
-
* in the Ryu SDK.
|
|
2
|
+
* Runnable — the execution contract unifying Agent, Workflow, Tool, and Skill
|
|
3
|
+
* in the Ryu SDK. `defineAction` is the governed business-operation facade over
|
|
4
|
+
* the Tool kind.
|
|
4
5
|
*
|
|
5
6
|
* Design rules (from the M8 spike doc packages/sdk/README.md):
|
|
6
7
|
* - An agent may invoke a workflow as a named tool.
|
|
@@ -12,6 +13,14 @@
|
|
|
12
13
|
* import from `@ryuhq/sdk/runnable` as a single entry point.
|
|
13
14
|
*/
|
|
14
15
|
|
|
16
|
+
export type {
|
|
17
|
+
ActionAnnotations,
|
|
18
|
+
ActionEffect,
|
|
19
|
+
ActionManifestOptions,
|
|
20
|
+
ActionOptions,
|
|
21
|
+
ActionRunnable,
|
|
22
|
+
} from "./action.ts";
|
|
23
|
+
export { defineAction } from "./action.ts";
|
|
15
24
|
export type {
|
|
16
25
|
AgentCard,
|
|
17
26
|
AgentManifestOptions,
|
|
@@ -26,6 +35,8 @@ export { defineAgent } from "./agent.ts";
|
|
|
26
35
|
export type { AppToolSpec, DefineAppOptions } from "./app.ts";
|
|
27
36
|
export { appToolId, defineApp } from "./app.ts";
|
|
28
37
|
export type {
|
|
38
|
+
BackgroundClient,
|
|
39
|
+
BackgroundProcess,
|
|
29
40
|
DurableClient,
|
|
30
41
|
EnginesClient,
|
|
31
42
|
HttpPrimitiveTransportOptions,
|
|
@@ -40,6 +51,7 @@ export type {
|
|
|
40
51
|
RealtimeClient,
|
|
41
52
|
RealtimeSubscription,
|
|
42
53
|
RyuPrimitives,
|
|
54
|
+
StorageClient,
|
|
43
55
|
SttClient,
|
|
44
56
|
TtsClient,
|
|
45
57
|
} from "./primitives.ts";
|
|
@@ -55,7 +67,12 @@ export type {
|
|
|
55
67
|
} from "./runnable-types.ts";
|
|
56
68
|
export type { SkillOptions } from "./skill.ts";
|
|
57
69
|
export { defineSkill } from "./skill.ts";
|
|
58
|
-
export type {
|
|
70
|
+
export type {
|
|
71
|
+
InlineToolManifestOptions,
|
|
72
|
+
JsonSchemaProperty,
|
|
73
|
+
ToolOptions,
|
|
74
|
+
ToolSchema,
|
|
75
|
+
} from "./tool.ts";
|
|
59
76
|
export { defineTool } from "./tool.ts";
|
|
60
77
|
export type { WorkflowOptions, WorkflowStep } from "./workflow.ts";
|
|
61
78
|
export { defineWorkflow } from "./workflow.ts";
|
|
@@ -158,6 +158,35 @@ describe("createPrimitives — transport routing mirrors rpc.ts", () => {
|
|
|
158
158
|
expect(ragCall.body).toMatchObject({ op: "retrieve" });
|
|
159
159
|
});
|
|
160
160
|
|
|
161
|
+
it("storage primitives use the grant-gated app-owned KV bridge", async () => {
|
|
162
|
+
const { transport, calls } = recordingTransport();
|
|
163
|
+
const p = createPrimitives(transport);
|
|
164
|
+
await p.storage.get({ namespace: "tickets", key: "t-1" });
|
|
165
|
+
await p.storage.set({ namespace: "tickets", key: "t-1", value: "open" });
|
|
166
|
+
await p.storage.delete({ namespace: "tickets", key: "t-1" });
|
|
167
|
+
await p.storage.keys({ namespace: "tickets" });
|
|
168
|
+
await p.storage.compareAndSet({
|
|
169
|
+
namespace: "tickets",
|
|
170
|
+
key: "t-1",
|
|
171
|
+
expected: null,
|
|
172
|
+
value: "open",
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
expect(calls.map((c) => c.target)).toEqual([
|
|
176
|
+
"storage.get",
|
|
177
|
+
"storage.set",
|
|
178
|
+
"storage.delete",
|
|
179
|
+
"storage.keys",
|
|
180
|
+
"storage.compareAndSet",
|
|
181
|
+
]);
|
|
182
|
+
expect(calls.every((c) => c.op === "bridge")).toBe(true);
|
|
183
|
+
expect(calls[1]?.body).toEqual({
|
|
184
|
+
namespace: "tickets",
|
|
185
|
+
key: "t-1",
|
|
186
|
+
value: "open",
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
161
190
|
it("every declared primitive has a binding entry (no silent drift)", () => {
|
|
162
191
|
const expected = [
|
|
163
192
|
"engines.complete",
|
|
@@ -174,6 +203,11 @@ describe("createPrimitives — transport routing mirrors rpc.ts", () => {
|
|
|
174
203
|
"realtime.subscribe",
|
|
175
204
|
"durable.checkpoint",
|
|
176
205
|
"durable.resume",
|
|
206
|
+
"storage.get",
|
|
207
|
+
"storage.set",
|
|
208
|
+
"storage.delete",
|
|
209
|
+
"storage.keys",
|
|
210
|
+
"storage.compareAndSet",
|
|
177
211
|
];
|
|
178
212
|
for (const key of expected) {
|
|
179
213
|
expect(PRIMITIVE_BINDINGS[key]).toBeDefined();
|
|
@@ -316,6 +316,41 @@ export const PRIMITIVE_BINDINGS: Record<string, PrimitiveBinding> = {
|
|
|
316
316
|
method: "model.complete",
|
|
317
317
|
grant: "hook:side-model",
|
|
318
318
|
},
|
|
319
|
+
"background.list": {
|
|
320
|
+
transport: "bridge",
|
|
321
|
+
method: "background.list",
|
|
322
|
+
grant: "background:control",
|
|
323
|
+
},
|
|
324
|
+
"background.stop": {
|
|
325
|
+
transport: "bridge",
|
|
326
|
+
method: "background.stop",
|
|
327
|
+
grant: "background:control",
|
|
328
|
+
},
|
|
329
|
+
"storage.get": {
|
|
330
|
+
transport: "bridge",
|
|
331
|
+
method: "storage.get",
|
|
332
|
+
grant: "storage:kv",
|
|
333
|
+
},
|
|
334
|
+
"storage.set": {
|
|
335
|
+
transport: "bridge",
|
|
336
|
+
method: "storage.set",
|
|
337
|
+
grant: "storage:kv",
|
|
338
|
+
},
|
|
339
|
+
"storage.delete": {
|
|
340
|
+
transport: "bridge",
|
|
341
|
+
method: "storage.delete",
|
|
342
|
+
grant: "storage:kv",
|
|
343
|
+
},
|
|
344
|
+
"storage.keys": {
|
|
345
|
+
transport: "bridge",
|
|
346
|
+
method: "storage.keys",
|
|
347
|
+
grant: "storage:kv",
|
|
348
|
+
},
|
|
349
|
+
"storage.compareAndSet": {
|
|
350
|
+
transport: "bridge",
|
|
351
|
+
method: "storage.compareAndSet",
|
|
352
|
+
grant: "storage:kv",
|
|
353
|
+
},
|
|
319
354
|
// Host-direct media data-path (the host holds the node token; returns data: URLs).
|
|
320
355
|
"image.generate": {
|
|
321
356
|
transport: "direct",
|
|
@@ -468,6 +503,56 @@ export interface EnginesClient {
|
|
|
468
503
|
}): Promise<number[][]>;
|
|
469
504
|
}
|
|
470
505
|
|
|
506
|
+
/** One process visible through the shared Core background registry. */
|
|
507
|
+
export interface BackgroundProcess {
|
|
508
|
+
command: string;
|
|
509
|
+
cwd: string;
|
|
510
|
+
description?: string | null;
|
|
511
|
+
elapsed_ms: number;
|
|
512
|
+
exit_code?: number | null;
|
|
513
|
+
exit_signal?: string | null;
|
|
514
|
+
kind: string;
|
|
515
|
+
label?: string | null;
|
|
516
|
+
pid?: number | null;
|
|
517
|
+
process_id: string;
|
|
518
|
+
producer: string;
|
|
519
|
+
running: boolean;
|
|
520
|
+
shell_id?: string | null;
|
|
521
|
+
started_at: number;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/** Background-process registry and cooperative stop requests. */
|
|
525
|
+
export interface BackgroundClient {
|
|
526
|
+
list(input?: {
|
|
527
|
+
producer?: string;
|
|
528
|
+
runningOnly?: boolean;
|
|
529
|
+
}): Promise<BackgroundProcess[]>;
|
|
530
|
+
stop(input: { processId: string }): Promise<{
|
|
531
|
+
ok: boolean;
|
|
532
|
+
requested: boolean;
|
|
533
|
+
process_id: string;
|
|
534
|
+
}>;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/** Durable app-owned key/value state (`storage:kv` via the host bridge). */
|
|
538
|
+
export interface StorageClient {
|
|
539
|
+
/** Atomically replace a value when it still equals `expected`. */
|
|
540
|
+
compareAndSet(input: {
|
|
541
|
+
expected: string | null;
|
|
542
|
+
key: string;
|
|
543
|
+
namespace?: string;
|
|
544
|
+
value: string | null;
|
|
545
|
+
}): Promise<boolean>;
|
|
546
|
+
/** Delete one value. */
|
|
547
|
+
delete(input: { key: string; namespace?: string }): Promise<void>;
|
|
548
|
+
/** Read one value; missing keys return `null`. */
|
|
549
|
+
get(input: { key: string; namespace?: string }): Promise<string | null>;
|
|
550
|
+
/** List keys in an app-owned namespace. */
|
|
551
|
+
keys(input?: { namespace?: string }): Promise<string[]>;
|
|
552
|
+
/** Write one string value. JSON can be encoded by the app when needed. */
|
|
553
|
+
set(input: { key: string; namespace?: string; value: string }): Promise<void>;
|
|
554
|
+
}
|
|
555
|
+
|
|
471
556
|
/** TTS primitive — speech synthesis (`crates/ryu-tts`). */
|
|
472
557
|
export interface TtsClient {
|
|
473
558
|
/**
|
|
@@ -513,12 +598,14 @@ export interface ImageClient {
|
|
|
513
598
|
* Core node) — the same "what runs vs what is allowed" split as `ctx.gateway`.
|
|
514
599
|
*/
|
|
515
600
|
export interface RyuPrimitives {
|
|
601
|
+
background: BackgroundClient;
|
|
516
602
|
durable: DurableClient;
|
|
517
603
|
engines: EnginesClient;
|
|
518
604
|
image: ImageClient;
|
|
519
605
|
memory: MemoryClient;
|
|
520
606
|
rag: RagClient;
|
|
521
607
|
realtime: RealtimeClient;
|
|
608
|
+
storage: StorageClient;
|
|
522
609
|
stt: SttClient;
|
|
523
610
|
tts: TtsClient;
|
|
524
611
|
}
|
|
@@ -542,6 +629,35 @@ function brokerCall(
|
|
|
542
629
|
*/
|
|
543
630
|
export function createPrimitives(transport: PrimitiveTransport): RyuPrimitives {
|
|
544
631
|
return {
|
|
632
|
+
background: {
|
|
633
|
+
list: (input) =>
|
|
634
|
+
transport.bridge("background.list", {
|
|
635
|
+
producer: input?.producer,
|
|
636
|
+
running_only: input?.runningOnly,
|
|
637
|
+
}) as Promise<BackgroundProcess[]>,
|
|
638
|
+
stop: (input) =>
|
|
639
|
+
transport.bridge("background.stop", {
|
|
640
|
+
process_id: input.processId,
|
|
641
|
+
}) as Promise<{
|
|
642
|
+
ok: boolean;
|
|
643
|
+
requested: boolean;
|
|
644
|
+
process_id: string;
|
|
645
|
+
}>,
|
|
646
|
+
},
|
|
647
|
+
storage: {
|
|
648
|
+
get: (input) =>
|
|
649
|
+
transport.bridge("storage.get", input) as Promise<string | null>,
|
|
650
|
+
set: (input) =>
|
|
651
|
+
transport.bridge("storage.set", input).then(() => undefined),
|
|
652
|
+
delete: (input) =>
|
|
653
|
+
transport.bridge("storage.delete", input).then(() => undefined),
|
|
654
|
+
keys: (input) =>
|
|
655
|
+
transport
|
|
656
|
+
.bridge("storage.keys", input ?? {})
|
|
657
|
+
.then((value) => (Array.isArray(value) ? (value as string[]) : [])),
|
|
658
|
+
compareAndSet: (input) =>
|
|
659
|
+
transport.bridge("storage.compareAndSet", input) as Promise<boolean>,
|
|
660
|
+
},
|
|
545
661
|
rag: {
|
|
546
662
|
retrieve: (input) =>
|
|
547
663
|
brokerCall(transport, "rag", "retrieve", input) as Promise<RagChunk[]>,
|
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
MemoryClient,
|
|
15
15
|
RagClient,
|
|
16
16
|
RealtimeClient,
|
|
17
|
+
StorageClient,
|
|
17
18
|
SttClient,
|
|
18
19
|
TtsClient,
|
|
19
20
|
} from "./primitives.ts";
|
|
@@ -81,6 +82,8 @@ export interface RunnableContext {
|
|
|
81
82
|
sessionId?: string;
|
|
82
83
|
/** Signal to abort a long-running run. */
|
|
83
84
|
signal?: AbortSignal;
|
|
85
|
+
/** Durable app-owned KV state, gated by the manifest's `storage:kv` grant. */
|
|
86
|
+
storage?: StorageClient;
|
|
84
87
|
/** STT primitive: transcribe (`crates/ryu-stt`). */
|
|
85
88
|
stt?: SttClient;
|
|
86
89
|
/** TTS primitive: speak (`crates/ryu-tts`). */
|
package/src/runnable/tool.ts
CHANGED
|
@@ -55,12 +55,16 @@ export interface ToolSchema {
|
|
|
55
55
|
required?: string[];
|
|
56
56
|
/** Type is always "object" for a tool's top-level input schema. */
|
|
57
57
|
type: "object";
|
|
58
|
+
/** Preserve additional JSON Schema keywords for Core/MCP consumers. */
|
|
59
|
+
[key: string]: unknown;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
// ── Options ───────────────────────────────────────────────────────────────────
|
|
61
63
|
|
|
62
64
|
/** Options accepted by `defineTool`. */
|
|
63
65
|
export interface ToolOptions<TInput extends Record<string, unknown>, TOutput> {
|
|
66
|
+
/** Optional description surfaced to models and tool discovery. */
|
|
67
|
+
description?: string;
|
|
64
68
|
/** Stable unique identifier (e.g. "tool-web-search"). */
|
|
65
69
|
id: string;
|
|
66
70
|
/** Human-readable display name. */
|
|
@@ -171,6 +175,8 @@ export interface ToolRunnable<
|
|
|
171
175
|
* the sandbox form is the second parameter aliased to `host`.
|
|
172
176
|
*/
|
|
173
177
|
readonly code: string;
|
|
178
|
+
/** Optional description surfaced to models and tool discovery. */
|
|
179
|
+
readonly description?: string;
|
|
174
180
|
readonly kind: "tool";
|
|
175
181
|
/** JSON Schema for this tool's input — compatible with Core's ToolInfo.schema. */
|
|
176
182
|
readonly schema: ToolSchema;
|
|
@@ -211,7 +217,7 @@ export function defineTool<
|
|
|
211
217
|
TInput extends Record<string, unknown> = Record<string, unknown>,
|
|
212
218
|
TOutput = unknown,
|
|
213
219
|
>(options: ToolOptions<TInput, TOutput>): ToolRunnable<TInput, TOutput> {
|
|
214
|
-
const { id, name, schema, run } = options;
|
|
220
|
+
const { description, id, name, schema, run } = options;
|
|
215
221
|
|
|
216
222
|
// Serialize the run body for Core's `inline_deno` backend — the same approach
|
|
217
223
|
// `defineTurnHook` uses: the sandbox wraps this in an async IIFE where `input`
|
|
@@ -223,6 +229,7 @@ export function defineTool<
|
|
|
223
229
|
name,
|
|
224
230
|
kind: "tool",
|
|
225
231
|
schema,
|
|
232
|
+
...(description === undefined ? {} : { description }),
|
|
226
233
|
code,
|
|
227
234
|
run(input: TInput, ctx: RunnableContext): Promise<TOutput> {
|
|
228
235
|
validateInput(input, schema, id);
|
|
@@ -231,19 +238,33 @@ export function defineTool<
|
|
|
231
238
|
} satisfies ToolRunnable<TInput, TOutput>;
|
|
232
239
|
}
|
|
233
240
|
|
|
241
|
+
/** Optional metadata lowered alongside a tool's executable backend. */
|
|
242
|
+
export interface InlineToolManifestOptions {
|
|
243
|
+
/** Marks the entry as a semantic Ryu Action. */
|
|
244
|
+
action?: boolean;
|
|
245
|
+
/** MCP-style effect annotations, for example `readOnlyHint`. */
|
|
246
|
+
annotations?: Record<string, boolean | undefined>;
|
|
247
|
+
/** Description shown in Core's unified tool catalog. */
|
|
248
|
+
description?: string;
|
|
249
|
+
/** Force the human approval gate before the tool runs. */
|
|
250
|
+
needsApproval?: boolean;
|
|
251
|
+
/** JSON Schema for the structured result. */
|
|
252
|
+
outputSchema?: Record<string, unknown>;
|
|
253
|
+
}
|
|
254
|
+
|
|
234
255
|
/**
|
|
235
256
|
* Convert a {@link ToolRunnable} into a `manifest.json` `kind:"tool"` runnable that
|
|
236
257
|
* ships its `run` body as Core's `inline_deno` backend. The emitted config
|
|
237
258
|
* mirrors Core's `ToolConfig` (`apps/core/src/plugin_manifest/schema.rs`):
|
|
238
259
|
* `{ slug, backend:"inline_deno", code, description?, input_schema }`. Core
|
|
239
|
-
* registers it as `
|
|
260
|
+
* registers it as `app.<slug>` — discoverable via `/api/tools/search` and
|
|
240
261
|
* executed in the grant-gated sandbox.
|
|
241
262
|
*
|
|
242
263
|
* The plugin must declare the `tool:execute` grant (see `definePlugin`).
|
|
243
264
|
*/
|
|
244
265
|
export function inlineToolRunnable(
|
|
245
|
-
tool: ToolRunnable,
|
|
246
|
-
options?:
|
|
266
|
+
tool: Pick<ToolRunnable, "code" | "description" | "id" | "name" | "schema">,
|
|
267
|
+
options?: InlineToolManifestOptions
|
|
247
268
|
): RunnableMeta {
|
|
248
269
|
return {
|
|
249
270
|
id: tool.id,
|
|
@@ -254,7 +275,17 @@ export function inlineToolRunnable(
|
|
|
254
275
|
backend: "inline_deno",
|
|
255
276
|
code: tool.code,
|
|
256
277
|
input_schema: tool.schema,
|
|
257
|
-
...(options?.description
|
|
278
|
+
...(options?.description === undefined
|
|
279
|
+
? tool.description === undefined
|
|
280
|
+
? {}
|
|
281
|
+
: { description: tool.description }
|
|
282
|
+
: { description: options.description }),
|
|
283
|
+
...(options?.outputSchema ? { output_schema: options.outputSchema } : {}),
|
|
284
|
+
...(options?.annotations ? { annotations: options.annotations } : {}),
|
|
285
|
+
...(options?.action === undefined ? {} : { action: options.action }),
|
|
286
|
+
...(options?.needsApproval === undefined
|
|
287
|
+
? {}
|
|
288
|
+
: { needs_approval: options.needsApproval }),
|
|
258
289
|
},
|
|
259
290
|
};
|
|
260
291
|
}
|
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
HookEventContribution,
|
|
19
19
|
PluginManifest,
|
|
20
20
|
RunnableMeta,
|
|
21
|
+
SlashCommandContribution,
|
|
21
22
|
Surface,
|
|
22
23
|
TurnHookContribution,
|
|
23
24
|
} from "../manifest.ts";
|
|
@@ -144,8 +145,8 @@ export interface DefinePluginOptions {
|
|
|
144
145
|
requires?: DefineAppRequires;
|
|
145
146
|
/** Declarative settings tabs (model pickers, fields), passed verbatim. */
|
|
146
147
|
settingsTabs?: Record<string, unknown>[];
|
|
147
|
-
/** Declarative slash commands,
|
|
148
|
-
slashCommands?:
|
|
148
|
+
/** Declarative slash commands, including sequential argument choices. */
|
|
149
|
+
slashCommands?: SlashCommandContribution[];
|
|
149
150
|
/**
|
|
150
151
|
* Host surfaces this plugin runs on. **Omitted/empty = every surface** (the
|
|
151
152
|
* backward-compatible default); it never means "hidden".
|
|
@@ -154,7 +155,7 @@ export interface DefinePluginOptions {
|
|
|
154
155
|
/**
|
|
155
156
|
* Inline tools the plugin ships — each a {@link ToolRunnable} from `defineTool`
|
|
156
157
|
* whose `run` body is bundled as Core's `inline_deno` backend (registered as
|
|
157
|
-
* `
|
|
158
|
+
* `app.<tool.id>`). Shipping any tool auto-adds the `tool:execute` grant.
|
|
158
159
|
*/
|
|
159
160
|
tools?: ToolRunnable[];
|
|
160
161
|
/** Turn hooks the plugin contributes. */
|
|
@@ -173,6 +174,7 @@ export function definePlugin(options: DefinePluginOptions): PluginManifest {
|
|
|
173
174
|
turn_hooks: options.turnHooks ?? [],
|
|
174
175
|
hook_events: options.hookEvents ?? [],
|
|
175
176
|
composer_controls: options.composerControls ?? [],
|
|
177
|
+
chat_features: [],
|
|
176
178
|
settings_tabs: options.settingsTabs ?? [],
|
|
177
179
|
slash_commands: options.slashCommands ?? [],
|
|
178
180
|
lsp_servers: options.lspServers ?? {},
|
|
@@ -184,9 +186,12 @@ export function definePlugin(options: DefinePluginOptions): PluginManifest {
|
|
|
184
186
|
sidebar_sections: [],
|
|
185
187
|
sidebar_buttons: [],
|
|
186
188
|
dock_panels: [],
|
|
189
|
+
live_activities: [],
|
|
187
190
|
data_categories: [],
|
|
188
191
|
pi_extensions: [],
|
|
189
192
|
output_styles: [],
|
|
193
|
+
message_actions: [],
|
|
194
|
+
selection_actions: [],
|
|
190
195
|
};
|
|
191
196
|
// Ship each inline tool as a `kind:"tool"` runnable (Core's `inline_deno`
|
|
192
197
|
// backend). Shipping tools requires the `tool:execute` grant; add it once.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
SlashCommandContributionSchema,
|
|
4
|
+
app,
|
|
5
|
+
defineApp,
|
|
6
|
+
definePlugin,
|
|
7
|
+
} from "./index.ts";
|
|
8
|
+
|
|
9
|
+
const slashCommand = {
|
|
10
|
+
args: [
|
|
11
|
+
{
|
|
12
|
+
custom: { label: "Use another environment" },
|
|
13
|
+
name: "environment",
|
|
14
|
+
options: [
|
|
15
|
+
{ label: "Staging", value: "staging" },
|
|
16
|
+
{ label: "Production", value: "production" },
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
command: "/deploy",
|
|
21
|
+
description: "Deploy the current project",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
describe("slash command contributions", () => {
|
|
25
|
+
test("validate sequential options and custom choices", () => {
|
|
26
|
+
expect(SlashCommandContributionSchema.parse(slashCommand)).toEqual(
|
|
27
|
+
slashCommand
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("defineApp publishes slash commands", () => {
|
|
32
|
+
const manifest = defineApp({
|
|
33
|
+
id: "com.example.deploy",
|
|
34
|
+
title: "Deploy",
|
|
35
|
+
version: "1.0.0",
|
|
36
|
+
slug: "deploy",
|
|
37
|
+
uiEntry: "src/deploy.tsx",
|
|
38
|
+
tools: [{ name: "render", description: "Render deploy status" }],
|
|
39
|
+
slashCommands: [slashCommand],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(manifest.contributes?.slash_commands).toEqual([slashCommand]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("definePlugin publishes slash commands", () => {
|
|
46
|
+
const manifest = definePlugin({
|
|
47
|
+
id: "com.example.deploy-plugin",
|
|
48
|
+
name: "Deploy",
|
|
49
|
+
version: "1.0.0",
|
|
50
|
+
slashCommands: [slashCommand],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(manifest.contributes?.slash_commands).toEqual([slashCommand]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("AppBuilder publishes slash commands", () => {
|
|
57
|
+
const manifest = app()
|
|
58
|
+
.id("com.example.deploy-builder")
|
|
59
|
+
.title("Deploy")
|
|
60
|
+
.version("1.0.0")
|
|
61
|
+
.slug("deploy-builder")
|
|
62
|
+
.uiEntry("src/deploy.tsx")
|
|
63
|
+
.tool({ name: "render", description: "Render deploy status" })
|
|
64
|
+
.slashCommand(slashCommand)
|
|
65
|
+
.build();
|
|
66
|
+
|
|
67
|
+
expect(manifest.contributes?.slash_commands).toEqual([slashCommand]);
|
|
68
|
+
});
|
|
69
|
+
});
|