@ericsanchezok/synergy-util 2.4.5 → 3.0.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.
@@ -12,14 +12,6 @@ export const SYNERGY_CAPABILITY_CATEGORIES = [
12
12
  "communication",
13
13
  "platform",
14
14
  ];
15
- export function publicToolNames(manifest) {
16
- return (manifest.contributes?.tools ?? [])
17
- .filter((tool) => tool.exposure?.mode !== "internal")
18
- .map((tool) => tool.name);
19
- }
20
- export function hasPublicTools(manifest) {
21
- return publicToolNames(manifest).length > 0;
22
- }
23
15
  export const SYNERGY_CAPABILITY_DETAILS = {
24
16
  shell_read: {
25
17
  category: "runtime",
@@ -33,17 +25,30 @@ export const SYNERGY_CAPABILITY_DETAILS = {
33
25
  title: "Run shell commands",
34
26
  description: "Can execute non-destructive shell commands in the current workspace.",
35
27
  },
28
+ shell_branch_mutation: {
29
+ category: "runtime",
30
+ severity: "medium",
31
+ title: "Change active branch in workspace",
32
+ description: "Can run git checkout / git switch to change the active branch, which is safe in worktrees but can corrupt concurrent sessions on the main checkout.",
33
+ },
36
34
  shell_remote_write: {
37
35
  category: "runtime",
38
36
  severity: "medium",
39
37
  title: "Run remote-write shell commands",
40
38
  description: "Can modify existing remote state outside ordinary branch/PR publication workflows.",
41
39
  },
40
+ shell_remote_execute: {
41
+ category: "runtime",
42
+ severity: "high",
43
+ title: "Execute commands on remote host",
44
+ description: "Can run bash/process commands on a remote Synergy Link host.",
45
+ nonBypassable: true,
46
+ },
42
47
  shell_remote_publish: {
43
48
  category: "runtime",
44
49
  severity: "medium",
45
50
  title: "Publish development changes",
46
- description: "Can push explicit non-protected branches or create pull requests without destructive remote updates.",
51
+ description: "Can push explicit non-protected branches, create pull requests, comment on PRs/issues, and create issues without destructive remote updates.",
47
52
  },
48
53
  shell_destructive: {
49
54
  category: "runtime",
@@ -143,9 +148,21 @@ export const SYNERGY_CAPABILITY_DETAILS = {
143
148
  },
144
149
  task: {
145
150
  category: "tools",
146
- severity: "medium",
151
+ severity: "low",
147
152
  title: "Delegate tasks to subagents",
148
- description: "Can launch approved Synergy subagents from plugin tools.",
153
+ description: "Runtime control-profile permission to launch an approved Synergy subagent.",
154
+ },
155
+ "blueprint.delegate": {
156
+ category: "runtime",
157
+ severity: "high",
158
+ title: "Delegate Blueprint runs",
159
+ description: "Can create and control BlueprintLoop executions in an existing Session.",
160
+ },
161
+ "lightloop.delegate": {
162
+ category: "runtime",
163
+ severity: "high",
164
+ title: "Enable Light Loop",
165
+ description: "Can enable the Light Loop workflow in an existing Session.",
149
166
  },
150
167
  prompt_transform: {
151
168
  category: "hooks",
@@ -220,17 +237,30 @@ export const SYNERGY_CAPABILITY_DETAILS = {
220
237
  title: "Use browser clipboard",
221
238
  description: "Can read from or write to browser clipboard state.",
222
239
  },
240
+ browser_upload: {
241
+ category: "browser",
242
+ severity: "high",
243
+ title: "Upload files to browser",
244
+ description: "Can transfer approved workspace file content into the current browser page.",
245
+ nonBypassable: true,
246
+ },
247
+ browser_coordinate: {
248
+ category: "browser",
249
+ severity: "medium",
250
+ title: "Use browser coordinates",
251
+ description: "Can interact with a browser page using visual coordinates instead of semantic locators.",
252
+ },
223
253
  browser_download: {
224
254
  category: "browser",
225
255
  severity: "medium",
226
256
  title: "Download browser files",
227
257
  description: "Can download files through the browser workspace.",
228
258
  },
229
- browser_viewport: {
259
+ browser_emulation: {
230
260
  category: "browser",
231
261
  severity: "low",
232
- title: "Resize browser viewport",
233
- description: "Can change the browser workspace viewport.",
262
+ title: "Emulate a browser environment",
263
+ description: "Can change browser viewport, device, locale, media, CPU, and network emulation.",
234
264
  },
235
265
  identity_act: {
236
266
  category: "identity",
@@ -273,8 +303,10 @@ export const SYNERGY_PROFILE_CAPABILITIES = [
273
303
  "file_write",
274
304
  "shell_read",
275
305
  "shell",
276
- "shell_remote_write",
306
+ "shell_branch_mutation",
307
+ "shell_remote_execute",
277
308
  "shell_remote_publish",
309
+ "shell_remote_write",
278
310
  "shell_destructive",
279
311
  "shell_hardline",
280
312
  "file_external_read",
@@ -306,8 +338,10 @@ export const SYNERGY_PROFILE_CAPABILITIES = [
306
338
  "browser_eval_readonly",
307
339
  "browser_eval_trusted",
308
340
  "browser_clipboard",
341
+ "browser_upload",
342
+ "browser_coordinate",
309
343
  "browser_download",
310
- "browser_viewport",
344
+ "browser_emulation",
311
345
  ];
312
346
  export const SYNERGY_PERMISSION_CAPABILITY = {
313
347
  read: "file_read",
@@ -332,6 +366,7 @@ export const SYNERGY_PERMISSION_CAPABILITY = {
332
366
  network_request: "network_request",
333
367
  scan_document: "file_read",
334
368
  look_at: "file_read",
369
+ view_image: "file_read",
335
370
  attach: "file_read",
336
371
  ast_grep: "file_read",
337
372
  lsp: "file_read",
@@ -370,7 +405,7 @@ export const SYNERGY_PERMISSION_CAPABILITY = {
370
405
  email_read: "communication_email",
371
406
  email_send: "communication_email",
372
407
  communication_email: "communication_email",
373
- session_send: "channel_outbound",
408
+ session_send: "identity_act",
374
409
  channel_outbound: "channel_outbound",
375
410
  identity_act: "identity_act",
376
411
  platform_control: "platform_control",
@@ -381,375 +416,8 @@ export function permissionCapability(permission) {
381
416
  export function capabilityNonBypassable(capability) {
382
417
  return SYNERGY_CAPABILITY_DETAILS[capability]?.nonBypassable === true;
383
418
  }
384
- export function capabilityRisk(capability, manifest) {
385
- if (capability === "network_request" && manifest) {
386
- return (manifest.permissions?.network?.connectDomains ?? []).length > 0 ? "medium" : "high";
387
- }
388
- const details = SYNERGY_CAPABILITY_DETAILS[capability];
389
- if (details)
390
- return details.severity;
391
- return "high";
392
- }
393
- function maxRisk(current, next) {
394
- if (current === "high" || next === "high")
395
- return "high";
396
- if (current === "medium" || next === "medium")
397
- return "medium";
398
- return "low";
399
- }
400
- function sortKeys(value) {
401
- if (Array.isArray(value))
402
- return value.map(sortKeys);
403
- if (value !== null && typeof value === "object") {
404
- const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b));
405
- const result = {};
406
- for (const [key, item] of entries) {
407
- result[key] = sortKeys(item);
408
- }
409
- return result;
410
- }
411
- return value;
412
- }
413
- function buildCapabilitySet(permissions, toolOverrides) {
414
- const caps = new Set();
415
- const pt = permissions?.tools;
416
- const pd = permissions?.data;
417
- const tc = toolOverrides;
418
- const fs = tc?.filesystem ?? pt?.filesystem ?? "none";
419
- if (fs === "read")
420
- caps.add("file_read");
421
- if (fs === "write") {
422
- caps.add("file_read");
423
- caps.add("file_write");
424
- }
425
- if (tc?.shell ?? pt?.shell ?? false)
426
- caps.add("shell");
427
- if (tc?.network ?? pt?.network ?? false)
428
- caps.add("network_request");
429
- if (pt?.mcp === "invoke")
430
- caps.add("mcp_invoke");
431
- if (pt?.mcp === "spawn") {
432
- caps.add("mcp_invoke");
433
- caps.add("mcp_spawn");
434
- }
435
- if (pt?.task)
436
- caps.add("task");
437
- const sess = tc?.session ?? pd?.session ?? "none";
438
- if (sess === "read")
439
- caps.add("session_data");
440
- const ws = tc?.workspace ?? pd?.workspace ?? "none";
441
- if (ws === "read")
442
- caps.add("workspace_data");
443
- const cfg = tc?.config ?? pd?.config ?? "none";
444
- if (cfg === "global") {
445
- caps.add("config:read");
446
- caps.add("config:write");
447
- }
448
- if (cfg === "plugin")
449
- caps.add("config:read");
450
- if (pd?.secrets === "own")
451
- caps.add("secrets");
452
- const hooks = permissions?.hooks;
453
- if (hooks?.promptTransform)
454
- caps.add("prompt_transform");
455
- if (hooks?.compactionTransform)
456
- caps.add("compaction_transform");
457
- if (hooks?.config)
458
- caps.add("config_hook");
459
- if (hooks?.toolExecute && hooks.toolExecute !== "none")
460
- caps.add("tool_execution_hook");
461
- if (hooks?.permissionAsk && hooks.permissionAsk !== "none")
462
- caps.add("permission_hook");
463
- if (hooks?.events === "all" || (hooks?.events === "selected" && (hooks.eventNames?.length ?? 0) > 0)) {
464
- caps.add("event_hook");
465
- }
466
- return [...caps].sort();
467
- }
468
- export function baseCapabilities(manifest) {
469
- return buildCapabilitySet(manifest.permissions);
470
- }
471
- export function toolCapabilities(manifest, tool) {
472
- return buildCapabilitySet(manifest.permissions, tool.capabilities);
473
- }
474
- export function publicToolCapabilities(manifest) {
475
- const caps = new Set();
476
- for (const tool of manifest.contributes?.tools ?? []) {
477
- if (tool.exposure?.mode === "internal")
478
- continue;
479
- for (const capability of toolCapabilities(manifest, tool))
480
- caps.add(capability);
481
- }
482
- return [...caps].sort();
483
- }
484
- export function computeRisk(capabilities, manifest) {
485
- if (capabilities.length === 0)
486
- return "low";
487
- let risk = "low";
488
- for (const cap of capabilities) {
489
- risk = maxRisk(risk, capabilityRisk(cap, manifest));
490
- }
491
- return risk;
492
- }
493
- export function capabilitiesForRiskScope(manifest, scope) {
494
- return scope === "agent" ? publicToolCapabilities(manifest) : baseCapabilities(manifest);
495
- }
496
- export function pluginRisk(manifest, input) {
497
- return computeRisk(capabilitiesForRiskScope(manifest, input.scope), manifest);
498
- }
499
- export function toolRisk(manifest, tool) {
500
- return computeRisk(toolCapabilities(manifest, tool), manifest);
501
- }
502
- function networkPermissionItem(manifest) {
503
- const domains = manifest.permissions?.network?.connectDomains ?? [];
504
- const severity = capabilityRisk("network_request", manifest);
505
- return {
506
- key: "network_request",
507
- category: "network",
508
- severity,
509
- title: "Access network",
510
- description: domains.length > 0
511
- ? `Can make network requests to: ${domains.join(", ")}.`
512
- : "Can make outbound network requests to any domain.",
513
- technical: domains.length > 0 ? `domains: ${domains.join(", ")}` : undefined,
514
- };
515
- }
516
- function contributionPermissionItems(manifest) {
517
- const ui = manifest.contributes?.ui;
518
- const perms = manifest.permissions?.ui;
519
- const items = [];
520
- if (ui?.toolRenderers || perms?.toolRenderers) {
521
- items.push({
522
- key: "ui.toolRenderers",
523
- category: "ui",
524
- severity: "low",
525
- title: "Custom tool renderers",
526
- description: "Overrides how tool outputs appear in the chat UI.",
527
- });
528
- }
529
- if (ui?.partRenderers || perms?.partRenderers) {
530
- items.push({
531
- key: "ui.partRenderers",
532
- category: "ui",
533
- severity: "low",
534
- title: "Custom part renderers",
535
- description: "Overrides how message parts appear in the chat UI.",
536
- });
537
- }
538
- if (ui?.workbenchPanels || perms?.workbenchPanels) {
539
- items.push({
540
- key: "ui.workbenchPanels",
541
- category: "ui",
542
- severity: "low",
543
- title: "Workbench panels",
544
- description: "Adds custom panels to the side workspace or BottomSpace.",
545
- });
546
- }
547
- if (ui?.appPanels || perms?.appPanels) {
548
- items.push({
549
- key: "ui.appPanels",
550
- category: "ui",
551
- severity: "low",
552
- title: "App panels",
553
- description: "Adds top-level sidebar panels to the app.",
554
- });
555
- }
556
- if (ui?.settings || perms?.settings) {
557
- items.push({
558
- key: "ui.settings",
559
- category: "ui",
560
- severity: "low",
561
- title: "Settings page",
562
- description: "Adds a custom settings page or form.",
563
- });
564
- }
565
- if (ui?.themes || perms?.themes) {
566
- items.push({
567
- key: "ui.themes",
568
- category: "ui",
569
- severity: "low",
570
- title: "Custom themes",
571
- description: "Adds custom color themes to the UI.",
572
- });
573
- }
574
- if (ui?.icons || perms?.icons) {
575
- items.push({
576
- key: "ui.icons",
577
- category: "ui",
578
- severity: "low",
579
- title: "Custom icons",
580
- description: "Adds custom icon sets to the UI.",
581
- });
582
- }
583
- if (ui?.messageSlots || perms?.messageSlots) {
584
- items.push({
585
- key: "ui.messageSlots",
586
- category: "ui",
587
- severity: "low",
588
- title: "Message slots",
589
- description: "Adds custom components around reasoning and tool timeline sections.",
590
- });
591
- }
592
- if (ui?.appRoutes || perms?.appRoutes) {
593
- items.push({
594
- key: "ui.appRoutes",
595
- category: "ui",
596
- severity: "low",
597
- title: "App routes",
598
- description: "Adds custom plugin pages to the app.",
599
- });
600
- }
601
- if (ui?.commands || perms?.commands) {
602
- items.push({
603
- key: "ui.commands",
604
- category: "ui",
605
- severity: "low",
606
- title: "Custom commands",
607
- description: "Adds custom UI commands to the command palette.",
608
- });
609
- }
610
- return items;
611
- }
612
- function dataPermissionItems(manifest) {
613
- const data = manifest.permissions?.data;
614
- const items = [];
615
- if (data?.session === "read") {
616
- items.push({
617
- key: "data.session",
618
- category: "data",
619
- severity: "medium",
620
- title: "Read session data",
621
- description: "Can access session history and metadata across all sessions.",
622
- });
623
- }
624
- else if (data?.session === "metadata") {
625
- items.push({
626
- key: "data.session",
627
- category: "data",
628
- severity: "low",
629
- title: "Read session metadata",
630
- description: "Can access session metadata but not message content.",
631
- });
632
- }
633
- if (data?.workspace === "read") {
634
- items.push({
635
- key: "data.workspace",
636
- category: "data",
637
- severity: "low",
638
- title: "Read workspace metadata",
639
- description: "Can access workspace metadata and directory information.",
640
- });
641
- }
642
- if (data?.secrets === "own") {
643
- items.push({
644
- key: "data.secrets",
645
- category: "data",
646
- severity: "high",
647
- title: "Own credential store",
648
- description: "Can read and write its own stored credentials.",
649
- });
650
- }
651
- return items;
652
- }
653
- function hookPermissionItems(manifest) {
654
- const hooks = manifest.permissions?.hooks;
655
- const items = [];
656
- if (hooks?.promptTransform) {
657
- items.push({
658
- key: "hooks.promptTransform",
659
- category: "hooks",
660
- severity: "high",
661
- title: "Transform prompts",
662
- description: "Can modify the system prompt and message context sent to the LLM.",
663
- });
664
- }
665
- if (hooks?.config) {
666
- items.push({
667
- key: "hooks.config",
668
- category: "hooks",
669
- severity: "medium",
670
- title: "Observe runtime config",
671
- description: "Can observe redacted runtime configuration snapshots when Synergy starts or reloads config.",
672
- });
673
- }
674
- if (hooks?.toolExecute === "all") {
675
- items.push({
676
- key: "hooks.toolExecute",
677
- category: "hooks",
678
- severity: "medium",
679
- title: "Intercept all tool execution",
680
- description: "Can rewrite arguments and outputs for all tool calls, including from other plugins.",
681
- });
682
- }
683
- else if (hooks?.toolExecute === "declared") {
684
- items.push({
685
- key: "hooks.toolExecute",
686
- category: "hooks",
687
- severity: "low",
688
- title: "Intercept declared tool execution",
689
- description: "Can rewrite arguments and outputs for tools declared in its manifest.",
690
- });
691
- }
692
- else if (hooks?.toolExecute === "own") {
693
- items.push({
694
- key: "hooks.toolExecute",
695
- category: "hooks",
696
- severity: "low",
697
- title: "Intercept own tool execution",
698
- description: "Can rewrite arguments and outputs for its own tool calls.",
699
- });
700
- }
701
- if (hooks?.permissionAsk === "all") {
702
- items.push({
703
- key: "hooks.permissionAsk",
704
- category: "hooks",
705
- severity: "high",
706
- title: "Override all permission decisions",
707
- description: "Can allow or deny any permission request, including from other plugins.",
708
- });
709
- }
710
- else if (hooks?.permissionAsk === "own") {
711
- items.push({
712
- key: "hooks.permissionAsk",
713
- category: "hooks",
714
- severity: "medium",
715
- title: "Override own permission decisions",
716
- description: "Can allow or deny its own permission requests.",
717
- });
718
- }
719
- return items;
720
- }
721
- export function permissionItems(manifest, capabilities) {
722
- const items = [];
723
- const seen = new Set();
724
- const add = (item) => {
725
- if (seen.has(item.key))
726
- return;
727
- seen.add(item.key);
728
- items.push(item);
729
- };
730
- for (const capability of capabilities) {
731
- if (capability === "network_request") {
732
- add(networkPermissionItem(manifest));
733
- continue;
734
- }
735
- const details = SYNERGY_CAPABILITY_DETAILS[capability];
736
- if (details)
737
- add({ key: capability, ...details });
738
- }
739
- for (const item of contributionPermissionItems(manifest))
740
- add(item);
741
- for (const item of dataPermissionItems(manifest))
742
- add(item);
743
- for (const item of hookPermissionItems(manifest))
744
- add(item);
745
- return items;
746
- }
747
- export function registryPermissionSummary(manifest, capabilities) {
748
- return permissionItems(manifest, capabilities).map((item) => ({
749
- key: item.key,
750
- description: item.description,
751
- risk: item.severity,
752
- }));
419
+ export function capabilityRisk(capability) {
420
+ return SYNERGY_CAPABILITY_DETAILS[capability]?.severity ?? "high";
753
421
  }
754
422
  export function permissionCategoryForKey(key) {
755
423
  const details = SYNERGY_CAPABILITY_DETAILS[key];
@@ -769,91 +437,3 @@ export function permissionCategoryForKey(key) {
769
437
  return "browser";
770
438
  return "tools";
771
439
  }
772
- export function permissionsHashPayload(manifest, capabilities) {
773
- return {
774
- capabilities: [...capabilities].sort(),
775
- permissions: manifest.permissions ?? {},
776
- contributes: permissionsRelevantContributes(manifest),
777
- lifecycle: manifest.lifecycle ?? {},
778
- };
779
- }
780
- function permissionsRelevantContributes(manifest) {
781
- const contributes = manifest.contributes;
782
- if (!contributes)
783
- return undefined;
784
- return {
785
- ...(contributes.permissions ? { permissions: contributes.permissions } : {}),
786
- ...(contributes.tools
787
- ? {
788
- tools: contributes.tools.map((tool) => ({
789
- id: tool.id,
790
- name: tool.name,
791
- exposure: tool.exposure,
792
- capabilities: tool.capabilities,
793
- })),
794
- }
795
- : {}),
796
- ...(contributes.agents
797
- ? {
798
- agents: contributes.agents.map((agent) => ({
799
- name: agent.name,
800
- mode: agent.mode,
801
- hidden: agent.hidden,
802
- model: agent.model,
803
- modelRole: agent.modelRole,
804
- permission: agent.permission,
805
- })),
806
- }
807
- : {}),
808
- ...(contributes.mcp ? { mcp: contributes.mcp } : {}),
809
- ...(contributes.ui
810
- ? {
811
- ui: {
812
- toolRenderers: Boolean(contributes.ui.toolRenderers?.length),
813
- partRenderers: Boolean(contributes.ui.partRenderers?.length),
814
- workbenchPanels: Boolean(contributes.ui.workbenchPanels?.length),
815
- appPanels: Boolean(contributes.ui.appPanels?.length),
816
- settings: Boolean(contributes.ui.settings?.length),
817
- messageSlots: Boolean(contributes.ui.messageSlots?.length),
818
- themes: Boolean(contributes.ui.themes?.length),
819
- icons: Boolean(contributes.ui.icons?.length),
820
- appRoutes: Boolean(contributes.ui.appRoutes?.length),
821
- commands: Boolean(contributes.ui.commands?.length),
822
- },
823
- }
824
- : {}),
825
- };
826
- }
827
- export function manifestHashPayload(manifest) {
828
- return manifest;
829
- }
830
- export function stablePluginJson(value) {
831
- return JSON.stringify(sortKeys(value));
832
- }
833
- export const SYNERGY_BRIDGE_METHOD_POLICY = {
834
- "config.get": { type: "capability", capability: "config:read" },
835
- "config.set": { type: "capability", capability: "config:write" },
836
- "config.replace": { type: "capability", capability: "config:write" },
837
- "secret.get": { type: "capability", capability: "secrets" },
838
- "secret.set": { type: "capability", capability: "secrets" },
839
- "secret.delete": { type: "capability", capability: "secrets" },
840
- "cache.get": { type: "unprivileged" },
841
- "cache.set": { type: "unprivileged" },
842
- "cache.delete": { type: "unprivileged" },
843
- "file.read": { type: "capability", capability: "file_read" },
844
- "file.write": { type: "capability", capability: "file_write" },
845
- "network.fetch": { type: "capability", capability: "network_request" },
846
- "shell.run": { type: "capability", capability: "shell" },
847
- "session.getMetadata": { type: "capability", capability: "session_data" },
848
- "workspace.getMetadata": { type: "capability", capability: "workspace_data" },
849
- "task.run": { type: "capability", capability: "task" },
850
- "tool.invoke": { type: "unprivileged" },
851
- "permission.request": { type: "unprivileged" },
852
- };
853
- export function bridgeMethodPolicy(method) {
854
- return SYNERGY_BRIDGE_METHOD_POLICY[method] ?? { type: "unknown" };
855
- }
856
- export function bridgeCapability(method) {
857
- const policy = bridgeMethodPolicy(method);
858
- return policy.type === "capability" ? policy.capability : undefined;
859
- }