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
Requiresentry above means to declare - Events — the payloads a hook’s
eventargument carries
