@ericsanchezok/synergy-plugin 2.2.2 → 2.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 +111 -0
- package/dist/display.d.ts +15 -0
- package/dist/display.js +0 -0
- package/dist/hooks.js +6 -0
- package/dist/index.d.ts +120 -0
- package/dist/manifest.d.ts +60 -0
- package/dist/manifest.js +62 -1
- package/dist/tool.d.ts +86 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -61,6 +61,114 @@ export default plugin
|
|
|
61
61
|
|
|
62
62
|
There is no compatibility layer for legacy descriptor shapes. `plugin.json.name` must match `plugin.id`; Synergy fails validation or loading if they differ.
|
|
63
63
|
|
|
64
|
+
## Tool Results And Attachments
|
|
65
|
+
|
|
66
|
+
Tools can return user-facing files through `attachments`. Use the generated SDK `asset.upload()` route or the public `/asset` endpoint to upload binary data, then return the resulting `asset://...` URL. Do not import Synergy internal asset modules from a plugin.
|
|
67
|
+
|
|
68
|
+
For visual tools whose output should appear as the main answer instead of a tool card, set `metadata.display.presentation` to `artifact-only` and list the attachment ids to promote:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
return {
|
|
72
|
+
output: "",
|
|
73
|
+
metadata: {
|
|
74
|
+
display: {
|
|
75
|
+
presentation: "artifact-only",
|
|
76
|
+
primaryAttachmentIds: [partId],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
attachments: [
|
|
80
|
+
{
|
|
81
|
+
id: partId,
|
|
82
|
+
sessionID: context.sessionID,
|
|
83
|
+
messageID: context.messageID,
|
|
84
|
+
type: "file",
|
|
85
|
+
mime: "image/svg+xml",
|
|
86
|
+
filename: "result.svg",
|
|
87
|
+
url: uploaded.url,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Running and failed tool states still render normally, so progress, approvals, and errors remain visible.
|
|
94
|
+
|
|
95
|
+
For image, video, or audio generation tools, declare the display protocol on the tool definition as well. This lets Synergy show its built-in media generation placeholder as soon as the tool starts, then replace it with the promoted attachment when the tool completes:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
const mediaDisplay = {
|
|
99
|
+
kind: "media-generation",
|
|
100
|
+
visibility: "media",
|
|
101
|
+
presentation: "artifact-only",
|
|
102
|
+
media: {
|
|
103
|
+
type: "image",
|
|
104
|
+
actionLabel: "Create image",
|
|
105
|
+
pendingTitle: "Generating image",
|
|
106
|
+
pendingDescription: "Preparing the image...",
|
|
107
|
+
promptField: "prompt",
|
|
108
|
+
aspectRatio: "1:1",
|
|
109
|
+
},
|
|
110
|
+
} as const
|
|
111
|
+
|
|
112
|
+
tool({
|
|
113
|
+
description: "Generate an image",
|
|
114
|
+
display: mediaDisplay,
|
|
115
|
+
args: {
|
|
116
|
+
prompt: tool.schema.string(),
|
|
117
|
+
},
|
|
118
|
+
async execute(args, context) {
|
|
119
|
+
// Upload the generated image, then return metadata.display with primaryAttachmentIds.
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Use `visibility: "media"` for tools whose running and completed success states should be represented by the media surface instead of the ordinary tool transcript. Error states still fall back to normal tool cards.
|
|
125
|
+
|
|
126
|
+
## Internal Tools And Delegated Tasks
|
|
127
|
+
|
|
128
|
+
Plugins can register helper tools that are only available to a controlled delegated task by setting `exposure: { mode: "internal" }`. Internal tools are not visible to the primary agent, resident tool lists, grouped tools, or `search_tools`; Synergy can still enable them explicitly for a delegated subagent run.
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
tool({
|
|
132
|
+
description: "Validate a private planning result",
|
|
133
|
+
exposure: { mode: "internal" },
|
|
134
|
+
args: {
|
|
135
|
+
choice: tool.schema.string(),
|
|
136
|
+
},
|
|
137
|
+
async execute(args) {
|
|
138
|
+
return { output: JSON.stringify({ choice: args.choice }) }
|
|
139
|
+
},
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Use `context.task.run()` when a public plugin tool needs Synergy's existing Cortex delegation flow. The host always fills `parentSessionID`, `parentMessageID`, and `executionRole: "delegated_subagent"`; plugins cannot forge those fields.
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
const plan = await context.task?.run({
|
|
147
|
+
subagent: "my-plugin-planner",
|
|
148
|
+
description: "Plan the plugin result",
|
|
149
|
+
prompt: "Choose a valid plan and return JSON.",
|
|
150
|
+
tools: {
|
|
151
|
+
"*": false,
|
|
152
|
+
"plugin__my-plugin__private_helper": true,
|
|
153
|
+
},
|
|
154
|
+
visibility: "hidden",
|
|
155
|
+
timeoutMs: 30_000,
|
|
156
|
+
output: {
|
|
157
|
+
mode: "structured",
|
|
158
|
+
schema: {
|
|
159
|
+
type: "object",
|
|
160
|
+
required: ["choice"],
|
|
161
|
+
properties: {
|
|
162
|
+
choice: { type: "string" },
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
maxRepairTurns: 3,
|
|
166
|
+
},
|
|
167
|
+
})
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
When `output.mode` is `structured`, Cortex validates the child task result against the schema and may run repair turns before completing. Cortex still stores its normal task trajectory summary in `task.result`; the structured value is returned to the plugin call site as `plan.outputResult.data`.
|
|
171
|
+
|
|
64
172
|
## Plugin Input
|
|
65
173
|
|
|
66
174
|
`init(input)` receives runtime services scoped to the active Synergy Scope:
|
|
@@ -107,6 +215,9 @@ Each distributable plugin has a root `plugin.json`:
|
|
|
107
215
|
"name": "greet",
|
|
108
216
|
"title": "Greet",
|
|
109
217
|
"description": "Greet a user by name",
|
|
218
|
+
"display": {
|
|
219
|
+
"kind": "default",
|
|
220
|
+
},
|
|
110
221
|
"capabilities": {
|
|
111
222
|
"filesystem": "none",
|
|
112
223
|
"network": false,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ToolMediaDisplay {
|
|
2
|
+
type: "image" | "video" | "audio";
|
|
3
|
+
actionLabel?: string;
|
|
4
|
+
pendingTitle?: string;
|
|
5
|
+
pendingDescription?: string;
|
|
6
|
+
promptField?: string;
|
|
7
|
+
aspectRatio?: "1:1" | "4:3" | "16:9" | "auto";
|
|
8
|
+
}
|
|
9
|
+
export interface ToolDisplay {
|
|
10
|
+
kind?: "default" | "media-generation";
|
|
11
|
+
visibility?: "default" | "media" | "hidden-unless-error";
|
|
12
|
+
presentation?: "default" | "artifact-only";
|
|
13
|
+
media?: ToolMediaDisplay;
|
|
14
|
+
primaryAttachmentIds?: string[];
|
|
15
|
+
}
|
package/dist/display.js
ADDED
|
File without changes
|
package/dist/hooks.js
CHANGED
|
@@ -11,6 +11,12 @@ export const HOOKS = [
|
|
|
11
11
|
mutatesOutput: false,
|
|
12
12
|
summary: "Add provider auth methods and auth loaders",
|
|
13
13
|
},
|
|
14
|
+
{
|
|
15
|
+
name: "provider",
|
|
16
|
+
category: "core",
|
|
17
|
+
mutatesOutput: false,
|
|
18
|
+
summary: "Register provider catalog/runtime profiles",
|
|
19
|
+
},
|
|
14
20
|
{
|
|
15
21
|
name: "config",
|
|
16
22
|
category: "core",
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { AgendaItem, AgendaRunLog, CortexTask, Event, createSynergyClient,
|
|
|
2
2
|
import type { BunShell } from "./shell";
|
|
3
3
|
import type { ToolDefinition, ToolResult } from "./tool";
|
|
4
4
|
export * from "./tool";
|
|
5
|
+
export type { ToolDisplay, ToolMediaDisplay } from "./display";
|
|
5
6
|
export type { ToolResult };
|
|
6
7
|
export * from "./manifest";
|
|
7
8
|
export type { BunShell, BunShellOutput, BunShellPromise, ShellExpression, ShellFunction } from "./shell";
|
|
@@ -150,8 +151,43 @@ export type AuthHook = {
|
|
|
150
151
|
} | {
|
|
151
152
|
type: "failed";
|
|
152
153
|
}>;
|
|
154
|
+
} | {
|
|
155
|
+
type: "import";
|
|
156
|
+
label: string;
|
|
157
|
+
prompts?: Array<{
|
|
158
|
+
type: "text";
|
|
159
|
+
key: string;
|
|
160
|
+
message: string;
|
|
161
|
+
placeholder?: string;
|
|
162
|
+
validate?: (value: string) => string | undefined;
|
|
163
|
+
condition?: (inputs: Record<string, string>) => boolean;
|
|
164
|
+
} | {
|
|
165
|
+
type: "select";
|
|
166
|
+
key: string;
|
|
167
|
+
message: string;
|
|
168
|
+
options: Array<{
|
|
169
|
+
label: string;
|
|
170
|
+
value: string;
|
|
171
|
+
hint?: string;
|
|
172
|
+
}>;
|
|
173
|
+
condition?: (inputs: Record<string, string>) => boolean;
|
|
174
|
+
}>;
|
|
175
|
+
import(inputs?: Record<string, string>): Promise<AuthImportResult>;
|
|
153
176
|
})[];
|
|
154
177
|
};
|
|
178
|
+
export type AuthImportResult = ({
|
|
179
|
+
type: "success";
|
|
180
|
+
provider?: string;
|
|
181
|
+
} & ({
|
|
182
|
+
refresh: string;
|
|
183
|
+
access: string;
|
|
184
|
+
expires: number;
|
|
185
|
+
} | {
|
|
186
|
+
key: string;
|
|
187
|
+
})) | {
|
|
188
|
+
type: "failed";
|
|
189
|
+
message?: string;
|
|
190
|
+
};
|
|
155
191
|
export type AuthOuathResult = {
|
|
156
192
|
url: string;
|
|
157
193
|
instructions: string;
|
|
@@ -184,6 +220,88 @@ export type AuthOuathResult = {
|
|
|
184
220
|
type: "failed";
|
|
185
221
|
}>;
|
|
186
222
|
});
|
|
223
|
+
export type ProviderProfileHook = {
|
|
224
|
+
id: string;
|
|
225
|
+
name: string;
|
|
226
|
+
aliases?: string[];
|
|
227
|
+
description?: string;
|
|
228
|
+
signupUrl?: string;
|
|
229
|
+
env?: string[];
|
|
230
|
+
baseURL?: string;
|
|
231
|
+
modelsURL?: string;
|
|
232
|
+
apiMode?: "chat_completions" | "responses" | "anthropic_messages" | "codex_responses" | "external_process";
|
|
233
|
+
authKind?: "api_key" | "oauth" | "oauth_external" | "copilot" | "wellknown" | "none";
|
|
234
|
+
aiSdkPackage?: string;
|
|
235
|
+
modelFactory?: "languageModel" | "openaiResponses" | "openaiChat" | "openaiResponsesUnlessCompletionUrls" | "call" | "copilotAuto" | "anthropicMessages";
|
|
236
|
+
modelsDevProviderID?: string;
|
|
237
|
+
fallbackModels?: string[];
|
|
238
|
+
defaultAuxModel?: string;
|
|
239
|
+
usageKind?: "codex" | "anthropic-oauth" | "openrouter" | "unsupported";
|
|
240
|
+
healthCheck?: "models" | "none";
|
|
241
|
+
requestQuirks?: string[];
|
|
242
|
+
autoload?: (input: {
|
|
243
|
+
providerID: string;
|
|
244
|
+
auth?: Auth;
|
|
245
|
+
provider?: Provider;
|
|
246
|
+
}) => Promise<boolean>;
|
|
247
|
+
resolveAuth?: (input: {
|
|
248
|
+
providerID: string;
|
|
249
|
+
auth?: Auth;
|
|
250
|
+
provider?: Provider;
|
|
251
|
+
}) => Promise<Auth | undefined>;
|
|
252
|
+
refreshAuth?: (input: {
|
|
253
|
+
providerID: string;
|
|
254
|
+
auth?: Auth;
|
|
255
|
+
provider?: Provider;
|
|
256
|
+
}) => Promise<Auth | undefined>;
|
|
257
|
+
buildHeaders?: (input: {
|
|
258
|
+
providerID: string;
|
|
259
|
+
auth?: Auth;
|
|
260
|
+
url: string;
|
|
261
|
+
headers: Headers;
|
|
262
|
+
body?: unknown;
|
|
263
|
+
}) => Promise<Record<string, string> | Headers | undefined>;
|
|
264
|
+
rewriteBody?: (input: {
|
|
265
|
+
providerID: string;
|
|
266
|
+
auth?: Auth;
|
|
267
|
+
url: string;
|
|
268
|
+
headers: Headers;
|
|
269
|
+
body?: unknown;
|
|
270
|
+
}) => Promise<unknown>;
|
|
271
|
+
modelOptions?: (input: {
|
|
272
|
+
providerID: string;
|
|
273
|
+
auth?: Auth;
|
|
274
|
+
provider?: Provider;
|
|
275
|
+
}) => Promise<Record<string, any>>;
|
|
276
|
+
classifyError?: (input: {
|
|
277
|
+
providerID: string;
|
|
278
|
+
status?: number;
|
|
279
|
+
error?: unknown;
|
|
280
|
+
body?: unknown;
|
|
281
|
+
}) => {
|
|
282
|
+
code: string;
|
|
283
|
+
retryable: boolean;
|
|
284
|
+
reloginRequired?: boolean;
|
|
285
|
+
exhausted?: boolean;
|
|
286
|
+
cooldownUntil?: number;
|
|
287
|
+
resetAt?: number;
|
|
288
|
+
} | undefined;
|
|
289
|
+
runtimeOptions?: (input: {
|
|
290
|
+
providerID: string;
|
|
291
|
+
auth?: Auth;
|
|
292
|
+
provider?: Provider;
|
|
293
|
+
}) => Promise<Record<string, any>>;
|
|
294
|
+
getModel?: (input: {
|
|
295
|
+
sdk: any;
|
|
296
|
+
modelID: string;
|
|
297
|
+
options?: Record<string, any>;
|
|
298
|
+
}) => Promise<any>;
|
|
299
|
+
fetchModels?: (input: {
|
|
300
|
+
auth?: Auth;
|
|
301
|
+
fetch?: typeof fetch;
|
|
302
|
+
baseURL?: string;
|
|
303
|
+
}) => Promise<string[]>;
|
|
304
|
+
};
|
|
187
305
|
export type PluginInput = {
|
|
188
306
|
client: ReturnType<typeof createSynergyClient>;
|
|
189
307
|
scope: {
|
|
@@ -235,6 +353,8 @@ export interface PluginHooks {
|
|
|
235
353
|
tool?: Record<string, ToolDefinition>;
|
|
236
354
|
/** Provider auth integration */
|
|
237
355
|
auth?: AuthHook;
|
|
356
|
+
/** Provider runtime/catalog profiles */
|
|
357
|
+
provider?: ProviderProfileHook | ProviderProfileHook[];
|
|
238
358
|
/** Observe runtime bus events */
|
|
239
359
|
event?(input: {
|
|
240
360
|
event: Event;
|
package/dist/manifest.d.ts
CHANGED
|
@@ -38,6 +38,10 @@ export declare const PluginManifest: z.ZodObject<{
|
|
|
38
38
|
invoke: "invoke";
|
|
39
39
|
spawn: "spawn";
|
|
40
40
|
}>>;
|
|
41
|
+
task: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
42
|
+
agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
43
|
+
maxRuntimeMs: z.ZodOptional<z.ZodNumber>;
|
|
44
|
+
}, z.core.$strict>]>>;
|
|
41
45
|
}, z.core.$strip>>;
|
|
42
46
|
data: z.ZodOptional<z.ZodObject<{
|
|
43
47
|
session: z.ZodDefault<z.ZodEnum<{
|
|
@@ -53,6 +57,7 @@ export declare const PluginManifest: z.ZodObject<{
|
|
|
53
57
|
config: z.ZodDefault<z.ZodEnum<{
|
|
54
58
|
plugin: "plugin";
|
|
55
59
|
global: "global";
|
|
60
|
+
none: "none";
|
|
56
61
|
}>>;
|
|
57
62
|
secrets: z.ZodDefault<z.ZodEnum<{
|
|
58
63
|
none: "none";
|
|
@@ -107,6 +112,54 @@ export declare const PluginManifest: z.ZodObject<{
|
|
|
107
112
|
icon: z.ZodOptional<z.ZodString>;
|
|
108
113
|
category: z.ZodOptional<z.ZodString>;
|
|
109
114
|
kind: z.ZodOptional<z.ZodString>;
|
|
115
|
+
exposure: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
116
|
+
mode: z.ZodLiteral<"resident">;
|
|
117
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
118
|
+
mode: z.ZodLiteral<"group">;
|
|
119
|
+
group: z.ZodString;
|
|
120
|
+
title: z.ZodOptional<z.ZodString>;
|
|
121
|
+
description: z.ZodOptional<z.ZodString>;
|
|
122
|
+
whenToExpand: z.ZodOptional<z.ZodString>;
|
|
123
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
124
|
+
mode: z.ZodLiteral<"search">;
|
|
125
|
+
title: z.ZodOptional<z.ZodString>;
|
|
126
|
+
keywords: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
127
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
128
|
+
mode: z.ZodLiteral<"internal">;
|
|
129
|
+
}, z.core.$strict>], "mode">>;
|
|
130
|
+
display: z.ZodOptional<z.ZodObject<{
|
|
131
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
132
|
+
default: "default";
|
|
133
|
+
"media-generation": "media-generation";
|
|
134
|
+
}>>;
|
|
135
|
+
visibility: z.ZodOptional<z.ZodEnum<{
|
|
136
|
+
default: "default";
|
|
137
|
+
media: "media";
|
|
138
|
+
"hidden-unless-error": "hidden-unless-error";
|
|
139
|
+
}>>;
|
|
140
|
+
presentation: z.ZodOptional<z.ZodEnum<{
|
|
141
|
+
default: "default";
|
|
142
|
+
"artifact-only": "artifact-only";
|
|
143
|
+
}>>;
|
|
144
|
+
media: z.ZodOptional<z.ZodObject<{
|
|
145
|
+
type: z.ZodEnum<{
|
|
146
|
+
image: "image";
|
|
147
|
+
video: "video";
|
|
148
|
+
audio: "audio";
|
|
149
|
+
}>;
|
|
150
|
+
actionLabel: z.ZodOptional<z.ZodString>;
|
|
151
|
+
pendingTitle: z.ZodOptional<z.ZodString>;
|
|
152
|
+
pendingDescription: z.ZodOptional<z.ZodString>;
|
|
153
|
+
promptField: z.ZodOptional<z.ZodString>;
|
|
154
|
+
aspectRatio: z.ZodOptional<z.ZodEnum<{
|
|
155
|
+
"1:1": "1:1";
|
|
156
|
+
"4:3": "4:3";
|
|
157
|
+
"16:9": "16:9";
|
|
158
|
+
auto: "auto";
|
|
159
|
+
}>>;
|
|
160
|
+
}, z.core.$strict>>;
|
|
161
|
+
primaryAttachmentIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
162
|
+
}, z.core.$strict>>;
|
|
110
163
|
capabilities: z.ZodOptional<z.ZodObject<{
|
|
111
164
|
filesystem: z.ZodOptional<z.ZodEnum<{
|
|
112
165
|
read: "read";
|
|
@@ -151,6 +204,8 @@ export declare const PluginManifest: z.ZodObject<{
|
|
|
151
204
|
all: "all";
|
|
152
205
|
}>>;
|
|
153
206
|
model: z.ZodOptional<z.ZodString>;
|
|
207
|
+
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
208
|
+
permission: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
154
209
|
}, z.core.$strip>>>>;
|
|
155
210
|
mcp: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
156
211
|
defaults: z.ZodOptional<z.ZodObject<{
|
|
@@ -300,6 +355,10 @@ export declare const PluginManifest: z.ZodObject<{
|
|
|
300
355
|
invoke: "invoke";
|
|
301
356
|
spawn: "spawn";
|
|
302
357
|
}>>;
|
|
358
|
+
task: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
359
|
+
agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
360
|
+
maxRuntimeMs: z.ZodOptional<z.ZodNumber>;
|
|
361
|
+
}, z.core.$strict>]>>;
|
|
303
362
|
}, z.core.$strip>>;
|
|
304
363
|
data: z.ZodOptional<z.ZodObject<{
|
|
305
364
|
session: z.ZodDefault<z.ZodEnum<{
|
|
@@ -315,6 +374,7 @@ export declare const PluginManifest: z.ZodObject<{
|
|
|
315
374
|
config: z.ZodDefault<z.ZodEnum<{
|
|
316
375
|
plugin: "plugin";
|
|
317
376
|
global: "global";
|
|
377
|
+
none: "none";
|
|
318
378
|
}>>;
|
|
319
379
|
secrets: z.ZodDefault<z.ZodEnum<{
|
|
320
380
|
none: "none";
|
package/dist/manifest.js
CHANGED
|
@@ -87,6 +87,62 @@ const UICommandDef = z
|
|
|
87
87
|
icon: z.string().optional(),
|
|
88
88
|
})
|
|
89
89
|
.strict();
|
|
90
|
+
const ToolExposureDef = z.discriminatedUnion("mode", [
|
|
91
|
+
z
|
|
92
|
+
.object({
|
|
93
|
+
mode: z.literal("resident"),
|
|
94
|
+
})
|
|
95
|
+
.strict(),
|
|
96
|
+
z
|
|
97
|
+
.object({
|
|
98
|
+
mode: z.literal("group"),
|
|
99
|
+
group: z.string().min(1),
|
|
100
|
+
title: z.string().optional(),
|
|
101
|
+
description: z.string().optional(),
|
|
102
|
+
whenToExpand: z.string().optional(),
|
|
103
|
+
})
|
|
104
|
+
.strict(),
|
|
105
|
+
z
|
|
106
|
+
.object({
|
|
107
|
+
mode: z.literal("search"),
|
|
108
|
+
title: z.string().optional(),
|
|
109
|
+
keywords: z.array(z.string()).optional(),
|
|
110
|
+
})
|
|
111
|
+
.strict(),
|
|
112
|
+
z
|
|
113
|
+
.object({
|
|
114
|
+
mode: z.literal("internal"),
|
|
115
|
+
})
|
|
116
|
+
.strict(),
|
|
117
|
+
]);
|
|
118
|
+
const TaskPermissionDef = z.union([
|
|
119
|
+
z.boolean(),
|
|
120
|
+
z
|
|
121
|
+
.object({
|
|
122
|
+
agents: z.array(z.string().min(1)).optional(),
|
|
123
|
+
maxRuntimeMs: z.number().int().positive().optional(),
|
|
124
|
+
})
|
|
125
|
+
.strict(),
|
|
126
|
+
]);
|
|
127
|
+
const ToolDisplayDef = z
|
|
128
|
+
.object({
|
|
129
|
+
kind: z.enum(["default", "media-generation"]).optional(),
|
|
130
|
+
visibility: z.enum(["default", "media", "hidden-unless-error"]).optional(),
|
|
131
|
+
presentation: z.enum(["default", "artifact-only"]).optional(),
|
|
132
|
+
media: z
|
|
133
|
+
.object({
|
|
134
|
+
type: z.enum(["image", "video", "audio"]),
|
|
135
|
+
actionLabel: z.string().min(1).max(80).optional(),
|
|
136
|
+
pendingTitle: z.string().min(1).max(120).optional(),
|
|
137
|
+
pendingDescription: z.string().min(1).max(200).optional(),
|
|
138
|
+
promptField: z.string().min(1).max(64).optional(),
|
|
139
|
+
aspectRatio: z.enum(["1:1", "4:3", "16:9", "auto"]).optional(),
|
|
140
|
+
})
|
|
141
|
+
.strict()
|
|
142
|
+
.optional(),
|
|
143
|
+
primaryAttachmentIds: z.array(z.string().min(1)).optional(),
|
|
144
|
+
})
|
|
145
|
+
.strict();
|
|
90
146
|
const UIContribution = z
|
|
91
147
|
.object({
|
|
92
148
|
entry: z
|
|
@@ -127,6 +183,7 @@ const PluginPermissionsSchema = z
|
|
|
127
183
|
}, z.enum(["none", "read", "write"]).default("none")),
|
|
128
184
|
network: z.boolean().default(false),
|
|
129
185
|
mcp: z.enum(["none", "invoke", "spawn"]).default("none"),
|
|
186
|
+
task: TaskPermissionDef.optional(),
|
|
130
187
|
})
|
|
131
188
|
.optional(),
|
|
132
189
|
/** Data access */
|
|
@@ -134,7 +191,7 @@ const PluginPermissionsSchema = z
|
|
|
134
191
|
.object({
|
|
135
192
|
session: z.enum(["none", "metadata", "read"]).default("none"),
|
|
136
193
|
workspace: z.enum(["none", "metadata", "read"]).default("none"),
|
|
137
|
-
config: z.enum(["plugin", "global"]).default("plugin"),
|
|
194
|
+
config: z.enum(["none", "plugin", "global"]).default("plugin"),
|
|
138
195
|
secrets: z.enum(["none", "own"]).default("none"),
|
|
139
196
|
})
|
|
140
197
|
.optional(),
|
|
@@ -221,6 +278,8 @@ export const PluginManifest = z
|
|
|
221
278
|
icon: z.string().optional(),
|
|
222
279
|
category: z.string().optional(),
|
|
223
280
|
kind: z.string().optional(),
|
|
281
|
+
exposure: ToolExposureDef.optional(),
|
|
282
|
+
display: ToolDisplayDef.optional(),
|
|
224
283
|
capabilities: z
|
|
225
284
|
.object({
|
|
226
285
|
filesystem: z.enum(["none", "read", "write"]).optional(),
|
|
@@ -247,6 +306,8 @@ export const PluginManifest = z
|
|
|
247
306
|
description: z.string(),
|
|
248
307
|
mode: z.enum(["subagent", "primary", "all"]).default("subagent"),
|
|
249
308
|
model: z.string().optional(),
|
|
309
|
+
hidden: z.boolean().optional(),
|
|
310
|
+
permission: z.record(z.string(), z.any()).optional(),
|
|
250
311
|
}))
|
|
251
312
|
.optional(),
|
|
252
313
|
mcp: z
|
package/dist/tool.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import type { ToolDisplay } from "./display";
|
|
3
|
+
export type { ToolDisplay, ToolMediaDisplay } from "./display";
|
|
2
4
|
export type ToolContext = {
|
|
3
5
|
sessionID: string;
|
|
4
6
|
messageID: string;
|
|
@@ -11,11 +13,75 @@ export type ToolContext = {
|
|
|
11
13
|
patterns: string[];
|
|
12
14
|
metadata?: Record<string, any>;
|
|
13
15
|
}): Promise<void>;
|
|
16
|
+
/** Run a Synergy delegated subagent task from inside this tool. */
|
|
17
|
+
task?: ToolTaskService;
|
|
18
|
+
/** Invoke another visible/explicitly-allowed Synergy tool from inside this tool. */
|
|
19
|
+
tools?: ToolInvokeService;
|
|
20
|
+
};
|
|
21
|
+
export type ToolTaskVisibility = "visible" | "hidden";
|
|
22
|
+
export type ToolTaskOutput = {
|
|
23
|
+
mode?: "summary";
|
|
24
|
+
} | {
|
|
25
|
+
mode: "final_response";
|
|
26
|
+
} | {
|
|
27
|
+
mode: "structured";
|
|
28
|
+
schema: Record<string, unknown>;
|
|
29
|
+
maxRepairTurns?: 0 | 1 | 2 | 3;
|
|
30
|
+
};
|
|
31
|
+
export type ToolTaskOutputResult = {
|
|
32
|
+
mode: "final_response";
|
|
33
|
+
text: string;
|
|
34
|
+
} | {
|
|
35
|
+
mode: "structured";
|
|
36
|
+
status: "valid" | "invalid";
|
|
37
|
+
source?: "structured_tool" | "final_response";
|
|
38
|
+
data?: unknown;
|
|
39
|
+
text?: string;
|
|
40
|
+
repairTurns: number;
|
|
41
|
+
error?: string;
|
|
42
|
+
validationErrors?: string[];
|
|
43
|
+
};
|
|
44
|
+
export interface ToolTaskRunInput {
|
|
45
|
+
subagent: string;
|
|
46
|
+
description: string;
|
|
47
|
+
prompt: string;
|
|
48
|
+
tools?: Record<string, boolean>;
|
|
49
|
+
visibility?: ToolTaskVisibility;
|
|
50
|
+
timeoutMs?: number;
|
|
51
|
+
output?: ToolTaskOutput;
|
|
52
|
+
category?: string;
|
|
53
|
+
model?: {
|
|
54
|
+
providerID: string;
|
|
55
|
+
modelID: string;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export interface ToolTaskRunResult {
|
|
59
|
+
taskId: string;
|
|
60
|
+
sessionId: string;
|
|
61
|
+
status: "pending" | "queued" | "running" | "completed" | "error" | "cancelled" | "timeout";
|
|
62
|
+
output: string;
|
|
63
|
+
outputResult?: ToolTaskOutputResult;
|
|
64
|
+
error?: string;
|
|
65
|
+
}
|
|
66
|
+
export interface ToolTaskService {
|
|
67
|
+
run(input: ToolTaskRunInput): Promise<ToolTaskRunResult>;
|
|
68
|
+
}
|
|
69
|
+
export interface ToolInvokeInput {
|
|
70
|
+
tool: string;
|
|
71
|
+
args?: unknown;
|
|
72
|
+
timeoutMs?: number;
|
|
73
|
+
}
|
|
74
|
+
export interface ToolInvokeService {
|
|
75
|
+
invoke(input: ToolInvokeInput): Promise<ToolResult>;
|
|
76
|
+
}
|
|
77
|
+
export type ToolResultMetadata = Record<string, any> & {
|
|
78
|
+
display?: ToolDisplay;
|
|
79
|
+
primaryAttachmentIds?: string[];
|
|
14
80
|
};
|
|
15
81
|
export interface ToolResult {
|
|
16
82
|
title?: string;
|
|
17
83
|
output: string;
|
|
18
|
-
metadata?:
|
|
84
|
+
metadata?: ToolResultMetadata;
|
|
19
85
|
attachments?: Array<{
|
|
20
86
|
type: "file";
|
|
21
87
|
id: string;
|
|
@@ -27,12 +93,31 @@ export interface ToolResult {
|
|
|
27
93
|
localPath?: string;
|
|
28
94
|
}>;
|
|
29
95
|
}
|
|
96
|
+
export type ToolExposure = {
|
|
97
|
+
mode: "resident";
|
|
98
|
+
} | {
|
|
99
|
+
mode: "group";
|
|
100
|
+
group: string;
|
|
101
|
+
title?: string;
|
|
102
|
+
description?: string;
|
|
103
|
+
whenToExpand?: string;
|
|
104
|
+
} | {
|
|
105
|
+
mode: "search";
|
|
106
|
+
title?: string;
|
|
107
|
+
keywords?: string[];
|
|
108
|
+
} | {
|
|
109
|
+
mode: "internal";
|
|
110
|
+
};
|
|
30
111
|
export declare function tool<Args extends z.ZodRawShape>(input: {
|
|
31
112
|
description: string;
|
|
113
|
+
exposure?: ToolExposure;
|
|
114
|
+
display?: ToolDisplay;
|
|
32
115
|
args: Args;
|
|
33
116
|
execute(args: z.infer<z.ZodObject<Args>>, context: ToolContext): Promise<string | ToolResult>;
|
|
34
117
|
}): {
|
|
35
118
|
description: string;
|
|
119
|
+
exposure?: ToolExposure;
|
|
120
|
+
display?: ToolDisplay;
|
|
36
121
|
args: Args;
|
|
37
122
|
execute(args: z.infer<z.ZodObject<Args>>, context: ToolContext): Promise<string | ToolResult>;
|
|
38
123
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@ericsanchezok/synergy-plugin",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.4.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
"import": "./dist/tool.js",
|
|
26
26
|
"types": "./dist/tool.d.ts"
|
|
27
27
|
},
|
|
28
|
+
"./display": {
|
|
29
|
+
"import": "./dist/display.js",
|
|
30
|
+
"types": "./dist/display.d.ts"
|
|
31
|
+
},
|
|
28
32
|
"./hooks": {
|
|
29
33
|
"import": "./dist/hooks.js",
|
|
30
34
|
"types": "./dist/hooks.d.ts"
|