Plugin API reference
Look up the exact signature of anything an extension can call, the permission it needs, and whether the shipped type declarations know about it. For what each namespace is for, with worked examples, read The plugin API first — this page is the lookup layer that guide defers to.
How to read this
Section titled “How to read this”Members are grouped by where the code runs:
ctx.api.*— the main-process half, reached through thectxpassed toactivate(ctx). Declared asPluginAPIin@wamp/extension-sdk.pluginAPI.*— the window half, imported from@wamp/plugin-api.
Every table carries an In types column. No means the member exists at
runtime but is absent from the published declarations, so calling it is a
compile error until you cast around it. Treat a No as unsupported: undeclared
means unversioned, and it can change without notice.
The Permission noted on a namespace is the manifest permission that must be
declared for the namespace to exist. Without it the member is undefined — not
a thrown error — so guard or declare. See Permissions.
Four permission strings are not runtime gates: ai, filesystem, network,
and terminal are consent signals shown to the user, and the host does not
withhold those capabilities today. Read this column as a statement about which
namespaces are present, not as a security boundary.
ctx.api.db, the old raw-SQLite handle, no longer exists. Raw SQL survives in
exactly two places: the migrations array of a typed-data schema, and
data.raw(sql, params).
The context object
Section titled “The context object”ctx carries five members besides api.
| Member | In types | Description |
|---|---|---|
ctx.pluginId: string |
Yes | This extension’s id — its directory name |
ctx.pluginPath: string |
Yes | Absolute path to the extension’s own directory |
ctx.storage: PluginStorage |
Yes | The same object as ctx.api.storage |
ctx.log |
Yes | info / warn / error / debug, each (message, ...args) |
ctx.subscriptions: { dispose }[] |
Yes | Array of disposables the host tears down on deactivation |
ctx.api.ai
Section titled “ctx.api.ai”Permission: none.
| Member | In types | Description |
|---|---|---|
complete(input, options?): Promise<string> |
Yes | One-shot completion; resolves to the assistant text |
stream(input, callbacks, options?): Promise<void> |
Yes | The same completion, streamed through callbacks.onChunk |
generateObject<T>(input, schema, options?): Promise<T> |
Yes | Completion constrained to a JSON schema |
createSession(opts): AISession |
Yes | Multi-turn session owned by this extension |
chat(input, options?): Promise<string> |
No | Behaves exactly like complete |
chatWithUsage(input, options?) |
No | chat plus { text, usage } |
completeWithUsage(input, options?) |
No | complete plus { text, usage } |
abort(requestId): void |
No | Cancels an in-flight stream |
input is either a prompt string or a { role, content }[] array.
options is ExtensionAIOptions: model? (defaults to the user’s selected
model), system?, maxTokens? (defaults to 4096), temperature?.
callbacks for stream:
| Callback | In types | Description |
|---|---|---|
onChunk(text) |
Yes | Fires per streamed text fragment |
onEnd(usage, stopReason?) |
Yes | stopReason: 'max_tokens' means the answer was cut off, not finished |
onError(error: Error) |
Yes | Terminal failure |
onRequestStart(requestId) |
No | The id abort takes |
Cancelling a main-process stream therefore needs two undeclared members. In the
window, pluginAPI.ai.stream returns a cancel function instead, which is
declared.
createSession options
Section titled “createSession options”CreateAISessionOptions — agent is the only required field.
| Field | Description |
|---|---|
agent: string |
Agent definition id; sets the base prompt and tool set |
conversationId?: string |
Stable id. Generated when omitted |
systemPromptExtension?: string |
Appended to the agent’s base system prompt |
tools?: { include?, exclude? } |
Name filters. Exact match, except a trailing * which matches a prefix |
model?: string |
Overrides model resolution |
maxTurns?: number |
Per-send turn cap |
maxTokens?: number |
Output token cap per turn |
persistence?: AISessionHistoryPersistor |
load(), replace(history), optional appendHint(msg) |
hooks?: AISessionHooks |
beforeCall, onUsage, afterTurn |
The window’s pluginAPI.ai.createSession accepts the same object minus
persistence and hooks.
AISession
Section titled “AISession”| Member | In types | Description |
|---|---|---|
id, extensionId |
Yes | Readonly strings |
isBusy, isDestroyed |
Yes | Readonly booleans |
events.on(event, handler): () => void |
Yes | event is chunk, tool_use, turn_end, or error |
send(input, opts?): Promise<AISessionTurnResult> |
Yes | Runs a turn. Throws when another send is in flight |
appendContext(text, opts?): Promise<AISessionMessage> |
Yes | Adds a model-visible transcript entry with no model call |
cancel(): void |
Yes | Aborts the in-flight send; no-op otherwise |
fork(newId, persistor?): AISession |
Yes | Independent session with a copy of the history |
destroy(): Promise<void> |
Yes | Idempotent teardown |
history(): AISessionMessage[] |
Yes | Fresh copy of the current history |
send accepts a string or { content: AISessionContentBlock[] }, plus
{ hidden } in opts for grounding the model sees but the UI does not.
AISessionTurnResult carries text, toolsUsed, usage, stopReason,
turns, and history. Full treatment in AI sessions.
ctx.api.agents
Section titled “ctx.api.agents”Permission: none.
| Member | In types | Description |
|---|---|---|
delegate(agentType, task, options?) |
Yes | Runs an agent to completion and resolves with its result |
getAvailable(): string[] |
Yes | Agent ids currently registered |
options: context?, todos?, skills?, modelOverride? (a tier name —
fast, balanced, powerful — or a full model id). The result is
{ text, toolsUsed, stopReason, usage, costUsd }; costUsd is 0 for unpriced
models. The window’s delegate additionally accepts outputSchema and returns
object / schemaError, which the main-process declaration does not carry.
ctx.api.tools
Section titled “ctx.api.tools”Permission: none.
| Member | In types | Description |
|---|---|---|
register(tool): Disposable |
Yes | Registers a tool the assistant can call |
tool fields: name, description, inputSchema (JSON Schema object),
execute(input, ctx), plus optional category, display, and policy.
execute returns a string or an array of content blocks
({ type: 'text', text } / { type: 'image', data, mediaType, dims? }).
Tool names must match ^[a-zA-Z0-9_-]+$. A name containing a dot throws inside
activate(), which faults the whole extension — so write notes_search, never
notes.search. Schemas, display metadata, and policy flags are covered in
Contributing tools.
Inside a tool’s execute
Section titled “Inside a tool’s execute”The second argument to execute is typed unknown. The SDK publishes one
narrow shape for it, ToolContext, and the runtime object has more.
| Member | In types | Description |
|---|---|---|
ctx.services?.client?.request(method, params?, opts?) |
Yes | Reverse-RPC to the connected client; absent on a desktop host, which is the client |
ctx.sendToolUse(name, input, status, result?, meta?) |
No | Pushes an interim tool-use card to the UI; status is running, success, or error |
ctx.api.data
Section titled “ctx.api.data”Permission: none. The local typed store is ungated — the database permission
withholds cloud, the store whose records leave the machine, not this one.
| Member | In types | Description |
|---|---|---|
defineSchema(schema): Promise<DataAPI<S>> |
Yes | Creates or migrates the tables and returns the typed query surface |
The returned object has one property per table plus raw:
| Member | In types | Description |
|---|---|---|
findMany(args?): Promise<Row[]> |
Yes | where, orderBy, take, skip |
findUnique(args): Promise<Row | null> |
Yes | Single row by where |
create(args): Promise<Row> |
Yes | Inserts args.data and returns the stored row |
update(args): Promise<{ changes }> |
Yes | Applies args.data to rows matching args.where |
delete(args): Promise<{ changes }> |
Yes | Deletes rows matching args.where |
count(args?): Promise<number> |
Yes | Row count |
raw<R>(sql, params?): Promise<R[]> |
Yes | Raw SQL escape hatch; rows come back untyped |
The predicate builder q (eq, ne, in, notIn, like, isNull,
isNotNull, now, and the comparison operators) is imported from
@wamp/extension-sdk. Schema shape, column types, and migrations are in
Typed data.
ctx.api.cloud
Section titled “ctx.api.cloud”Permission: database.
cloud.mine is private to the signed-in end user and follows them across
devices. cloud.shared is one copy that every signed-in user of the app can
read and write. Both expose the same four operations.
| Member | In types | Description |
|---|---|---|
get<T>(collection, key): Promise<T | null> |
Yes | null when the key does not exist |
set<T>(collection, key, value): Promise<void> |
Yes | Create or overwrite; undefined is not storable |
delete(collection, key): Promise<void> |
Yes | Idempotent |
list<T>(collection): Promise<CloudRecord<T>[]> |
Yes | The whole collection; the host follows the server’s cursor to the end |
Collection and key names match [A-Za-z0-9._-]{1,128}. Values are anything
JSON.stringify accepts, up to 256 KB serialized. The namespace also requires a
product with an app backend configured; in a build without one, every call
throws a message saying so. The window’s pluginAPI.cloud calls the same
service, and the main process re-checks the database permission on each
operation — a modified bundle must not reach the app’s backend without it.
Backend setup is documented at
docs.cloud.vampikez.fun.
ctx.api.storage, secrets, settings
Section titled “ctx.api.storage, secrets, settings”Permission: none for all three.
| Member | In types | Description |
|---|---|---|
storage.get<T>(key, defaultValue?): Promise<T | undefined> |
Yes | Per-extension key/value read |
storage.set<T>(key, value): Promise<void> |
Yes | Write |
storage.delete(key): Promise<void> |
Yes | Remove one key |
storage.keys(): Promise<string[]> |
Yes | Every key this extension has stored |
secrets.set(key, value): Promise<void> |
Yes | Encrypted write |
secrets.get(key): Promise<string | null> |
Yes | Encrypted read |
secrets.delete(key): Promise<void> |
Yes | Remove |
secrets.has(key): Promise<boolean> |
Yes | Existence check without decrypting |
settings.get<T>(key): Promise<T | undefined> |
Yes | The user’s override, falling back to the contributes.settings default |
settings.set<T>(key, value): Promise<void> |
Yes | Persists under settings:<key> in this extension’s storage |
ctx.api.commands, services
Section titled “ctx.api.commands, services”Permission: none for both.
| Member | In types | Description |
|---|---|---|
commands.register(cmd): Disposable |
Yes | cmd carries the handler inside the descriptor |
commands.register(cmd, handler): Disposable |
Yes | Older two-argument shape; still accepted |
commands.execute(id, args?): Promise<unknown> |
Yes | Invokes any registered command |
commands.getAll(): Command[] |
Yes | Every registered command |
services.register<T>(id, impl): Disposable |
Yes | Publishes a service under id |
services.get<T>(id): T | undefined |
Yes | Lookup that tolerates absence |
services.require<T>(id): T |
Yes | Lookup that throws when absent |
services.onRegister<T>(id, cb): Disposable |
Yes | Fires when a service appears |
services.onUnregister(id, cb): Disposable |
Yes | Fires when it goes away |
A Command is { id, title, keybinding?, category?, scope? }, where scope is
focused-view, focused-slot, global, or an app-specific string. Registering
with no handler in either position throws.
ctx.api.ipc, events
Section titled “ctx.api.ipc, events”Permission: none for both.
| Member | In types | Description |
|---|---|---|
ipc.handle(channel, handler): () => void |
Yes | Answers window.electronAPI.extensionIPC.invoke from your window half |
ipc.broadcast(channel, payload): void |
Yes | Pushes to every window; throws synchronously if the channel’s prefix is not a declared ipcNamespaces entry |
ipc.subscribe(channel, handler): Disposable |
Yes | Listens on an extension-owned channel from the main half |
ipc.send(channel, ...args): void |
Yes | Legacy un-namespaced send; prefer broadcast |
events.on(event, handler): () => void |
Yes | In-process subscribe on plugin:<id>:<event> |
events.emit(event, ...args): void |
Yes | Emits in-process and fans out to every window |
ipc.handle is generic in its argument tuple, so a typed handler such as
(_e, req: SpawnRequest) => … infers without a cast.
ctx.api.fs
Section titled “ctx.api.fs”Permission: none. These call Node directly with the path you pass, so they are not restricted to the workspace.
| Member | In types | Description |
|---|---|---|
readFile(path): Promise<string> |
Yes | UTF-8 read |
readFileBuffer(path): Promise<Buffer> |
Yes | Binary read |
writeFile(path, content): Promise<void> |
Yes | UTF-8 write |
exists(path): Promise<boolean> |
Yes | Access check |
readDir(path): Promise<string[]> |
Yes | Entry names |
mkdir(path): Promise<void> |
Yes | Recursive create |
copyFile(src, dest): Promise<void> |
Yes | Copy |
remove(path): Promise<void> |
Yes | Delete |
stat(path) |
Yes | { size, isDirectory, isFile, mtime } |
getDataPath(subdir?): string |
Yes | This extension’s own data directory, created if missing |
join, dirname, basename, extname |
Yes | Path helpers, synchronous |
ctx.api.dialog, notifications, shell
Section titled “ctx.api.dialog, notifications, shell”| Member | Permission | In types | Description |
|---|---|---|---|
dialog.openFile(options?): Promise<string[] | null> |
none | Yes | Native picker; options.directory picks folders, options.multiSelections allows several |
dialog.saveFile(options?): Promise<string | null> |
none | Yes | Save picker with title, defaultPath, filters |
notifications.show({ title, body, silent? }): void |
notifications |
Yes | System notification; works with no window open |
shell.onWillOpenExternal(handler): Disposable |
none | Yes | Claims a URL open before the OS browser sees it |
An interceptor returns true to claim the URL. Handlers run in registration
order across extensions, the first true wins, and a throw is treated as
fall-through. Only user-initiated opens travel through this surface.
ctx.api.webviews
Section titled “ctx.api.webviews”Permission: none, but navigation control, script injection, and request
interception each need a matching entry in the manifest’s capabilities.
| Member | In types | Description |
|---|---|---|
create(options): WebviewHandle |
Yes | options: id, html?, url?, enableScripts?, retainContextWhenHidden?, partition?, surface?, webRequestRewrite? |
WebviewHandle:
| Member | In types | Description |
|---|---|---|
id |
Yes | Readonly string |
webview.postMessage(message): void |
Yes | Send into the page |
webview.onDidReceiveMessage(handler): Disposable |
Yes | Receive from the page |
webview.asWebviewUri(localPath): string |
Yes | Returns wamp-webview://<extensionId>/<path> |
webview.cspSource |
Yes | Readonly per-webview CSP source |
loadURL(url): Promise<void> |
Yes | Navigate; resolves on load |
goBack(), goForward(), reload(opts?) |
Yes | History and refresh |
executeScript(source, options?): Promise<unknown> |
Yes | options.world is isolated or main |
onWillNavigate(handler): Disposable |
Yes | Return allow, deny, or { redirect } |
onResourceRequest(handler): Disposable |
Yes | Return { cancel?, redirect?, modifyHeaders? } |
getState<T>(), setState<T>(state) |
Yes | Per-webview state slot |
setSurface(surface): void |
Yes | Moves between visible and headless without reloading |
reveal(): void |
Yes | setSurface('visible') plus raise-to-front |
setBounds(rect): void |
Yes | Window pixels, not CSS pixels — multiply by devicePixelRatio |
openDevTools(opts?): void |
Yes | mode: bottom, right, undocked, detach |
dispose(): void |
Yes | Destroys the webview |
ctx.api.cron, http
Section titled “ctx.api.cron, http”| Member | Permission | In types | Description |
|---|---|---|---|
cron.schedule(id, interval, handler): CronHandle |
cron |
Yes | Repeating job; the handle is a disposable that removes the entry |
cron.runOnce(id, handler): Promise<void> |
cron |
Yes | Runs once, tracked under id |
cron.cancel(id): void |
cron |
Yes | Removes an entry by id |
cron.list(): CronJobInfo[] |
cron |
Yes | { id, interval, lastRun, nextRun, status, lastError? } |
http.route(method, path, handler): () => void |
http-routes |
Yes | method is GET, POST, PUT, DELETE, or PATCH |
http.baseUrl(): string |
http-routes |
Yes | The base URL to hand to whoever calls your route |
A route handler receives { method, path, params, query, body, headers } and
returns { status?, body?, headers? }. Both namespaces are covered in
Scheduled work.
ctx.api.process, pty, git
Section titled “ctx.api.process, pty, git”All three are gated on the single process permission — every git operation
is a git process spawn, so it grants nothing process does not already imply.
| Member | In types | Description |
|---|---|---|
process.spawn(command, args, options?): PluginChildProcess |
Yes | options: cwd, env, stdio |
process.killAll(): void |
Yes | Kills every child this extension started |
pty.spawn(options): PtyHandle |
Yes | options: command, args?, cwd, env?, cols?, rows? (defaults 80×24) |
pty.get(id): PtyHandle | undefined |
Yes | undefined once the session has exited |
pty.list(): PtyHandle[] |
Yes | Live sessions this extension owns |
git.isRepo(cwd): Promise<boolean> |
Yes | Whether cwd is inside a repository |
git.getRepoRoot(cwd): Promise<string> |
Yes | Repository root for cwd |
git.worktree.create(cwd, opts): Promise<GitWorktree> |
Yes | opts: branch, baseRef?, path? |
git.worktree.remove(cwd, ref, opts?): Promise<void> |
Yes | opts: deleteBranch?, force? |
git.worktree.list(cwd): Promise<GitWorktree[]> |
Yes | Every worktree of the repository |
git.worktree.diff(ref, mainRepoCwd?): Promise<GitFileDiff[]> |
Yes | Per-file status and line counts |
PluginChildProcess exposes pid, stdin, stdout, stderr,
kill(signal?), and on('exit' | 'error', handler).
PtyHandle exposes id, pid, write(data), resize(cols, rows),
kill(signal?), and the disposable-returning onData(cb) and onExit(cb).
Reach for pty whenever a human or a terminal UI is on the other end;
process gives line-buffered pipes with no resize.
GitWorktree is { path, branch, head, isMain, lockReason? }. GitFileDiff is
{ path, status, oldPath?, additions, deletions, patch?, binary? }, where
patch is populated on request rather than by list operations.
ctx.api.auth
Section titled “ctx.api.auth”Permission: auth.identity. Outbound origins are declared separately as
{ "auth.outbound": [origin, …] }.
| Member | In types | Description |
|---|---|---|
getSession(): Promise<AuthSession | null> |
Yes | Cached identity; null when signed out. Never round-trips |
fetch(url, init?): Promise<AuthFetchResult> |
Yes | Signed call to your own backend, forwarded through the main process |
onChange(cb): () => void |
Yes | cb(session, reason), where reason is logged-in, logged-out, token-refreshed, or session-expired |
AuthSession is { user: { id, email, name? }, extensionId, audience, childTokenExpiresAt }. Your extension never sees WAMP’s own token; fetch
attaches a child token whose audience is ext:<extensionId> and returns
{ status, data, headers }. It throws when the URL’s origin is not in the
auth.outbound allowlist (loopback origins are auto-allowed in a dev build) or
when nobody is signed in. See
Sign in with WAMP.
ctx.api.tokens, workspace, disposables
Section titled “ctx.api.tokens, workspace, disposables”| Member | Permission | In types | Description |
|---|---|---|---|
tokens.onUsage(handler): () => void |
none | Yes | handler({ input_tokens, output_tokens, model?, conversationId? }) |
workspace.getWorkspacePath(): string | null |
none | Yes | The open workspace root, or null |
workspace.getOpenFiles(): string[] |
none | Yes | Declared, but the current host always returns an empty array |
workspace.openFile(path): Promise<void> |
none | Yes | Declared, but the current host resolves without opening anything |
disposables.add(d): void |
none | Yes | Accepts a Disposable or a plain function |
disposables.disposed: boolean |
none | Yes | Readonly; true after the host tears the extension down |
The canonical path for workspace access is
ctx.api.services.require('workspace'); ctx.api.workspace is a convenience
pass-through over that service.
ctx.api.sessions, clientAffordances
Section titled “ctx.api.sessions, clientAffordances”These two are gated on the host rather than on a permission. Both are absent on
a headless core, and clientAffordances is also absent in pure-local mode.
| Member | In types | Description |
|---|---|---|
sessions.getActive(): ActiveSession | null |
Yes | The session on screen; null on a fresh launch |
sessions.getKnownIds(): readonly string[] | null |
Yes | Every session that exists. null means the catalog has not loaded — which is not “no sessions” |
sessions.onDidChange(cb): Disposable |
Yes | Fires when the active session or the known-id set changes |
clientAffordances.register(config): Disposable |
Yes | Claims a wamp.client.<namespace>.* namespace |
ActiveSession is { id, name, context }, where context is absent until the
user picks a project. There is deliberately no session-deleted event: deletes
have an undo window and can happen while the app is closed, so reconcile
against getKnownIds() instead of listening.
ClientAffordanceConfig is { namespace, methods, rateLimit: { burst, perMinute }, singleFlight?, handle(submethod, params) }. Registering a
namespace another extension already claimed throws.
The window surface
Section titled “The window surface”pluginAPI is a closed interface: every member is required, so a typo is a
compile error rather than a runtime undefined. It is ungated apart from two
namespaces whose calls are re-checked in the main process — cloud needs
database, and mcp needs mcp. Declaring them in the manifest is not
optional; the window bundle is a convenience layer, and main decides.
pluginAPI.notify, ai, agents, skills, tasks
Section titled “pluginAPI.notify, ai, agents, skills, tasks”| Member | In types | Description |
|---|---|---|
notify(message, type?): void |
Yes | In-app toast; type is info, success, or error |
notify.success(message), .error(message), .info(message) |
Yes | The same, spelled per level |
ai.complete(input, options?): Promise<string> |
Yes | One-shot completion |
ai.stream(input, callbacks, options?): () => void |
Yes | Streams, and returns a cancel function |
ai.generateObject<T>(input, schema, options?): Promise<T> |
Yes | Schema-constrained completion |
ai.createSession(opts): Promise<AISession> |
Yes | Awaited here; synchronous in main |
ai.listModels(): Promise<ListModelsResult> |
Yes | { models, aliases } — the surface a model picker needs |
agents.delegate(agentType, task, options?): Promise<DelegationResult> |
Yes | options adds outputSchema; the result adds object and schemaError |
agents.list(): Promise<string[]> |
Yes | Agent ids available for delegation |
agents.listEntries(): Promise<AgentEntry[]> |
Yes | Full catalog with scope, triggers, tools, and skills |
agents.save(payload): Promise<{ success, error? }> |
Yes | Creates or overwrites a user- or workspace-scope agent |
agents.delete(agentId): Promise<{ success, error? }> |
Yes | Deletes a user- or workspace-scope agent |
agents.listLoadErrors(): Promise<AgentLoadErrorEntry[]> |
Yes | Agent files that currently fail to parse |
agents.listScheduled(): Promise<ScheduledAgentEntry[]> |
Yes | Cron and webhook agents with next fire time |
agents.onRegistryChanged(cb): () => void |
Yes | Fires on any registry change |
skills.list(): Promise<SkillEntry[]> |
Yes | Every installed skill |
skills.install(registryId, skillId, options?): Promise<SkillInstallResult> |
Yes | options.allowUntrusted for an untrusted registry |
skills.uninstall(registryId, skillId): Promise<SkillInstallResult> |
Yes | Removes a registry-installed skill |
skills.checkUpdates(skills): Promise<…> |
Yes | Compares installed hashes against the registries |
skills.listRegistries(): Promise<SkillRegistryEntry[]> |
Yes | Configured registries |
skills.searchRegistries(query, maxResults?): Promise<SkillSearchResult> |
Yes | { hits, errors } |
tasks.enqueue(input): Promise<{ runId }> |
Yes | runId is null when the requested agentId is not registered |
tasks.list(runId): Promise<PluginTask[]> |
Yes | Tasks for a run, in creation order |
tasks.get(runId, id): Promise<PluginTask | null> |
Yes | One task |
tasks.approveDrafts(runId): Promise<PluginTask[]> |
Yes | Promotes drafts to pending and spawns a worker run |
tasks.subscribe(runId, callbacks): () => void |
Yes | onCreated, onUpdated, onDeleted. Call the returned function or listeners leak |
Neither listModels, skills, nor agents.listEntries has a main-process
equivalent, so build pickers in the window. Agents and
skills covers delegation and the registries.
pluginAPI.data, cloud, storage
Section titled “pluginAPI.data, cloud, storage”| Member | In types | Description |
|---|---|---|
data.defineSchema(schema): DataAPI<S> |
Yes | Not awaited here; awaited in main |
cloud.mine, cloud.shared |
Yes | The same four operations as ctx.api.cloud; needs database |
storage.get<T>(key): Promise<T | null> |
Yes | Per-extension read. Returns null, where main returns undefined |
storage.set(key, value): Promise<void> |
Yes | Write |
The window’s storage has no delete or keys — do those from the main half.
pluginAPI.fs, dialog, terminal, http
Section titled “pluginAPI.fs, dialog, terminal, http”| Member | In types | Description |
|---|---|---|
fs.read(path): Promise<string> |
Yes | Named readFile in main |
fs.write(path, content): Promise<void> |
Yes | Named writeFile in main |
fs.listDir(path): Promise<DirEntry[]> |
Yes | { name, path, isDirectory } per entry |
fs.exists(path): Promise<boolean> |
Yes | Existence check |
fs.stat(path): Promise<FileStat | null> |
Yes | { type, size, mtimeMs, ctimeMs }, or null when absent or outside the workspace |
fs.mkdir(path): Promise<void> |
Yes | Create |
fs.delete(path): Promise<void> |
Yes | Named remove in main |
fs.move(sourcePath, destPath): Promise<void> |
Yes | No main-process equivalent |
dialog.openFile(options?): Promise<string[]> |
Yes | options: filters, multiple |
dialog.openFolder(): Promise<string | null> |
Yes | No main-process equivalent |
dialog.saveFile(options?): Promise<string | null> |
Yes | options: title, defaultPath, filters |
terminal.exec(command, args, opts?): Promise<ExecResult> |
Yes | An argv array, not a shell string. Returns { stdout, stderr, code } |
http.fetch(url, options?): Promise<FetchResult> |
Yes | Routed through the main process, so CORS does not apply. Returns { status, data, headers } |
The window’s fs goes through the host’s file service and its path rules; the
main-process fs does not. It also has no path helpers — join, dirname,
basename, extname, and getDataPath exist only on ctx.api.fs.
pluginAPI.ui, navigation, shell, workspace, tools, events, mcp, auth
Section titled “pluginAPI.ui, navigation, shell, workspace, tools, events, mcp, auth”| Member | In types | Description |
|---|---|---|
ui.showExtension(pluginId): Promise<void> |
Yes | Surfaces another extension’s primary view |
ui.exitAppMode(): void |
Yes | Leaves a presentation: 'app' page for the previous docked view |
navigation.go(pageId, params?): void |
Yes | Unknown ids no-op with a console warning |
navigation.back(): void |
Yes | Pops the navigation history |
navigation.params(): Record<string, unknown> |
Yes | Params from the most recent go |
shell.openExternal(url): Promise<void> |
Yes | Opens in the OS browser; a rejected scheme throws |
workspace.path(): string | null |
Yes | Workspace root, or null |
workspace.pluginsPath(): Promise<string> |
Yes | Where extensions are installed |
tools.call(toolName, input?): Promise<unknown> |
Yes | Invokes one of this extension’s own registered tools |
events.on(event, callback): () => void |
Yes | event is file:changed or ai:tool-use |
mcp.listServers(): Promise<McpServerInfo[]> |
Yes | { name, displayName, status, toolCount }. Needs mcp |
mcp.setEnabled(name, enabled): Promise<McpSetEnabledResult> |
Yes | App-wide start/stop. Needs mcp |
auth.getSession(): Promise<AuthSession | null> |
Yes | Same shape as the main-process member |
auth.fetch(url, init?): Promise<AuthFetchResult> |
Yes | Requires the target origin in auth.outbound |
auth.onChange(cb): () => void |
Yes | Session lifecycle events |
Never call window.alert, prompt, or confirm — they are blocked. Use
<Dialog> from @wamp/ui; see Interface kit.
The window’s activate host
Section titled “The window’s activate host”A UI bundle may export activate(host) to register renderer-side
contributions. None of this surface is in the published types, so an
extension that uses it declares the shape itself. It is the only way to reach
tool-card rendering, chat blocks, and dock tabs.
| Member | In types | Description |
|---|---|---|
host.ownerId: string |
No | The extension’s own id |
host.tools.registerCardRenderer(displayKind, component) |
No | Renders tool-use cards for a display.kind with your own React component |
host.chat.registerBlockKind(kind) |
No | Registers a chat-input block kind with a chip and a serializer |
host.chat.insertBlock(block) |
No | Inserts a block into the active chat draft |
host.dock.setTabs(viewId, provider) |
No | Publishes a session.dock view’s tabs into the dock’s tab row |
host.dock.clearTabs(viewId) |
No | Removes them; an empty list removes the view from the strip |
Each registration returns a disposable, and activate may return an object with
dispose() that the host calls on unload.
window.electronAPI.extensionIPC
Section titled “window.electronAPI.extensionIPC”The bridge between an extension’s two halves, and the one capability reached
through a global rather than an import. It is declared in the SDK’s ambient
globals; every other member of window.electronAPI is deliberately not, and
stays a type error.
| Member | In types | Description |
|---|---|---|
invoke<TResult>(channel, ...args): Promise<TResult> |
Yes | Calls a handler your main half registered with ipc.handle. Rejects when none is registered, which the host reports as an extension fault |
subscribe<TPayload>(channel, handler): { dispose() } |
Yes | Listens for ipc.broadcast from the main half |
The channel prefix before : must be a namespace listed in the manifest’s
contributes.ipcNamespaces. There is no renderer-side broadcast.
- The plugin API — the same surfaces, organized by task.
- Permissions — declaring them, and writing for absence.
- Manifest reference — every field, including
contributesandcapabilities. - Troubleshooting — what a specific error means.