@ericsanchezok/synergy-plugin 1.2.16 → 1.2.18

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/dist/example.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { Plugin } from "./index";
1
+ import type { Plugin } from "./index";
2
2
  export declare const ExamplePlugin: Plugin;
3
3
  export default ExamplePlugin;
package/dist/example.js CHANGED
@@ -26,63 +26,69 @@ function preview(text, limit = 160) {
26
26
  return singleLine;
27
27
  return `${singleLine.slice(0, limit - 1)}…`;
28
28
  }
29
- export const ExamplePlugin = async (ctx) => {
30
- const logger = ctx.$.env({ SYNERGY_LOG_LEVEL: "warn" });
31
- return {
32
- tool: {
33
- workspace_summary: tool({
34
- description: "Summarize the active Synergy plugin runtime context",
35
- args: {
36
- includeDirectorySample: tool.schema.boolean().optional().describe("Include a short directory listing sample"),
37
- },
38
- async execute(args, context) {
39
- const lines = [
40
- `scope: ${ctx.scope.type}:${ctx.scope.id}`,
41
- `directory: ${ctx.directory}`,
42
- `worktree: ${ctx.worktree}`,
43
- `server: ${ctx.serverUrl.toString()}`,
44
- `session: ${context.sessionID}`,
45
- `agent: ${context.agent}`,
46
- ];
47
- if (args.includeDirectorySample) {
48
- const result = await logger `ls`;
49
- const sample = result
50
- .text()
51
- .split("\n")
52
- .map((line) => line.trim())
53
- .filter(Boolean)
54
- .slice(0, 10);
55
- if (sample.length > 0) {
56
- lines.push(`entries: ${sample.join(", ")}`);
29
+ export const ExamplePlugin = {
30
+ id: "example",
31
+ name: "Example Plugin",
32
+ async init(ctx) {
33
+ const logger = ctx.$.env({ SYNERGY_LOG_LEVEL: "warn" });
34
+ return {
35
+ tool: {
36
+ workspace_summary: tool({
37
+ description: "Summarize the active Synergy plugin runtime context",
38
+ args: {
39
+ includeDirectorySample: tool.schema
40
+ .boolean()
41
+ .optional()
42
+ .describe("Include a short directory listing sample"),
43
+ },
44
+ async execute(args, context) {
45
+ const lines = [
46
+ `scope: ${ctx.scope.type}:${ctx.scope.id}`,
47
+ `directory: ${ctx.directory}`,
48
+ `worktree: ${ctx.worktree}`,
49
+ `server: ${ctx.serverUrl.toString()}`,
50
+ `session: ${context.sessionID}`,
51
+ `agent: ${context.agent}`,
52
+ ];
53
+ if (args.includeDirectorySample) {
54
+ const result = await logger `ls`;
55
+ const sample = result
56
+ .text()
57
+ .split("\n")
58
+ .map((line) => line.trim())
59
+ .filter(Boolean)
60
+ .slice(0, 10);
61
+ if (sample.length > 0) {
62
+ lines.push(`entries: ${sample.join(", ")}`);
63
+ }
57
64
  }
58
- }
59
- return lines.join("\n");
60
- },
61
- }),
62
- },
63
- async "session.turn.after"(input) {
64
- const status = input.error ? "error" : (input.finish ?? "unknown");
65
- const summary = {
66
- sessionID: input.sessionID,
67
- assistantMessageID: input.assistantMessageID,
68
- agent: input.assistant.agent,
69
- finish: status,
70
- };
71
- console.info("[example-plugin] session.turn.after", summary);
72
- },
73
- async "note.create.before"(_, output) {
74
- output.note.title = output.note.title.trim() || "Untitled note";
75
- output.note.tags = mergeTags(output.note.tags);
76
- if (!output.note.contentText?.trim()) {
77
- output.note.contentText = preview(output.note.title);
78
- }
79
- },
80
- async "engram.memory.search.after"(input, output) {
81
- output.results = output.results
82
- .slice()
83
- .sort((left, right) => right.similarity - left.similarity)
84
- .slice(0, input.topK);
85
- },
86
- };
65
+ return lines.join("\n");
66
+ },
67
+ }),
68
+ },
69
+ async "session.turn.after"(input) {
70
+ const status = input.error ? "error" : (input.finish ?? "unknown");
71
+ console.info("[example-plugin] session.turn.after", {
72
+ sessionID: input.sessionID,
73
+ assistantMessageID: input.assistantMessageID,
74
+ agent: input.assistant.agent,
75
+ finish: status,
76
+ });
77
+ },
78
+ async "note.create.before"(_, output) {
79
+ output.note.title = output.note.title.trim() || "Untitled note";
80
+ output.note.tags = mergeTags(output.note.tags);
81
+ if (!output.note.contentText?.trim()) {
82
+ output.note.contentText = preview(output.note.title);
83
+ }
84
+ },
85
+ async "engram.memory.search.after"(input, output) {
86
+ output.results = output.results
87
+ .slice()
88
+ .sort((left, right) => right.similarity - left.similarity)
89
+ .slice(0, input.topK);
90
+ },
91
+ };
92
+ },
87
93
  };
88
94
  export default ExamplePlugin;
package/dist/index.d.ts CHANGED
@@ -1,38 +1,59 @@
1
1
  import type { AgendaItem, AgendaRunLog, CortexTask, Event, createSynergyClient, MemoryCategory, MemoryRecallMode, MemorySearchResult, Model, NoteCreateInput, NoteInfo, NotePatchInput, Provider, PermissionRequest, UserMessage, Message, Part, Auth, Config } from "@ericsanchezok/synergy-sdk";
2
2
  import type { BunShell } from "./shell";
3
- import { type ToolDefinition } from "./tool";
3
+ import type { ToolDefinition, ToolResult } from "./tool";
4
4
  export * from "./tool";
5
+ export type { ToolResult };
6
+ export interface PluginConfigAccessor {
7
+ /** Get the plugin's full config object */
8
+ get(): Promise<Record<string, any>>;
9
+ /** Set one or more config values (deep-merged into the plugin's namespace) */
10
+ set(values: Record<string, any>): Promise<void>;
11
+ }
12
+ export interface PluginAuthStore {
13
+ /** Read a credential by key */
14
+ get(key: string): Promise<string | undefined>;
15
+ /** Persist a credential (encrypted at rest) */
16
+ set(key: string, value: string): Promise<void>;
17
+ /** Remove a credential */
18
+ delete(key: string): Promise<void>;
19
+ /** Check if a credential exists */
20
+ has(key: string): Promise<boolean>;
21
+ }
22
+ export interface PluginCacheStore {
23
+ /** Read a cached value */
24
+ get<T = unknown>(key: string): Promise<T | undefined>;
25
+ /** Write a cached value with optional TTL in milliseconds */
26
+ set(key: string, value: unknown, ttl?: number): Promise<void>;
27
+ /** Remove a cached value */
28
+ delete(key: string): Promise<void>;
29
+ /** Absolute path to this plugin's cache directory */
30
+ directory: string;
31
+ }
32
+ export interface PluginCLICommand {
33
+ description: string;
34
+ options?: Record<string, {
35
+ type: "string" | "boolean" | "number";
36
+ description?: string;
37
+ required?: boolean;
38
+ }>;
39
+ execute(args: Record<string, any>): Promise<string | void>;
40
+ }
41
+ export interface PluginCLIGroup {
42
+ description: string;
43
+ subcommands: Record<string, PluginCLICommand>;
44
+ }
45
+ export type PluginCLIEntry = PluginCLICommand | PluginCLIGroup;
46
+ export interface PluginSkill {
47
+ name: string;
48
+ description: string;
49
+ content: string;
50
+ references?: Record<string, string>;
51
+ }
5
52
  export type ProviderContext = {
6
53
  source: "env" | "config" | "custom" | "api";
7
54
  info: Provider;
8
55
  options: Record<string, any>;
9
56
  };
10
- export type PluginInput = {
11
- client: ReturnType<typeof createSynergyClient>;
12
- scope: {
13
- type: "global" | "project";
14
- id: string;
15
- directory: string;
16
- worktree: string;
17
- vcs?: "git";
18
- name?: string;
19
- icon?: {
20
- url?: string;
21
- color?: string;
22
- };
23
- sandboxes?: string[];
24
- time?: {
25
- created: number;
26
- updated: number;
27
- initialized?: number;
28
- };
29
- };
30
- directory: string;
31
- worktree: string;
32
- serverUrl: URL;
33
- $: BunShell;
34
- };
35
- export type Plugin = (input: PluginInput) => Promise<Hooks>;
36
57
  export type AuthHook = {
37
58
  provider: string;
38
59
  loader?: (auth: () => Promise<Auth>, provider: Provider) => Promise<Record<string, any>>;
@@ -120,19 +141,61 @@ export type AuthOuathResult = {
120
141
  type: "failed";
121
142
  }>;
122
143
  });
123
- export interface Hooks {
124
- event?: (input: {
125
- event: Event;
126
- }) => Promise<void>;
127
- config?: (input: Config) => Promise<void>;
128
- tool?: {
129
- [key: string]: ToolDefinition;
144
+ export type PluginInput = {
145
+ client: ReturnType<typeof createSynergyClient>;
146
+ scope: {
147
+ type: "global" | "project";
148
+ id: string;
149
+ directory: string;
150
+ worktree: string;
151
+ vcs?: "git";
152
+ name?: string;
153
+ icon?: {
154
+ url?: string;
155
+ color?: string;
156
+ };
157
+ sandboxes?: string[];
158
+ time?: {
159
+ created: number;
160
+ updated: number;
161
+ initialized?: number;
162
+ };
130
163
  };
164
+ directory: string;
165
+ worktree: string;
166
+ serverUrl: URL;
167
+ $: BunShell;
168
+ config: PluginConfigAccessor;
169
+ auth: PluginAuthStore;
170
+ cache: PluginCacheStore;
171
+ };
172
+ export interface Plugin {
173
+ /** Unique identifier for this plugin (used as config/auth/cache namespace) */
174
+ id: string;
175
+ /** Human-readable display name */
176
+ name?: string;
177
+ /** Initialize the plugin and return hooks */
178
+ init(input: PluginInput): Promise<PluginHooks>;
179
+ }
180
+ export interface PluginHooks {
181
+ /** Called when the plugin is being unloaded (e.g. runtime reload) */
182
+ dispose?(): Promise<void>;
183
+ /** Register CLI commands under `synergy <pluginId> ...` */
184
+ cli?: Record<string, PluginCLIEntry>;
185
+ /** Register skills that become available when this plugin is loaded */
186
+ skills?: PluginSkill[];
187
+ /** Register custom tools */
188
+ tool?: Record<string, ToolDefinition>;
189
+ /** Provider auth integration */
131
190
  auth?: AuthHook;
132
- /**
133
- * Called when a new message is received
134
- */
135
- "chat.message"?: (input: {
191
+ /** Observe runtime bus events */
192
+ event?(input: {
193
+ event: Event;
194
+ }): Promise<void>;
195
+ /** Observe the loaded runtime config */
196
+ config?(input: Config): Promise<void>;
197
+ /** Rewrite incoming user messages */
198
+ "chat.message"?(input: {
136
199
  sessionID: string;
137
200
  agent?: string;
138
201
  model?: {
@@ -144,11 +207,9 @@ export interface Hooks {
144
207
  }, output: {
145
208
  message: UserMessage;
146
209
  parts: Part[];
147
- }) => Promise<void>;
148
- /**
149
- * Modify parameters sent to LLM
150
- */
151
- "chat.params"?: (input: {
210
+ }): Promise<void>;
211
+ /** Modify LLM parameters */
212
+ "chat.params"?(input: {
152
213
  sessionID: string;
153
214
  agent: string;
154
215
  model: Model;
@@ -159,18 +220,21 @@ export interface Hooks {
159
220
  topP: number;
160
221
  topK: number;
161
222
  options: Record<string, any>;
162
- }) => Promise<void>;
163
- "permission.ask"?: (input: PermissionRequest, output: {
223
+ }): Promise<void>;
224
+ /** Override permission decisions */
225
+ "permission.ask"?(input: PermissionRequest, output: {
164
226
  status: "ask" | "deny" | "allow";
165
- }) => Promise<void>;
166
- "tool.execute.before"?: (input: {
227
+ }): Promise<void>;
228
+ /** Rewrite tool args before execution */
229
+ "tool.execute.before"?(input: {
167
230
  tool: string;
168
231
  sessionID: string;
169
232
  callID: string;
170
233
  }, output: {
171
234
  args: any;
172
- }) => Promise<void>;
173
- "tool.execute.after"?: (input: {
235
+ }): Promise<void>;
236
+ /** Rewrite tool output after execution */
237
+ "tool.execute.after"?(input: {
174
238
  tool: string;
175
239
  sessionID: string;
176
240
  callID: string;
@@ -178,48 +242,48 @@ export interface Hooks {
178
242
  title: string;
179
243
  output: string;
180
244
  metadata: any;
181
- }) => Promise<void>;
182
- "experimental.chat.messages.transform"?: (input: {}, output: {
245
+ }): Promise<void>;
246
+ /** Rewrite chat message history sent to model */
247
+ "experimental.chat.messages.transform"?(input: {}, output: {
183
248
  messages: {
184
249
  info: Message;
185
250
  parts: Part[];
186
251
  }[];
187
- }) => Promise<void>;
188
- "experimental.chat.system.transform"?: (input: {}, output: {
252
+ }): Promise<void>;
253
+ /** Rewrite the assembled system prompt */
254
+ "experimental.chat.system.transform"?(input: {}, output: {
189
255
  system: string[];
190
- }) => Promise<void>;
191
- /**
192
- * Called before session compaction starts. Allows plugins to customize
193
- * the compaction prompt.
194
- *
195
- * - `context`: Additional context strings appended to the default prompt
196
- * - `prompt`: If set, replaces the default compaction prompt entirely
197
- */
198
- "experimental.session.compacting"?: (input: {
256
+ }): Promise<void>;
257
+ /** Customize session compaction */
258
+ "experimental.session.compacting"?(input: {
199
259
  sessionID: string;
200
260
  }, output: {
201
261
  context: string[];
202
262
  prompt?: string;
203
- }) => Promise<void>;
204
- "experimental.text.complete"?: (input: {
263
+ }): Promise<void>;
264
+ /** Rewrite text completion output */
265
+ "experimental.text.complete"?(input: {
205
266
  sessionID: string;
206
267
  messageID: string;
207
268
  partID: string;
208
269
  }, output: {
209
270
  text: string;
210
- }) => Promise<void>;
211
- "session.turn.after"?: (input: {
271
+ }): Promise<void>;
272
+ /** Observe completed assistant turns */
273
+ "session.turn.after"?(input: {
212
274
  sessionID: string;
213
275
  userMessageID: string;
214
276
  assistantMessageID: string;
215
277
  assistant: Message;
216
278
  finish?: string;
217
279
  error?: unknown;
218
- }, output: {}) => Promise<void>;
219
- "cortex.task.after"?: (input: {
280
+ }, output: {}): Promise<void>;
281
+ /** Observe completed Cortex tasks */
282
+ "cortex.task.after"?(input: {
220
283
  task: CortexTask;
221
- }, output: {}) => Promise<void>;
222
- "agenda.run.before"?: (input: {
284
+ }, output: {}): Promise<void>;
285
+ /** Skip or rewrite agenda run */
286
+ "agenda.run.before"?(input: {
223
287
  signal: {
224
288
  type: string;
225
289
  source: string;
@@ -231,8 +295,9 @@ export interface Hooks {
231
295
  }, output: {
232
296
  skip: boolean;
233
297
  item: AgendaItem;
234
- }) => Promise<void>;
235
- "agenda.run.after"?: (input: {
298
+ }): Promise<void>;
299
+ /** Observe successful agenda runs */
300
+ "agenda.run.after"?(input: {
236
301
  signal: {
237
302
  type: string;
238
303
  source: string;
@@ -242,8 +307,9 @@ export interface Hooks {
242
307
  item: AgendaItem;
243
308
  run: AgendaRunLog;
244
309
  scopeID: string;
245
- }, output: {}) => Promise<void>;
246
- "agenda.run.error"?: (input: {
310
+ }, output: {}): Promise<void>;
311
+ /** Observe failed agenda runs */
312
+ "agenda.run.error"?(input: {
247
313
  signal: {
248
314
  type: string;
249
315
  source: string;
@@ -254,32 +320,37 @@ export interface Hooks {
254
320
  scopeID: string;
255
321
  error: string;
256
322
  sessionID?: string;
257
- }, output: {}) => Promise<void>;
258
- "note.create.before"?: (input: {
323
+ }, output: {}): Promise<void>;
324
+ /** Rewrite note creation input */
325
+ "note.create.before"?(input: {
259
326
  scopeID: string;
260
327
  }, output: {
261
328
  note: NoteCreateInput;
262
- }) => Promise<void>;
263
- "note.create.after"?: (input: {
329
+ }): Promise<void>;
330
+ /** Observe created notes */
331
+ "note.create.after"?(input: {
264
332
  scopeID: string;
265
333
  noteID: string;
266
334
  }, output: {
267
335
  note: NoteInfo;
268
- }) => Promise<void>;
269
- "note.update.before"?: (input: {
336
+ }): Promise<void>;
337
+ /** Rewrite note update patches */
338
+ "note.update.before"?(input: {
270
339
  scopeID: string;
271
340
  noteID: string;
272
341
  current: NoteInfo;
273
342
  }, output: {
274
343
  patch: NotePatchInput;
275
- }) => Promise<void>;
276
- "note.update.after"?: (input: {
344
+ }): Promise<void>;
345
+ /** Observe updated notes */
346
+ "note.update.after"?(input: {
277
347
  scopeID: string;
278
348
  noteID: string;
279
349
  }, output: {
280
350
  note: NoteInfo;
281
- }) => Promise<void>;
282
- "note.search.before"?: (input: {
351
+ }): Promise<void>;
352
+ /** Rewrite note search filters */
353
+ "note.search.before"?(input: {
283
354
  scopeID: string;
284
355
  }, output: {
285
356
  pattern: string;
@@ -288,28 +359,32 @@ export interface Hooks {
288
359
  before?: string;
289
360
  tags?: string[];
290
361
  pinned?: boolean;
291
- }) => Promise<void>;
292
- "note.search.after"?: (input: {
362
+ }): Promise<void>;
363
+ /** Filter or reorder note search results */
364
+ "note.search.after"?(input: {
293
365
  scopeID: string;
294
366
  pattern: string;
295
367
  }, output: {
296
368
  notes: NoteInfo[];
297
- }) => Promise<void>;
298
- "engram.memory.search.before"?: (input: {}, output: {
369
+ }): Promise<void>;
370
+ /** Rewrite memory search query */
371
+ "engram.memory.search.before"?(input: {}, output: {
299
372
  query: string;
300
373
  vector?: number[];
301
374
  topK?: number;
302
375
  categories?: MemoryCategory[];
303
376
  recallModes?: MemoryRecallMode[];
304
377
  rerank?: boolean;
305
- }) => Promise<void>;
306
- "engram.memory.search.after"?: (input: {
378
+ }): Promise<void>;
379
+ /** Filter or reorder memory results */
380
+ "engram.memory.search.after"?(input: {
307
381
  query: string;
308
382
  topK: number;
309
383
  }, output: {
310
384
  results: MemorySearchResult[];
311
- }) => Promise<void>;
312
- "engram.experience.encode.after"?: (input: {
385
+ }): Promise<void>;
386
+ /** Observe experience encoding outcomes */
387
+ "engram.experience.encode.after"?(input: {
313
388
  sessionID: string;
314
389
  userMessageID: string;
315
390
  }, output: {
@@ -317,5 +392,5 @@ export interface Hooks {
317
392
  skipped: boolean;
318
393
  duplicateOf?: string;
319
394
  experienceID?: string;
320
- }) => Promise<void>;
395
+ }): Promise<void>;
321
396
  }
package/dist/tool.d.ts CHANGED
@@ -5,14 +5,19 @@ export type ToolContext = {
5
5
  agent: string;
6
6
  abort: AbortSignal;
7
7
  };
8
+ export interface ToolResult {
9
+ title?: string;
10
+ output: string;
11
+ metadata?: Record<string, any>;
12
+ }
8
13
  export declare function tool<Args extends z.ZodRawShape>(input: {
9
14
  description: string;
10
15
  args: Args;
11
- execute(args: z.infer<z.ZodObject<Args>>, context: ToolContext): Promise<string>;
16
+ execute(args: z.infer<z.ZodObject<Args>>, context: ToolContext): Promise<string | ToolResult>;
12
17
  }): {
13
18
  description: string;
14
19
  args: Args;
15
- execute(args: z.infer<z.ZodObject<Args>>, context: ToolContext): Promise<string>;
20
+ execute(args: z.infer<z.ZodObject<Args>>, context: ToolContext): Promise<string | ToolResult>;
16
21
  };
17
22
  export declare namespace tool {
18
23
  var schema: typeof z;
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": "1.2.16",
4
+ "version": "1.2.18",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -34,13 +34,13 @@
34
34
  "dist"
35
35
  ],
36
36
  "dependencies": {
37
- "@ericsanchezok/synergy-sdk": "workspace:*",
38
- "zod": "catalog:"
37
+ "@ericsanchezok/synergy-sdk": "1.2.18",
38
+ "zod": "4.1.8"
39
39
  },
40
40
  "devDependencies": {
41
- "@tsconfig/node22": "catalog:",
42
- "@types/node": "catalog:",
43
- "typescript": "catalog:",
44
- "@typescript/native-preview": "catalog:"
41
+ "@tsconfig/node22": "22.0.2",
42
+ "@types/node": "22.13.9",
43
+ "typescript": "5.8.2",
44
+ "@typescript/native-preview": "7.0.0-dev.20251207.1"
45
45
  }
46
46
  }