Repository navigation
ctx.flybot: extension API in ds-bot for FlyBot plugins #4
Copy link
Copy link
Closed
Description
Part of #3 (umbrella). Implemented in #2.
Goal
Give ds-bot a stable, documented extension API so FlyBot features ship as plugins under ds-bot/plugins/ without patching ds-bot internals.
Design
- Host service
flybotimplemented inds-bot/src/host/hooks.js, registered withctx.provide('flybot')(required by Cordis 4;ctx.setalone fails withcannot set property "flybot" without provide). - Plugins declare
inject: ['flybot']and callctx.flybot.scope('<plugin-name>')to get a handle whose fields and events are namespaced to that plugin. The unscoped service uses thesharednamespace.
API surface
Fields
fields.get(botId, key),fields.set(botId, key, value)(synchronous; persisted in the background;nulldeletes),fields.all(botId),fields.query(key, value?).- Stored as
state.bots[id].fields[plugin][key]in$DSH_HOME/bot/state.json. on('change', fn, { all? })->{ botId, plugin, key, value, prev }; only your own plugin's fields unless{ all: true }.
Bots
bot.list()(local bots only),bot.get,bot.find,bot.of(sessionId),bot.isRemote(ref).bot.create({ name, systemPrompt, fields, role?, instructions? }) -> Promise<id>,bot.setSystemPrompt(id, text).- Events:
bot.created(new record),bot.removed(fires after removal; payload carries the last record including fields, sobot.list()no longer contains it).
Sending
bot.send({ from, to, text, role?, hop?, meta?, attachments? })->{ action: 'allow' | 'hold' | 'deny', ... }.- Remote targets:
'@handle'or{ kind: 'remote', handle, home? }. - Middleware:
bot.send.use({ name, before?, after?, handle(msg) }).msg.targetis{ remote: false, id }or{ remote: true, handle, home }.undefined/allowcontinues;denyandholdstop the chain; a throw counts as deny.allowwithbypass: ['social']only takes effect ifconfig.flybot.bypass[name]lists it.
- Every built-in bot-to-bot send (
message_bot,stop_bot, replies) goes through the chain. The gate wrapsrt.deliver. - Held messages become owner approval cards (
flybot.approvals).
Tools, transports, approvals, API
tools.register({ name, parameters, visibleTo(botId), execute(args, { botId }) }); JSON Schema parameters are converted to DSH's format (minLength/maxLengthmust be checked inexecute).transports.add(kind, { send }).approvals.list/get/decide/onChange— decisions are owner-only from the UI, never a model tool.api.handle(name, fn), exposed asflybot/<name>on the existingapi/botroute.remote.publish/list/getfor remote members.
Browser
ctx.flybot.bots()(items includefields),ctx.flybot.fields.on('change', fn).- UI slots:
flybot.sidebar.section,flybot.composer.mention,flybot.bot.details,flybot.approvals.
Types
ds-bot/types/flybot.d.ts, exported viapackage.json(types,./types). Checks clean withtsc --noEmit.
Files changed in ds-bot
index.js, src/host/{hooks.js (new), delivery.js, tools.js, channel.js, roster.js, store.js, prompt.js}, src/client/{flybot-registry.js, flybot-slots.js (new), sidebar.js, at-menu.js, bot-settings.js, index.js}, rebuilt client.js, test/fake-ctx.js, scripts/lint.mjs (lints plugins/), package.json.
Tests
- New API tests plus a Cordis 4
ctx.providetest. Full suite 567/567 on Node 22 and 24; lint 0 errors;build-client.mjs --checkpasses.
Open items
- Hide tools from a bot's tool list when
visibleTois false (today only enforced at execute time). - Push browser field events instead of polling every 3 s.
-
viais set by the calling plugin and could be spoofed by another plugin. - Held messages are not re-queued after a restart.
-
fake-ctxshould enforce real Cordis semantics so regressions like theprovidebug are caught.
Metadata
Metadata
Assignees
Labels
No labels
Activity
ctx.flybot tools: visibleTo also removes the tool from a Bot's tool l…
Merge pull request #22 from Birfy/flybot-plugins
test(self-test): check the catalog page against docs/self-test.json (#…