@ericsanchezok/synergy-plugin
v3.0.23
Published
`definePlugin()` is the only source of plugin identity, capabilities, contributions, and executable handlers. Authors do not write `plugin.json`; `synergy-plugin build` generates it with runtime/UI bundles and integrity metadata.
Readme
Synergy Plugin API 4
definePlugin() is the only source of plugin identity, capabilities, contributions, and executable handlers. Authors do not write plugin.json; synergy-plugin build generates it with runtime/UI bundles and integrity metadata.
import z from "zod"
import { capability, definePlugin, event, operation, workbenchPanel } from "@ericsanchezok/synergy-plugin"
export default definePlugin({
id: "example",
version: "1.0.0",
description: "Example plugin",
compatibility: { synergy: ">=3.0.11" },
assets: [{ source: "src/prompts", target: "runtime/prompts" }],
capabilities: [capability("workspace.read"), capability("ui.hostActions")],
contributions: [
event({ id: "example.changed", payload: z.object({ reason: z.string() }) }),
operation({
id: "example.get",
type: "query",
input: z.object({}),
output: z.object({ scopeId: z.string() }),
async handler(_input, context) {
return { scopeId: context.scopeId }
},
}),
workbenchPanel({
id: "main",
label: "Example",
surface: "side",
cardinality: "singleton",
component: { source: "./src/ui.tsx" },
}),
],
})Definition Rules
- Plugin IDs use lowercase letters, digits, dots, and hyphens and begin with a letter.
- Contribution IDs are unique within each contribution kind. A command operation and its
ui.textActionmay intentionally share a local ID; generated runtime handler IDs remain kind-qualified. - A contribution's
requiresentries must exist in top-levelcapabilities. operation()defaults toexpose: ['ui']; addsdkexplicitly for public SDK access.- Zod and JSON Schema are accepted for operation, event, and tool schemas.
activate()runs once per runtime generation and does not receive Scope or Session state.- Top-level
assetsmap project-relative files or directories into package-relative targets. Asset contents are integrity-checked and included in the generation hash. - The plugin and plugin-kit npm package versions follow the Synergy product release version; package major versions do not select a Plugin API family.
apiVersionstays"4.0"for the stable API4 family. Setcompatibility.synergyonly when the plugin needs a newer additive host feature; otherwise the GA baseline is generated automatically.- Stable API4 types and behavior remain backward compatible. Avoid
experimental.*in published stable plugins because those surfaces may change or disappear.
Contribution Factories
Executable factories: operation, tool, hook, cliCommand, authProvider, lifecycleInstall, lifecycleUpgrade, and lifecycleUninstall.
Declarative factories: event, agent, skill, mcp, workbenchPanel, navigationItem, messageRenderer, composerAction, composerExtension, selectionExtension, textAction, messageSlot, settings, theme, and icon.
The generated manifest contains declarations only. Runtime startup reports its actual handler IDs, and the host requires an exact match.
Invocation Context
Every executable call receives a fresh PluginInvocationContext with request ID, Scope, optional Session, actor, cancellation, logger, scoped events, and only the Host Services allowed by approved capabilities. Plugins never receive a raw Synergy client, server URL, or token.
Capabilities govern Host Services; they do not claim to restrict direct OS access by the external process. task.delegate exposes start/run/current/get/cancel; run() waits for a native Cortex Task and returns its terminal snapshot, while current() reads the durable owner of the invoking child Session. Non-agent callers must provide an explicit parent Session/message for start() in the active Scope. Contributed Agents are registered in Synergy's native Agent registry. Set hidden: true for an owner-only Agent that must stay out of ordinary prompt and native-task exposure.
asset.write exposes context.asset.create() and returns a host-owned attachment. shell.execute exposes argv-only context.shell.run() through the ordinary permission and sandbox boundary. cliCommand() registers executable commands under synergy <pluginId> <command>. MCP contributions use strict shared local/remote schemas and are installed atomically under qualified ${pluginId}::${contributionId} names.
runtime.endpoint.read exposes context.runtimeEndpoint.get(). It accepts no arguments and returns only the current loopback HTTP origin plus an opaque listener generation. It never returns a token, route, SDK client, or wider server configuration. Loopback and wildcard binds are served over loopback and return a normalized http://127.0.0.1:<port> URL; a bind that excludes loopback makes the service fail closed. External process management remains plugin-owned.
task.delegate is the plugin capability; task is the separate runtime permission evaluated by the current control profile. task.start() parent binding failures expose PluginHostServiceErrorCode.TASK_PARENT_REQUIRED or TASK_PARENT_SCOPE_MISMATCH. Host Service error codes survive process IPC.
agent.call exposes bounded Sessionless Agent work only to an executable contribution that lists it in requires. context.agent.call() waits for text; context.agent.start() returns a call ID immediately and reports its memory-only terminal result to the same plugin generation and Scope through agent.call.after. By default a plugin may call only a hidden Agent owned by its active generation; capability constraints may allow additional Agent names, permitted model roles, and lower host runtime/input/output ceilings. Calls may choose an allowed Synergy role but never a concrete provider/model ID. Neither path has tools, Session history, or Cortex lifecycle.
Trusted UI
UI API 5 uses PluginComponentProps<Context> with one context property for every executable surface. The public service types cover environment/navigation, session collections and messages, draft/editor/submission, workbench resources, commands, overlays and lifetime. Specialized settings values, changes and save status live in context.settings. Import host components through @ericsanchezok/synergy-plugin/components, semantic icon types through /icons, formatting through /format, and Skin schemas through /skin.
import type { PluginComponentProps } from "@ericsanchezok/synergy-plugin"
import { EmptyState } from "@ericsanchezok/synergy-plugin/components"
export default function Panel({ context }: PluginComponentProps) {
return <EmptyState title={context.surface.id} description="Plugin content" />
}Plugin Kit generates artifacts.ui.apiVersion, named exports and hashes for the full JS/CSS/font/image graph. The host shares Solid, scopes styles and portals, and disposes registrations by generation. UI 4 executable surfaces require a rebuild and migration; backend API4 compatibility remains independent. See UI contributions for services, Shells, Skins and real-host preview, and UI 4 migration for the breaking changes.
Runtime and Data
External plugins run in one process per active pluginId + version + generation; enabled Scopes share it and receive separate invocation contexts. Trusted built-ins may use inProcess. The process boundary is crash/resource cleanup isolation, not an OS security sandbox.
The runtime transport protocol is host-owned. runtime.protocolVersion is diagnostic provenance, not a plugin feature gate or compatibility contract; use apiVersion and compatibility.synergy instead.
Plugins own their business data, schema, concurrency, backup, migration, and deletion. Synergy stores only installation metadata, approval, Scope enablement, declarative settings, and plugin credentials.
Integrity Contract
@ericsanchezok/synergy-plugin/integrity owns the canonical manifest and permissions hash functions used by plugin-kit signing, marketplace metadata, installation verification, and approval records. The permissions hash binds capability constraints, contribution requirements, operation exposure, and trusted UI presence. Tooling and hosts must import this contract rather than reimplement its serialization or payload.
Toolchain
synergy-plugin build
synergy-plugin validate --runtime-discovery
synergy-plugin test
synergy-plugin pack
synergy-plugin dev --server-url http://127.0.0.1:PORTLive reload requires an explicit isolated SYNERGY_HOME. Successful rebuilds publish a new generation atomically; failed builds leave the previous generation active.
See docs/plugins for architecture, lifecycle, marketplace, security, and UI guidance.
