Skip to content

Plugin context (ctx)

Every handler receives ctx as its second argument — (params, ctx) for a tool, (event, ctx) for a hook. A capability the manifest’s permissions doesn’t declare is still a member of ctx, but calling it throws a permission error whose message names the plugin and the capability with the reason not-declared. Two exceptions: ctx.toast and ctx.store are undefined when not declared, and ctx.log without log: true silently discards everything — no error, no output.

Member Signature Requires
ctx.plugin { name, version, dataDir }
ctx.settings.get / set / getAll get(key): Promise<unknown>, set(key, value), getAll(): Promise<Record<string, unknown>>getAll returns the keys under this plugin’s prefix settings: true
ctx.fetch the global fetch — external hosts only; localhost and private ranges are refused fetch { static?, settings?, deny? }
ctx.fs.read / write / list / exists read(path): Promise<Buffer>, write(path, data), list(dir): Promise<string[]>, exists(path): Promise<boolean> (feature-check on older hosts) fs.read / fs.write / fs.list
ctx.store.get / set / list / delete per-plugin key/value store store: true
ctx.toast ({ type: 'info' | 'error' | 'success', message }) toast: true
ctx.log.info / warn / error (msg, ...args) log: true
ctx.execute (commandId, args?): Promise<unknown> — another plugin’s command as other-plugin.commandId
ctx.session.getActive / sendMessage getActive(): Promise<{ id, title, origin?, workingPath? } | null>, sendMessage(sessionId, message) session { getActive?, sendMessage? }
ctx.channels.getIntegrationConfig / setIntegrationConfig (channelId, pluginName) → config or null; (channelId, pluginName, config) channels { … }
ctx.triggerInbound (payload, signal?): Promise<Response> — routes an inbound channel message to Filer’s own host hostTrigger: true
ctx.listChannels (signal?): Promise<ChannelListEntry[]> hostTrigger: true
ctx.getSessionHistory (sessionId, signal?) hostTrigger: true
ctx.respondToHitl (agentId, requestId, approved, reason, signal?) hostTrigger: true
ctx.ai.complete (prompt, options?, signal?): Promise<string> — one tool-free completion on the user’s configured model ai { complete: true }
ctx.renderHtmlToPdf (html): Promise<Buffer> renderHtmlToPdf: true
ctx.viewData.set / get view data for contributes.views
ctx.channelId the channel the current tool call belongs to
ctx.signal AbortSignal for the handler’s time budget — always present; execution { … } only sets the budget

ctx.fs.exists is worth checking before relying on it: a host old enough not to have it simply omits it from ctx.fs, so typeof ctx.fs.exists === 'function' is a safe feature-check for code that needs to run against older Filer installs.

ctx.signal is an AbortSignal tied to the handler’s own time budget — present on every ctx, with a default budget when the manifest declares no execution; execution.timeout / maxTimeout only change the numbers. Pass it to anything that accepts one — fetch(url, { signal: ctx.signal }), for example — so a timed-out call is actually cancelled instead of left running after the handler has already failed.

  • Permissions — what each Requires entry above means to declare
  • Events — the payloads a hook’s event argument carries