durar-ai 2026.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5497 -0
- package/LICENSE +21 -0
- package/README.md +614 -0
- package/assets/avatar-placeholder.svg +19 -0
- package/assets/chrome-extension/icons/icon128.png +0 -0
- package/assets/chrome-extension/icons/icon16.png +0 -0
- package/assets/chrome-extension/icons/icon32.png +0 -0
- package/assets/chrome-extension/icons/icon48.png +0 -0
- package/assets/dmg-background-small.png +0 -0
- package/assets/dmg-background.png +0 -0
- package/docs/.i18n/README.md +72 -0
- package/docs/.i18n/ar-navigation.json +18 -0
- package/docs/.i18n/de-navigation.json +18 -0
- package/docs/.i18n/es-navigation.json +18 -0
- package/docs/.i18n/fr-navigation.json +18 -0
- package/docs/.i18n/glossary.ar.json +5 -0
- package/docs/.i18n/glossary.de.json +5 -0
- package/docs/.i18n/glossary.es.json +5 -0
- package/docs/.i18n/glossary.fr.json +5 -0
- package/docs/.i18n/glossary.id.json +5 -0
- package/docs/.i18n/glossary.it.json +5 -0
- package/docs/.i18n/glossary.ja-JP.json +14 -0
- package/docs/.i18n/glossary.ko.json +5 -0
- package/docs/.i18n/glossary.pl.json +5 -0
- package/docs/.i18n/glossary.pt-BR.json +5 -0
- package/docs/.i18n/glossary.tr.json +5 -0
- package/docs/.i18n/glossary.zh-CN.json +358 -0
- package/docs/.i18n/id-navigation.json +18 -0
- package/docs/.i18n/it-navigation.json +18 -0
- package/docs/.i18n/ja-navigation.json +18 -0
- package/docs/.i18n/ko-navigation.json +18 -0
- package/docs/.i18n/pl-navigation.json +18 -0
- package/docs/.i18n/pt-BR-navigation.json +18 -0
- package/docs/.i18n/tr-navigation.json +18 -0
- package/docs/.i18n/zh-Hans-navigation.json +544 -0
- package/docs/assets/install-script.svg +1 -0
- package/docs/assets/macos-onboarding/01-macos-warning.jpeg +0 -0
- package/docs/assets/macos-onboarding/02-local-networks.jpeg +0 -0
- package/docs/assets/macos-onboarding/03-security-notice.png +0 -0
- package/docs/assets/macos-onboarding/04-choose-gateway.png +0 -0
- package/docs/assets/macos-onboarding/05-permissions.png +0 -0
- package/docs/assets/openclaw-logo-text-dark.png +0 -0
- package/docs/assets/openclaw-logo-text-dark.svg +418 -0
- package/docs/assets/openclaw-logo-text.png +0 -0
- package/docs/assets/openclaw-logo-text.svg +418 -0
- package/docs/assets/pixel-lobster.svg +60 -0
- package/docs/assets/showcase/agents-ui.jpg +0 -0
- package/docs/assets/showcase/bambu-cli.png +0 -0
- package/docs/assets/showcase/codexmonitor.png +0 -0
- package/docs/assets/showcase/gohome-grafana.png +0 -0
- package/docs/assets/showcase/ios-testflight.jpg +0 -0
- package/docs/assets/showcase/oura-health.png +0 -0
- package/docs/assets/showcase/padel-cli.svg +11 -0
- package/docs/assets/showcase/padel-screenshot.jpg +0 -0
- package/docs/assets/showcase/papla-tts.jpg +0 -0
- package/docs/assets/showcase/pr-review-telegram.jpg +0 -0
- package/docs/assets/showcase/roborock-screenshot.jpg +0 -0
- package/docs/assets/showcase/roborock-status.svg +13 -0
- package/docs/assets/showcase/roof-camera-sky.jpg +0 -0
- package/docs/assets/showcase/snag.png +0 -0
- package/docs/assets/showcase/tesco-shop.jpg +0 -0
- package/docs/assets/showcase/wienerlinien.png +0 -0
- package/docs/assets/showcase/wine-cellar-skill.jpg +0 -0
- package/docs/assets/showcase/winix-air-purifier.jpg +0 -0
- package/docs/assets/showcase/xuezh-pronunciation.jpeg +0 -0
- package/docs/assets/sponsors/blacksmith-light.svg +14 -0
- package/docs/assets/sponsors/blacksmith.svg +14 -0
- package/docs/assets/sponsors/convex-light.svg +16 -0
- package/docs/assets/sponsors/convex.svg +16 -0
- package/docs/assets/sponsors/github-light.svg +3 -0
- package/docs/assets/sponsors/github.svg +3 -0
- package/docs/assets/sponsors/nvidia-dark.svg +9 -0
- package/docs/assets/sponsors/nvidia.svg +9 -0
- package/docs/assets/sponsors/openai-light.svg +3 -0
- package/docs/assets/sponsors/openai.svg +3 -0
- package/docs/assets/sponsors/vercel-light.svg +5 -0
- package/docs/assets/sponsors/vercel.svg +5 -0
- package/docs/auth-credential-semantics.md +80 -0
- package/docs/automation/auth-monitoring.md +8 -0
- package/docs/automation/clawflow.md +8 -0
- package/docs/automation/cron-jobs.md +410 -0
- package/docs/automation/cron-vs-heartbeat.md +8 -0
- package/docs/automation/gmail-pubsub.md +8 -0
- package/docs/automation/hooks.md +303 -0
- package/docs/automation/index.md +115 -0
- package/docs/automation/poll.md +8 -0
- package/docs/automation/standing-orders.md +254 -0
- package/docs/automation/taskflow.md +82 -0
- package/docs/automation/tasks.md +323 -0
- package/docs/automation/troubleshooting.md +8 -0
- package/docs/automation/webhook.md +8 -0
- package/docs/brave-search.md +103 -0
- package/docs/channels/bluebubbles.md +435 -0
- package/docs/channels/broadcast-groups.md +442 -0
- package/docs/channels/channel-routing.md +139 -0
- package/docs/channels/discord.md +1254 -0
- package/docs/channels/feishu.md +793 -0
- package/docs/channels/googlechat.md +270 -0
- package/docs/channels/group-messages.md +84 -0
- package/docs/channels/groups.md +410 -0
- package/docs/channels/imessage.md +427 -0
- package/docs/channels/index.md +50 -0
- package/docs/channels/irc.md +252 -0
- package/docs/channels/line.md +225 -0
- package/docs/channels/location.md +56 -0
- package/docs/channels/matrix.md +869 -0
- package/docs/channels/mattermost.md +472 -0
- package/docs/channels/msteams.md +805 -0
- package/docs/channels/nextcloud-talk.md +149 -0
- package/docs/channels/nostr.md +252 -0
- package/docs/channels/pairing.md +129 -0
- package/docs/channels/qqbot.md +193 -0
- package/docs/channels/signal.md +337 -0
- package/docs/channels/slack.md +681 -0
- package/docs/channels/synology-chat.md +185 -0
- package/docs/channels/telegram.md +1072 -0
- package/docs/channels/tlon.md +290 -0
- package/docs/channels/troubleshooting.md +133 -0
- package/docs/channels/twitch.md +394 -0
- package/docs/channels/whatsapp.md +488 -0
- package/docs/channels/zalo.md +254 -0
- package/docs/channels/zalouser.md +195 -0
- package/docs/ci.md +66 -0
- package/docs/cli/acp.md +316 -0
- package/docs/cli/agent.md +57 -0
- package/docs/cli/agents.md +220 -0
- package/docs/cli/approvals.md +136 -0
- package/docs/cli/backup.md +84 -0
- package/docs/cli/browser.md +233 -0
- package/docs/cli/channels.md +131 -0
- package/docs/cli/clawbot.md +21 -0
- package/docs/cli/completion.md +35 -0
- package/docs/cli/config.md +353 -0
- package/docs/cli/configure.md +70 -0
- package/docs/cli/cron.md +167 -0
- package/docs/cli/daemon.md +57 -0
- package/docs/cli/dashboard.md +22 -0
- package/docs/cli/devices.md +171 -0
- package/docs/cli/directory.md +63 -0
- package/docs/cli/dns.md +48 -0
- package/docs/cli/docs.md +28 -0
- package/docs/cli/doctor.md +63 -0
- package/docs/cli/flows.md +18 -0
- package/docs/cli/gateway.md +307 -0
- package/docs/cli/health.md +36 -0
- package/docs/cli/hooks.md +337 -0
- package/docs/cli/index.md +1836 -0
- package/docs/cli/logs.md +59 -0
- package/docs/cli/mcp.md +505 -0
- package/docs/cli/memory.md +139 -0
- package/docs/cli/message.md +300 -0
- package/docs/cli/models.md +136 -0
- package/docs/cli/node.md +137 -0
- package/docs/cli/nodes.md +66 -0
- package/docs/cli/onboard.md +171 -0
- package/docs/cli/pairing.md +65 -0
- package/docs/cli/plugins.md +305 -0
- package/docs/cli/qr.md +52 -0
- package/docs/cli/reset.md +35 -0
- package/docs/cli/sandbox.md +197 -0
- package/docs/cli/secrets.md +197 -0
- package/docs/cli/security.md +86 -0
- package/docs/cli/sessions.md +113 -0
- package/docs/cli/setup.md +45 -0
- package/docs/cli/skills.md +59 -0
- package/docs/cli/status.md +35 -0
- package/docs/cli/system.md +71 -0
- package/docs/cli/tui.md +30 -0
- package/docs/cli/uninstall.md +39 -0
- package/docs/cli/update.md +113 -0
- package/docs/cli/voicecall.md +34 -0
- package/docs/cli/webhooks.md +91 -0
- package/docs/concepts/agent-loop.md +168 -0
- package/docs/concepts/agent-workspace.md +246 -0
- package/docs/concepts/agent.md +129 -0
- package/docs/concepts/architecture.md +156 -0
- package/docs/concepts/compaction.md +122 -0
- package/docs/concepts/context-engine.md +274 -0
- package/docs/concepts/context.md +179 -0
- package/docs/concepts/delegate-architecture.md +307 -0
- package/docs/concepts/dreaming.md +173 -0
- package/docs/concepts/features.md +76 -0
- package/docs/concepts/markdown-formatting.md +130 -0
- package/docs/concepts/memory-builtin.md +105 -0
- package/docs/concepts/memory-honcho.md +140 -0
- package/docs/concepts/memory-qmd.md +163 -0
- package/docs/concepts/memory-search.md +141 -0
- package/docs/concepts/memory.md +121 -0
- package/docs/concepts/messages.md +161 -0
- package/docs/concepts/model-failover.md +349 -0
- package/docs/concepts/model-providers.md +799 -0
- package/docs/concepts/models.md +255 -0
- package/docs/concepts/multi-agent.md +615 -0
- package/docs/concepts/oauth.md +225 -0
- package/docs/concepts/presence.md +102 -0
- package/docs/concepts/queue.md +89 -0
- package/docs/concepts/retry.md +69 -0
- package/docs/concepts/session-pruning.md +92 -0
- package/docs/concepts/session-tool.md +141 -0
- package/docs/concepts/session.md +116 -0
- package/docs/concepts/soul.md +110 -0
- package/docs/concepts/streaming.md +161 -0
- package/docs/concepts/system-prompt.md +182 -0
- package/docs/concepts/timezone.md +97 -0
- package/docs/concepts/typebox.md +307 -0
- package/docs/concepts/typing-indicators.md +69 -0
- package/docs/concepts/usage-tracking.md +59 -0
- package/docs/date-time.md +128 -0
- package/docs/debug/node-issue.md +85 -0
- package/docs/diagnostics/flags.md +91 -0
- package/docs/docs.json +1601 -0
- package/docs/gateway/authentication.md +218 -0
- package/docs/gateway/background-process.md +131 -0
- package/docs/gateway/bonjour.md +179 -0
- package/docs/gateway/bridge-protocol.md +89 -0
- package/docs/gateway/cli-backends.md +310 -0
- package/docs/gateway/configuration-examples.md +631 -0
- package/docs/gateway/configuration-reference.md +3618 -0
- package/docs/gateway/configuration.md +698 -0
- package/docs/gateway/discovery.md +141 -0
- package/docs/gateway/doctor.md +494 -0
- package/docs/gateway/gateway-lock.md +37 -0
- package/docs/gateway/health.md +61 -0
- package/docs/gateway/heartbeat.md +443 -0
- package/docs/gateway/index.md +367 -0
- package/docs/gateway/local-models.md +163 -0
- package/docs/gateway/logging.md +113 -0
- package/docs/gateway/multiple-gateways.md +120 -0
- package/docs/gateway/network-model.md +25 -0
- package/docs/gateway/openai-http-api.md +280 -0
- package/docs/gateway/openresponses-http-api.md +340 -0
- package/docs/gateway/openshell.md +307 -0
- package/docs/gateway/pairing.md +138 -0
- package/docs/gateway/protocol.md +588 -0
- package/docs/gateway/remote-gateway-readme.md +164 -0
- package/docs/gateway/remote.md +251 -0
- package/docs/gateway/sandbox-vs-tool-policy-vs-elevated.md +141 -0
- package/docs/gateway/sandboxing.md +473 -0
- package/docs/gateway/secrets-plan-contract.md +116 -0
- package/docs/gateway/secrets.md +541 -0
- package/docs/gateway/security/index.md +1362 -0
- package/docs/gateway/tailscale.md +136 -0
- package/docs/gateway/tools-invoke-http-api.md +161 -0
- package/docs/gateway/troubleshooting.md +451 -0
- package/docs/gateway/trusted-proxy-auth.md +399 -0
- package/docs/help/debugging.md +168 -0
- package/docs/help/environment.md +165 -0
- package/docs/help/faq.md +3244 -0
- package/docs/help/index.md +28 -0
- package/docs/help/scripts.md +27 -0
- package/docs/help/testing.md +640 -0
- package/docs/help/troubleshooting.md +372 -0
- package/docs/images/configure-model-picker-unsearchable.png +0 -0
- package/docs/images/feishu-step2-create-app.png +0 -0
- package/docs/images/feishu-step3-credentials.png +0 -0
- package/docs/images/feishu-step4-permissions.png +0 -0
- package/docs/images/feishu-step5-bot-capability.png +0 -0
- package/docs/images/feishu-step6-event-subscription.png +0 -0
- package/docs/images/feishu-verification-token.png +0 -0
- package/docs/images/groups-flow.svg +52 -0
- package/docs/images/mobile-ui-screenshot.png +0 -0
- package/docs/index.md +196 -0
- package/docs/install/ansible.md +230 -0
- package/docs/install/azure.md +311 -0
- package/docs/install/bun.md +55 -0
- package/docs/install/clawdock.md +106 -0
- package/docs/install/development-channels.md +131 -0
- package/docs/install/digitalocean.md +129 -0
- package/docs/install/docker-vm-runtime.md +142 -0
- package/docs/install/docker.md +412 -0
- package/docs/install/exe-dev.md +133 -0
- package/docs/install/fly.md +504 -0
- package/docs/install/gcp.md +412 -0
- package/docs/install/hetzner.md +259 -0
- package/docs/install/index.md +212 -0
- package/docs/install/installer.md +443 -0
- package/docs/install/kubernetes.md +192 -0
- package/docs/install/macos-vm.md +281 -0
- package/docs/install/migrating-matrix.md +349 -0
- package/docs/install/migrating.md +112 -0
- package/docs/install/nix.md +89 -0
- package/docs/install/node.md +144 -0
- package/docs/install/northflank.mdx +42 -0
- package/docs/install/oracle.md +158 -0
- package/docs/install/podman.md +210 -0
- package/docs/install/railway.mdx +90 -0
- package/docs/install/raspberry-pi.md +159 -0
- package/docs/install/render.mdx +165 -0
- package/docs/install/uninstall.md +128 -0
- package/docs/install/updating.md +142 -0
- package/docs/logging.md +389 -0
- package/docs/nav-tabs-underline.js +100 -0
- package/docs/network.md +69 -0
- package/docs/nodes/audio.md +191 -0
- package/docs/nodes/camera.md +162 -0
- package/docs/nodes/images.md +73 -0
- package/docs/nodes/index.md +408 -0
- package/docs/nodes/location-command.md +98 -0
- package/docs/nodes/media-understanding.md +432 -0
- package/docs/nodes/talk.md +92 -0
- package/docs/nodes/troubleshooting.md +123 -0
- package/docs/nodes/voicewake.md +66 -0
- package/docs/perplexity.md +181 -0
- package/docs/pi-dev.md +80 -0
- package/docs/pi.md +570 -0
- package/docs/platforms/android.md +244 -0
- package/docs/platforms/digitalocean.md +266 -0
- package/docs/platforms/index.md +55 -0
- package/docs/platforms/ios.md +223 -0
- package/docs/platforms/linux.md +100 -0
- package/docs/platforms/mac/bundled-gateway.md +75 -0
- package/docs/platforms/mac/canvas.md +125 -0
- package/docs/platforms/mac/child-process.md +69 -0
- package/docs/platforms/mac/dev-setup.md +107 -0
- package/docs/platforms/mac/health.md +34 -0
- package/docs/platforms/mac/icon.md +31 -0
- package/docs/platforms/mac/logging.md +57 -0
- package/docs/platforms/mac/menu-bar.md +81 -0
- package/docs/platforms/mac/peekaboo.md +65 -0
- package/docs/platforms/mac/permissions.md +50 -0
- package/docs/platforms/mac/remote.md +84 -0
- package/docs/platforms/mac/signing.md +47 -0
- package/docs/platforms/mac/skills.md +40 -0
- package/docs/platforms/mac/voice-overlay.md +60 -0
- package/docs/platforms/mac/voicewake.md +67 -0
- package/docs/platforms/mac/webchat.md +51 -0
- package/docs/platforms/mac/xpc.md +61 -0
- package/docs/platforms/macos.md +229 -0
- package/docs/platforms/oracle.md +305 -0
- package/docs/platforms/raspberry-pi.md +420 -0
- package/docs/platforms/windows.md +241 -0
- package/docs/plugins/agent-tools.md +10 -0
- package/docs/plugins/architecture.md +1609 -0
- package/docs/plugins/building-extensions.md +10 -0
- package/docs/plugins/building-plugins.md +319 -0
- package/docs/plugins/bundles.md +292 -0
- package/docs/plugins/community.md +149 -0
- package/docs/plugins/manifest.md +412 -0
- package/docs/plugins/sdk-channel-plugins.md +508 -0
- package/docs/plugins/sdk-entrypoints.md +210 -0
- package/docs/plugins/sdk-migration.md +359 -0
- package/docs/plugins/sdk-overview.md +475 -0
- package/docs/plugins/sdk-provider-plugins.md +712 -0
- package/docs/plugins/sdk-runtime.md +381 -0
- package/docs/plugins/sdk-setup.md +516 -0
- package/docs/plugins/sdk-testing.md +263 -0
- package/docs/plugins/voice-call.md +466 -0
- package/docs/plugins/zalouser.md +78 -0
- package/docs/prose.md +134 -0
- package/docs/providers/anthropic.md +402 -0
- package/docs/providers/bedrock-mantle.md +91 -0
- package/docs/providers/bedrock.md +273 -0
- package/docs/providers/chutes.md +103 -0
- package/docs/providers/claude-max-api-proxy.md +163 -0
- package/docs/providers/cloudflare-ai-gateway.md +71 -0
- package/docs/providers/deepgram.md +93 -0
- package/docs/providers/deepseek.md +53 -0
- package/docs/providers/fireworks.md +69 -0
- package/docs/providers/github-copilot.md +80 -0
- package/docs/providers/glm.md +68 -0
- package/docs/providers/google.md +149 -0
- package/docs/providers/groq.md +105 -0
- package/docs/providers/huggingface.md +193 -0
- package/docs/providers/index.md +81 -0
- package/docs/providers/kilocode.md +89 -0
- package/docs/providers/litellm.md +159 -0
- package/docs/providers/minimax.md +281 -0
- package/docs/providers/mistral.md +68 -0
- package/docs/providers/models.md +56 -0
- package/docs/providers/moonshot.md +224 -0
- package/docs/providers/nvidia.md +58 -0
- package/docs/providers/ollama.md +379 -0
- package/docs/providers/openai.md +472 -0
- package/docs/providers/opencode-go.md +45 -0
- package/docs/providers/opencode.md +68 -0
- package/docs/providers/openrouter.md +59 -0
- package/docs/providers/perplexity-provider.md +62 -0
- package/docs/providers/qianfan.md +90 -0
- package/docs/providers/qwen.md +128 -0
- package/docs/providers/qwen_modelstudio.md +137 -0
- package/docs/providers/sglang.md +115 -0
- package/docs/providers/stepfun.md +152 -0
- package/docs/providers/synthetic.md +101 -0
- package/docs/providers/together.md +70 -0
- package/docs/providers/venice.md +282 -0
- package/docs/providers/vercel-ai-gateway.md +60 -0
- package/docs/providers/vllm.md +103 -0
- package/docs/providers/volcengine.md +94 -0
- package/docs/providers/xai.md +94 -0
- package/docs/providers/xiaomi.md +89 -0
- package/docs/providers/zai.md +75 -0
- package/docs/reference/AGENTS.default.md +126 -0
- package/docs/reference/RELEASING.md +138 -0
- package/docs/reference/api-usage-costs.md +198 -0
- package/docs/reference/credits.md +30 -0
- package/docs/reference/device-models.md +47 -0
- package/docs/reference/memory-config.md +421 -0
- package/docs/reference/prompt-caching.md +344 -0
- package/docs/reference/rpc.md +43 -0
- package/docs/reference/secretref-credential-surface.md +148 -0
- package/docs/reference/secretref-user-supplied-credentials-matrix.json +607 -0
- package/docs/reference/session-management-compaction.md +352 -0
- package/docs/reference/templates/AGENTS.dev.md +84 -0
- package/docs/reference/templates/AGENTS.md +219 -0
- package/docs/reference/templates/BOOT.md +12 -0
- package/docs/reference/templates/BOOTSTRAP.md +62 -0
- package/docs/reference/templates/CLAUDE.md +1 -0
- package/docs/reference/templates/HEARTBEAT.md +14 -0
- package/docs/reference/templates/IDENTITY.dev.md +48 -0
- package/docs/reference/templates/IDENTITY.md +30 -0
- package/docs/reference/templates/SOUL.dev.md +77 -0
- package/docs/reference/templates/SOUL.md +45 -0
- package/docs/reference/templates/TOOLS.dev.md +25 -0
- package/docs/reference/templates/TOOLS.md +47 -0
- package/docs/reference/templates/USER.dev.md +19 -0
- package/docs/reference/templates/USER.md +24 -0
- package/docs/reference/test.md +119 -0
- package/docs/reference/token-use.md +197 -0
- package/docs/reference/transcript-hygiene.md +151 -0
- package/docs/reference/wizard.md +245 -0
- package/docs/security/CONTRIBUTING-THREAT-MODEL.md +98 -0
- package/docs/security/THREAT-MODEL-ATLAS.md +608 -0
- package/docs/security/formal-verification.md +167 -0
- package/docs/snippets/plugin-publish/minimal-openclaw.plugin.json +9 -0
- package/docs/snippets/plugin-publish/minimal-package.json +16 -0
- package/docs/start/bootstrapping.md +41 -0
- package/docs/start/docs-directory.md +67 -0
- package/docs/start/getting-started.md +148 -0
- package/docs/start/hubs.md +199 -0
- package/docs/start/lore.md +219 -0
- package/docs/start/onboarding-overview.md +69 -0
- package/docs/start/onboarding.md +92 -0
- package/docs/start/openclaw.md +225 -0
- package/docs/start/quickstart.md +22 -0
- package/docs/start/setup.md +172 -0
- package/docs/start/showcase.md +418 -0
- package/docs/start/wizard-cli-automation.md +233 -0
- package/docs/start/wizard-cli-reference.md +324 -0
- package/docs/start/wizard.md +127 -0
- package/docs/style.css +37 -0
- package/docs/tools/acp-agents.md +837 -0
- package/docs/tools/agent-send.md +100 -0
- package/docs/tools/apply-patch.md +52 -0
- package/docs/tools/brave-search.md +107 -0
- package/docs/tools/browser-linux-troubleshooting.md +145 -0
- package/docs/tools/browser-login.md +73 -0
- package/docs/tools/browser-wsl2-windows-remote-cdp-troubleshooting.md +221 -0
- package/docs/tools/browser.md +890 -0
- package/docs/tools/btw.md +142 -0
- package/docs/tools/capability-cookbook.md +119 -0
- package/docs/tools/clawhub.md +348 -0
- package/docs/tools/code-execution.md +90 -0
- package/docs/tools/creating-skills.md +119 -0
- package/docs/tools/diffs.md +434 -0
- package/docs/tools/duckduckgo-search.md +102 -0
- package/docs/tools/elevated.md +116 -0
- package/docs/tools/exa-search.md +127 -0
- package/docs/tools/exec-approvals.md +635 -0
- package/docs/tools/exec.md +237 -0
- package/docs/tools/firecrawl.md +147 -0
- package/docs/tools/gemini-search.md +98 -0
- package/docs/tools/grok-search.md +102 -0
- package/docs/tools/image-generation.md +139 -0
- package/docs/tools/index.md +174 -0
- package/docs/tools/kimi-search.md +98 -0
- package/docs/tools/llm-task.md +119 -0
- package/docs/tools/lobster.md +348 -0
- package/docs/tools/loop-detection.md +100 -0
- package/docs/tools/minimax-search.md +99 -0
- package/docs/tools/multi-agent-sandbox-tools.md +373 -0
- package/docs/tools/ollama-search.md +100 -0
- package/docs/tools/pdf.md +176 -0
- package/docs/tools/perplexity-search.md +185 -0
- package/docs/tools/plugin.md +348 -0
- package/docs/tools/reactions.md +78 -0
- package/docs/tools/searxng-search.md +132 -0
- package/docs/tools/skills-config.md +133 -0
- package/docs/tools/skills.md +377 -0
- package/docs/tools/slash-commands.md +322 -0
- package/docs/tools/subagents.md +341 -0
- package/docs/tools/tavily.md +129 -0
- package/docs/tools/thinking.md +102 -0
- package/docs/tools/tts.md +452 -0
- package/docs/tools/web-fetch.md +159 -0
- package/docs/tools/web.md +417 -0
- package/docs/tts.md +452 -0
- package/docs/vps.md +115 -0
- package/docs/web/control-ui.md +318 -0
- package/docs/web/dashboard.md +93 -0
- package/docs/web/index.md +126 -0
- package/docs/web/tui.md +176 -0
- package/docs/web/webchat.md +77 -0
- package/docs/whatsapp-openclaw-ai-zh.jpg +0 -0
- package/docs/whatsapp-openclaw.jpg +0 -0
- package/durar.mjs +180 -0
- package/package.json +1259 -0
- package/scripts/npm-runner.mjs +111 -0
- package/scripts/postinstall-bundled-plugins.mjs +188 -0
- package/skills/1password/SKILL.md +70 -0
- package/skills/1password/references/cli-examples.md +29 -0
- package/skills/1password/references/get-started.md +17 -0
- package/skills/apple-notes/SKILL.md +77 -0
- package/skills/apple-reminders/SKILL.md +118 -0
- package/skills/bear-notes/SKILL.md +107 -0
- package/skills/blogwatcher/SKILL.md +69 -0
- package/skills/blucli/SKILL.md +47 -0
- package/skills/bluebubbles/SKILL.md +131 -0
- package/skills/camsnap/SKILL.md +45 -0
- package/skills/canvas/SKILL.md +199 -0
- package/skills/clawhub/SKILL.md +77 -0
- package/skills/coding-agent/SKILL.md +316 -0
- package/skills/discord/SKILL.md +197 -0
- package/skills/eightctl/SKILL.md +50 -0
- package/skills/gemini/SKILL.md +43 -0
- package/skills/gh-issues/SKILL.md +885 -0
- package/skills/gifgrep/SKILL.md +79 -0
- package/skills/github/SKILL.md +163 -0
- package/skills/gog/SKILL.md +116 -0
- package/skills/goplaces/SKILL.md +52 -0
- package/skills/healthcheck/SKILL.md +245 -0
- package/skills/himalaya/SKILL.md +257 -0
- package/skills/himalaya/references/configuration.md +184 -0
- package/skills/himalaya/references/message-composition.md +199 -0
- package/skills/imsg/SKILL.md +122 -0
- package/skills/mcporter/SKILL.md +61 -0
- package/skills/model-usage/SKILL.md +69 -0
- package/skills/model-usage/references/codexbar-cli.md +33 -0
- package/skills/model-usage/scripts/model_usage.py +320 -0
- package/skills/model-usage/scripts/test_model_usage.py +40 -0
- package/skills/nano-pdf/SKILL.md +38 -0
- package/skills/node-connect/SKILL.md +142 -0
- package/skills/notion/SKILL.md +174 -0
- package/skills/obsidian/SKILL.md +81 -0
- package/skills/openai-whisper/SKILL.md +38 -0
- package/skills/openai-whisper-api/SKILL.md +62 -0
- package/skills/openai-whisper-api/scripts/transcribe.sh +88 -0
- package/skills/openhue/SKILL.md +112 -0
- package/skills/oracle/SKILL.md +125 -0
- package/skills/ordercli/SKILL.md +78 -0
- package/skills/peekaboo/SKILL.md +190 -0
- package/skills/sag/SKILL.md +87 -0
- package/skills/session-logs/SKILL.md +151 -0
- package/skills/sherpa-onnx-tts/SKILL.md +109 -0
- package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +178 -0
- package/skills/skill-creator/SKILL.md +372 -0
- package/skills/skill-creator/license.txt +202 -0
- package/skills/skill-creator/scripts/init_skill.py +378 -0
- package/skills/skill-creator/scripts/package_skill.py +139 -0
- package/skills/skill-creator/scripts/quick_validate.py +159 -0
- package/skills/skill-creator/scripts/test_package_skill.py +160 -0
- package/skills/skill-creator/scripts/test_quick_validate.py +72 -0
- package/skills/slack/SKILL.md +144 -0
- package/skills/songsee/SKILL.md +49 -0
- package/skills/sonoscli/SKILL.md +65 -0
- package/skills/spotify-player/SKILL.md +64 -0
- package/skills/summarize/SKILL.md +87 -0
- package/skills/taskflow/SKILL.md +149 -0
- package/skills/taskflow/examples/inbox-triage.lobster +33 -0
- package/skills/taskflow/examples/pr-intake.lobster +32 -0
- package/skills/taskflow-inbox-triage/SKILL.md +119 -0
- package/skills/things-mac/SKILL.md +86 -0
- package/skills/tmux/SKILL.md +170 -0
- package/skills/tmux/scripts/find-sessions.sh +112 -0
- package/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/skills/trello/SKILL.md +108 -0
- package/skills/video-frames/SKILL.md +46 -0
- package/skills/video-frames/scripts/frame.sh +81 -0
- package/skills/voice-call/SKILL.md +45 -0
- package/skills/wacli/SKILL.md +72 -0
- package/skills/weather/SKILL.md +129 -0
- package/skills/xurl/SKILL.md +461 -0
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Building Provider Plugins"
|
|
3
|
+
sidebarTitle: "Provider Plugins"
|
|
4
|
+
summary: "Step-by-step guide to building a model provider plugin for Durar"
|
|
5
|
+
read_when:
|
|
6
|
+
- You are building a new model provider plugin
|
|
7
|
+
- You want to add an OpenAI-compatible proxy or custom LLM to Durar
|
|
8
|
+
- You need to understand provider auth, catalogs, and runtime hooks
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Building Provider Plugins
|
|
12
|
+
|
|
13
|
+
This guide walks through building a provider plugin that adds a model provider
|
|
14
|
+
(LLM) to Durar. By the end you will have a provider with a model catalog,
|
|
15
|
+
API key auth, and dynamic model resolution.
|
|
16
|
+
|
|
17
|
+
<Info>
|
|
18
|
+
If you have not built any Durar plugin before, read
|
|
19
|
+
[Getting Started](/plugins/building-plugins) first for the basic package
|
|
20
|
+
structure and manifest setup.
|
|
21
|
+
</Info>
|
|
22
|
+
|
|
23
|
+
## Walkthrough
|
|
24
|
+
|
|
25
|
+
<Steps>
|
|
26
|
+
<a id="step-1-package-and-manifest"></a>
|
|
27
|
+
<Step title="Package and manifest">
|
|
28
|
+
<CodeGroup>
|
|
29
|
+
```json package.json
|
|
30
|
+
{
|
|
31
|
+
"name": "@myorg/Durar-acme-ai",
|
|
32
|
+
"version": "1.0.0",
|
|
33
|
+
"type": "module",
|
|
34
|
+
"Durar": {
|
|
35
|
+
"extensions": ["./index.ts"],
|
|
36
|
+
"providers": ["acme-ai"],
|
|
37
|
+
"compat": {
|
|
38
|
+
"pluginApi": ">=2026.3.24-beta.2",
|
|
39
|
+
"minGatewayVersion": "2026.3.24-beta.2"
|
|
40
|
+
},
|
|
41
|
+
"build": {
|
|
42
|
+
"DurarVersion": "2026.3.24-beta.2",
|
|
43
|
+
"pluginSdkVersion": "2026.3.24-beta.2"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```json Durar.plugin.json
|
|
50
|
+
{
|
|
51
|
+
"id": "acme-ai",
|
|
52
|
+
"name": "Acme AI",
|
|
53
|
+
"description": "Acme AI model provider",
|
|
54
|
+
"providers": ["acme-ai"],
|
|
55
|
+
"modelSupport": {
|
|
56
|
+
"modelPrefixes": ["acme-"]
|
|
57
|
+
},
|
|
58
|
+
"providerAuthEnvVars": {
|
|
59
|
+
"acme-ai": ["ACME_AI_API_KEY"]
|
|
60
|
+
},
|
|
61
|
+
"providerAuthChoices": [
|
|
62
|
+
{
|
|
63
|
+
"provider": "acme-ai",
|
|
64
|
+
"method": "api-key",
|
|
65
|
+
"choiceId": "acme-ai-api-key",
|
|
66
|
+
"choiceLabel": "Acme AI API key",
|
|
67
|
+
"groupId": "acme-ai",
|
|
68
|
+
"groupLabel": "Acme AI",
|
|
69
|
+
"cliFlag": "--acme-ai-api-key",
|
|
70
|
+
"cliOption": "--acme-ai-api-key <key>",
|
|
71
|
+
"cliDescription": "Acme AI API key"
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
"configSchema": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"additionalProperties": false
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
</CodeGroup>
|
|
81
|
+
|
|
82
|
+
The manifest declares `providerAuthEnvVars` so Durar can detect
|
|
83
|
+
credentials without loading your plugin runtime. `modelSupport` is optional
|
|
84
|
+
and lets Durar auto-load your provider plugin from shorthand model ids
|
|
85
|
+
like `acme-large` before runtime hooks exist. If you publish the
|
|
86
|
+
provider on Durar Gateway, those `Durar.compat` and `Durar.build` fields
|
|
87
|
+
are required in `package.json`.
|
|
88
|
+
|
|
89
|
+
</Step>
|
|
90
|
+
|
|
91
|
+
<Step title="Register the provider">
|
|
92
|
+
A minimal provider needs an `id`, `label`, `auth`, and `catalog`:
|
|
93
|
+
|
|
94
|
+
```typescript index.ts
|
|
95
|
+
import { definePluginEntry } from "Durar/plugin-sdk/plugin-entry";
|
|
96
|
+
import { createProviderApiKeyAuthMethod } from "Durar/plugin-sdk/provider-auth";
|
|
97
|
+
|
|
98
|
+
export default definePluginEntry({
|
|
99
|
+
id: "acme-ai",
|
|
100
|
+
name: "Acme AI",
|
|
101
|
+
description: "Acme AI model provider",
|
|
102
|
+
register(api) {
|
|
103
|
+
api.registerProvider({
|
|
104
|
+
id: "acme-ai",
|
|
105
|
+
label: "Acme AI",
|
|
106
|
+
docsPath: "/providers/acme-ai",
|
|
107
|
+
envVars: ["ACME_AI_API_KEY"],
|
|
108
|
+
|
|
109
|
+
auth: [
|
|
110
|
+
createProviderApiKeyAuthMethod({
|
|
111
|
+
providerId: "acme-ai",
|
|
112
|
+
methodId: "api-key",
|
|
113
|
+
label: "Acme AI API key",
|
|
114
|
+
hint: "API key from your Acme AI dashboard",
|
|
115
|
+
optionKey: "acmeAiApiKey",
|
|
116
|
+
flagName: "--acme-ai-api-key",
|
|
117
|
+
envVar: "ACME_AI_API_KEY",
|
|
118
|
+
promptMessage: "Enter your Acme AI API key",
|
|
119
|
+
defaultModel: "acme-ai/acme-large",
|
|
120
|
+
}),
|
|
121
|
+
],
|
|
122
|
+
|
|
123
|
+
catalog: {
|
|
124
|
+
order: "simple",
|
|
125
|
+
run: async (ctx) => {
|
|
126
|
+
const apiKey =
|
|
127
|
+
ctx.resolveProviderApiKey("acme-ai").apiKey;
|
|
128
|
+
if (!apiKey) return null;
|
|
129
|
+
return {
|
|
130
|
+
provider: {
|
|
131
|
+
baseUrl: "https://api.acme-ai.com/v1",
|
|
132
|
+
apiKey,
|
|
133
|
+
api: "openai-completions",
|
|
134
|
+
models: [
|
|
135
|
+
{
|
|
136
|
+
id: "acme-large",
|
|
137
|
+
name: "Acme Large",
|
|
138
|
+
reasoning: true,
|
|
139
|
+
input: ["text", "image"],
|
|
140
|
+
cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
141
|
+
contextWindow: 200000,
|
|
142
|
+
maxTokens: 32768,
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: "acme-small",
|
|
146
|
+
name: "Acme Small",
|
|
147
|
+
reasoning: false,
|
|
148
|
+
input: ["text"],
|
|
149
|
+
cost: { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
|
|
150
|
+
contextWindow: 128000,
|
|
151
|
+
maxTokens: 8192,
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
That is a working provider. Users can now
|
|
164
|
+
`Durar onboard --acme-ai-api-key <key>` and select
|
|
165
|
+
`acme-ai/acme-large` as their model.
|
|
166
|
+
|
|
167
|
+
For bundled providers that only register one text provider with API-key
|
|
168
|
+
auth plus a single catalog-backed runtime, prefer the narrower
|
|
169
|
+
`defineSingleProviderPluginEntry(...)` helper:
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
import { defineSingleProviderPluginEntry } from "Durar/plugin-sdk/provider-entry";
|
|
173
|
+
|
|
174
|
+
export default defineSingleProviderPluginEntry({
|
|
175
|
+
id: "acme-ai",
|
|
176
|
+
name: "Acme AI",
|
|
177
|
+
description: "Acme AI model provider",
|
|
178
|
+
provider: {
|
|
179
|
+
label: "Acme AI",
|
|
180
|
+
docsPath: "/providers/acme-ai",
|
|
181
|
+
auth: [
|
|
182
|
+
{
|
|
183
|
+
methodId: "api-key",
|
|
184
|
+
label: "Acme AI API key",
|
|
185
|
+
hint: "API key from your Acme AI dashboard",
|
|
186
|
+
optionKey: "acmeAiApiKey",
|
|
187
|
+
flagName: "--acme-ai-api-key",
|
|
188
|
+
envVar: "ACME_AI_API_KEY",
|
|
189
|
+
promptMessage: "Enter your Acme AI API key",
|
|
190
|
+
defaultModel: "acme-ai/acme-large",
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
catalog: {
|
|
194
|
+
buildProvider: () => ({
|
|
195
|
+
api: "openai-completions",
|
|
196
|
+
baseUrl: "https://api.acme-ai.com/v1",
|
|
197
|
+
models: [{ id: "acme-large", name: "Acme Large" }],
|
|
198
|
+
}),
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
If your auth flow also needs to patch `models.providers.*`, aliases, and
|
|
205
|
+
the agent default model during onboarding, use the preset helpers from
|
|
206
|
+
`Durar/plugin-sdk/provider-onboard`. The narrowest helpers are
|
|
207
|
+
`createDefaultModelPresetAppliers(...)`,
|
|
208
|
+
`createDefaultModelsPresetAppliers(...)`, and
|
|
209
|
+
`createModelCatalogPresetAppliers(...)`.
|
|
210
|
+
|
|
211
|
+
When a provider's native endpoint supports streamed usage blocks on the
|
|
212
|
+
normal `openai-completions` transport, prefer the shared catalog helpers in
|
|
213
|
+
`Durar/plugin-sdk/provider-catalog-shared` instead of hardcoding
|
|
214
|
+
provider-id checks. `supportsNativeStreamingUsageCompat(...)` and
|
|
215
|
+
`applyProviderNativeStreamingUsageCompat(...)` detect support from the
|
|
216
|
+
endpoint capability map, so native Moonshot/DashScope-style endpoints still
|
|
217
|
+
opt in even when a plugin is using a custom provider id.
|
|
218
|
+
|
|
219
|
+
</Step>
|
|
220
|
+
|
|
221
|
+
<Step title="Add dynamic model resolution">
|
|
222
|
+
If your provider accepts arbitrary model IDs (like a proxy or router),
|
|
223
|
+
add `resolveDynamicModel`:
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
api.registerProvider({
|
|
227
|
+
// ... id, label, auth, catalog from above
|
|
228
|
+
|
|
229
|
+
resolveDynamicModel: (ctx) => ({
|
|
230
|
+
id: ctx.modelId,
|
|
231
|
+
name: ctx.modelId,
|
|
232
|
+
provider: "acme-ai",
|
|
233
|
+
api: "openai-completions",
|
|
234
|
+
baseUrl: "https://api.acme-ai.com/v1",
|
|
235
|
+
reasoning: false,
|
|
236
|
+
input: ["text"],
|
|
237
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
238
|
+
contextWindow: 128000,
|
|
239
|
+
maxTokens: 8192,
|
|
240
|
+
}),
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
If resolving requires a network call, use `prepareDynamicModel` for async
|
|
245
|
+
warm-up — `resolveDynamicModel` runs again after it completes.
|
|
246
|
+
|
|
247
|
+
</Step>
|
|
248
|
+
|
|
249
|
+
<Step title="Add runtime hooks (as needed)">
|
|
250
|
+
Most providers only need `catalog` + `resolveDynamicModel`. Add hooks
|
|
251
|
+
incrementally as your provider requires them.
|
|
252
|
+
|
|
253
|
+
Shared helper builders now cover the most common replay/tool-compat
|
|
254
|
+
families, so plugins usually do not need to hand-wire each hook one by one:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
import { buildProviderReplayFamilyHooks } from "Durar/plugin-sdk/provider-model-shared";
|
|
258
|
+
import { buildProviderStreamFamilyHooks } from "Durar/plugin-sdk/provider-stream";
|
|
259
|
+
import { buildProviderToolCompatFamilyHooks } from "Durar/plugin-sdk/provider-tools";
|
|
260
|
+
|
|
261
|
+
const GOOGLE_FAMILY_HOOKS = {
|
|
262
|
+
...buildProviderReplayFamilyHooks({ family: "google-gemini" }),
|
|
263
|
+
...buildProviderStreamFamilyHooks("google-thinking"),
|
|
264
|
+
...buildProviderToolCompatFamilyHooks("gemini"),
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
api.registerProvider({
|
|
268
|
+
id: "acme-gemini-compatible",
|
|
269
|
+
// ...
|
|
270
|
+
...GOOGLE_FAMILY_HOOKS,
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Available replay families today:
|
|
275
|
+
|
|
276
|
+
| Family | What it wires in |
|
|
277
|
+
| --- | --- |
|
|
278
|
+
| `openai-compatible` | Shared OpenAI-style replay policy for OpenAI-compatible transports, including tool-call-id sanitation, assistant-first ordering fixes, and generic Gemini-turn validation where the transport needs it |
|
|
279
|
+
| `anthropic-by-model` | Claude-aware replay policy chosen by `modelId`, so Anthropic-message transports only get Claude-specific thinking-block cleanup when the resolved model is actually a Claude id |
|
|
280
|
+
| `google-gemini` | Native Gemini replay policy plus bootstrap replay sanitation and tagged reasoning-output mode |
|
|
281
|
+
| `passthrough-gemini` | Gemini thought-signature sanitation for Gemini models running through OpenAI-compatible proxy transports; does not enable native Gemini replay validation or bootstrap rewrites |
|
|
282
|
+
| `hybrid-anthropic-openai` | Hybrid policy for providers that mix Anthropic-message and OpenAI-compatible model surfaces in one plugin; optional Claude-only thinking-block dropping stays scoped to the Anthropic side |
|
|
283
|
+
|
|
284
|
+
Real bundled examples:
|
|
285
|
+
|
|
286
|
+
- `google` and `google-gemini-cli`: `google-gemini`
|
|
287
|
+
- `openrouter`, `kilocode`, `opencode`, and `opencode-go`: `passthrough-gemini`
|
|
288
|
+
- `amazon-bedrock` and `anthropic-vertex`: `anthropic-by-model`
|
|
289
|
+
- `minimax`: `hybrid-anthropic-openai`
|
|
290
|
+
- `moonshot`, `ollama`, `xai`, and `zai`: `openai-compatible`
|
|
291
|
+
|
|
292
|
+
Available stream families today:
|
|
293
|
+
|
|
294
|
+
| Family | What it wires in |
|
|
295
|
+
| --- | --- |
|
|
296
|
+
| `google-thinking` | Gemini thinking payload normalization on the shared stream path |
|
|
297
|
+
| `kilocode-thinking` | Kilo reasoning wrapper on the shared proxy stream path, with `kilo/auto` and unsupported proxy reasoning ids skipping injected thinking |
|
|
298
|
+
| `moonshot-thinking` | Moonshot binary native-thinking payload mapping from config + `/think` level |
|
|
299
|
+
| `minimax-fast-mode` | MiniMax fast-mode model rewrite on the shared stream path |
|
|
300
|
+
| `openai-responses-defaults` | Shared native OpenAI/Codex Responses wrappers: attribution headers, `/fast`/`serviceTier`, text verbosity, native Codex web search, reasoning-compat payload shaping, and Responses context management |
|
|
301
|
+
| `openrouter-thinking` | OpenRouter reasoning wrapper for proxy routes, with unsupported-model/`auto` skips handled centrally |
|
|
302
|
+
| `tool-stream-default-on` | Default-on `tool_stream` wrapper for providers like Z.AI that want tool streaming unless explicitly disabled |
|
|
303
|
+
|
|
304
|
+
Real bundled examples:
|
|
305
|
+
|
|
306
|
+
- `google` and `google-gemini-cli`: `google-thinking`
|
|
307
|
+
- `kilocode`: `kilocode-thinking`
|
|
308
|
+
- `moonshot`: `moonshot-thinking`
|
|
309
|
+
- `minimax` and `minimax-portal`: `minimax-fast-mode`
|
|
310
|
+
- `openai` and `openai-codex`: `openai-responses-defaults`
|
|
311
|
+
- `openrouter`: `openrouter-thinking`
|
|
312
|
+
- `zai`: `tool-stream-default-on`
|
|
313
|
+
|
|
314
|
+
`Durar/plugin-sdk/provider-model-shared` also exports the replay-family
|
|
315
|
+
enum plus the shared helpers those families are built from. Common public
|
|
316
|
+
exports include:
|
|
317
|
+
|
|
318
|
+
- `ProviderReplayFamily`
|
|
319
|
+
- `buildProviderReplayFamilyHooks(...)`
|
|
320
|
+
- shared replay builders such as `buildOpenAICompatibleReplayPolicy(...)`,
|
|
321
|
+
`buildAnthropicReplayPolicyForModel(...)`,
|
|
322
|
+
`buildGoogleGeminiReplayPolicy(...)`, and
|
|
323
|
+
`buildHybridAnthropicOrOpenAIReplayPolicy(...)`
|
|
324
|
+
- Gemini replay helpers such as `sanitizeGoogleGeminiReplayHistory(...)`
|
|
325
|
+
and `resolveTaggedReasoningOutputMode()`
|
|
326
|
+
- endpoint/model helpers such as `resolveProviderEndpoint(...)`,
|
|
327
|
+
`normalizeProviderId(...)`, `normalizeGooglePreviewModelId(...)`, and
|
|
328
|
+
`normalizeNativeXaiModelId(...)`
|
|
329
|
+
|
|
330
|
+
`Durar/plugin-sdk/provider-stream` exposes both the family builder and
|
|
331
|
+
the public wrapper helpers those families reuse. Common public exports
|
|
332
|
+
include:
|
|
333
|
+
|
|
334
|
+
- `ProviderStreamFamily`
|
|
335
|
+
- `buildProviderStreamFamilyHooks(...)`
|
|
336
|
+
- `composeProviderStreamWrappers(...)`
|
|
337
|
+
- shared OpenAI/Codex wrappers such as
|
|
338
|
+
`createOpenAIAttributionHeadersWrapper(...)`,
|
|
339
|
+
`createOpenAIFastModeWrapper(...)`,
|
|
340
|
+
`createOpenAIServiceTierWrapper(...)`,
|
|
341
|
+
`createOpenAIResponsesContextManagementWrapper(...)`, and
|
|
342
|
+
`createCodexNativeWebSearchWrapper(...)`
|
|
343
|
+
- shared proxy/provider wrappers such as `createOpenRouterWrapper(...)`,
|
|
344
|
+
`createToolStreamWrapper(...)`, and `createMinimaxFastModeWrapper(...)`
|
|
345
|
+
|
|
346
|
+
Some stream helpers stay provider-local on purpose. Current bundled
|
|
347
|
+
example: `@Durar/anthropic-provider` exports
|
|
348
|
+
`wrapAnthropicProviderStream`, `resolveAnthropicBetas`,
|
|
349
|
+
`resolveAnthropicFastMode`, `resolveAnthropicServiceTier`, and the
|
|
350
|
+
lower-level Anthropic wrapper builders from its public `api.ts` /
|
|
351
|
+
`contract-api.ts` seam. Those helpers remain Anthropic-specific because
|
|
352
|
+
they also encode Claude OAuth beta handling and `context1m` gating.
|
|
353
|
+
|
|
354
|
+
Other bundled providers also keep transport-specific wrappers local when
|
|
355
|
+
the behavior is not shared cleanly across families. Current example: the
|
|
356
|
+
bundled xAI plugin keeps native xAI Responses shaping in its own
|
|
357
|
+
`wrapStreamFn`, including `/fast` alias rewrites, default `tool_stream`,
|
|
358
|
+
unsupported strict-tool cleanup, and xAI-specific reasoning-payload
|
|
359
|
+
removal.
|
|
360
|
+
|
|
361
|
+
`Durar/plugin-sdk/provider-tools` currently exposes one shared
|
|
362
|
+
tool-schema family plus shared schema/compat helpers:
|
|
363
|
+
|
|
364
|
+
- `ProviderToolCompatFamily` documents the shared family inventory today.
|
|
365
|
+
- `buildProviderToolCompatFamilyHooks("gemini")` wires Gemini schema
|
|
366
|
+
cleanup + diagnostics for providers that need Gemini-safe tool schemas.
|
|
367
|
+
- `normalizeGeminiToolSchemas(...)` and `inspectGeminiToolSchemas(...)`
|
|
368
|
+
are the underlying public Gemini schema helpers.
|
|
369
|
+
- `resolveXaiModelCompatPatch()` returns the bundled xAI compat patch:
|
|
370
|
+
`toolSchemaProfile: "xai"`, unsupported schema keywords, native
|
|
371
|
+
`web_search` support, and HTML-entity tool-call argument decoding.
|
|
372
|
+
- `applyXaiModelCompat(model)` applies that same xAI compat patch to a
|
|
373
|
+
resolved model before it reaches the runner.
|
|
374
|
+
|
|
375
|
+
Real bundled example: the xAI plugin uses `normalizeResolvedModel` plus
|
|
376
|
+
`contributeResolvedModelCompat` to keep that compat metadata owned by the
|
|
377
|
+
provider instead of hardcoding xAI rules in core.
|
|
378
|
+
|
|
379
|
+
The same package-root pattern also backs other bundled providers:
|
|
380
|
+
|
|
381
|
+
- `@Durar/openai-provider`: `api.ts` exports provider builders,
|
|
382
|
+
default-model helpers, and realtime provider builders
|
|
383
|
+
- `@Durar/openrouter-provider`: `api.ts` exports the provider builder
|
|
384
|
+
plus onboarding/config helpers
|
|
385
|
+
|
|
386
|
+
<Tabs>
|
|
387
|
+
<Tab title="Token exchange">
|
|
388
|
+
For providers that need a token exchange before each inference call:
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
prepareRuntimeAuth: async (ctx) => {
|
|
392
|
+
const exchanged = await exchangeToken(ctx.apiKey);
|
|
393
|
+
return {
|
|
394
|
+
apiKey: exchanged.token,
|
|
395
|
+
baseUrl: exchanged.baseUrl,
|
|
396
|
+
expiresAt: exchanged.expiresAt,
|
|
397
|
+
};
|
|
398
|
+
},
|
|
399
|
+
```
|
|
400
|
+
</Tab>
|
|
401
|
+
<Tab title="Custom headers">
|
|
402
|
+
For providers that need custom request headers or body modifications:
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
// wrapStreamFn returns a StreamFn derived from ctx.streamFn
|
|
406
|
+
wrapStreamFn: (ctx) => {
|
|
407
|
+
if (!ctx.streamFn) return undefined;
|
|
408
|
+
const inner = ctx.streamFn;
|
|
409
|
+
return async (params) => {
|
|
410
|
+
params.headers = {
|
|
411
|
+
...params.headers,
|
|
412
|
+
"X-Acme-Version": "2",
|
|
413
|
+
};
|
|
414
|
+
return inner(params);
|
|
415
|
+
};
|
|
416
|
+
},
|
|
417
|
+
```
|
|
418
|
+
</Tab>
|
|
419
|
+
<Tab title="Native transport identity">
|
|
420
|
+
For providers that need native request/session headers or metadata on
|
|
421
|
+
generic HTTP or WebSocket transports:
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
resolveTransportTurnState: (ctx) => ({
|
|
425
|
+
headers: {
|
|
426
|
+
"x-request-id": ctx.turnId,
|
|
427
|
+
},
|
|
428
|
+
metadata: {
|
|
429
|
+
session_id: ctx.sessionId ?? "",
|
|
430
|
+
turn_id: ctx.turnId,
|
|
431
|
+
},
|
|
432
|
+
}),
|
|
433
|
+
resolveWebSocketSessionPolicy: (ctx) => ({
|
|
434
|
+
headers: {
|
|
435
|
+
"x-session-id": ctx.sessionId ?? "",
|
|
436
|
+
},
|
|
437
|
+
degradeCooldownMs: 60_000,
|
|
438
|
+
}),
|
|
439
|
+
```
|
|
440
|
+
</Tab>
|
|
441
|
+
<Tab title="Usage and billing">
|
|
442
|
+
For providers that expose usage/billing data:
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
resolveUsageAuth: async (ctx) => {
|
|
446
|
+
const auth = await ctx.resolveOAuthToken();
|
|
447
|
+
return auth ? { token: auth.token } : null;
|
|
448
|
+
},
|
|
449
|
+
fetchUsageSnapshot: async (ctx) => {
|
|
450
|
+
return await fetchAcmeUsage(ctx.token, ctx.timeoutMs);
|
|
451
|
+
},
|
|
452
|
+
```
|
|
453
|
+
</Tab>
|
|
454
|
+
</Tabs>
|
|
455
|
+
|
|
456
|
+
<Accordion title="All available provider hooks">
|
|
457
|
+
Durar calls hooks in this order. Most providers only use 2-3:
|
|
458
|
+
|
|
459
|
+
| # | Hook | When to use |
|
|
460
|
+
| --- | --- | --- |
|
|
461
|
+
| 1 | `catalog` | Model catalog or base URL defaults |
|
|
462
|
+
| 2 | `applyConfigDefaults` | Provider-owned global defaults during config materialization |
|
|
463
|
+
| 3 | `normalizeModelId` | Legacy/preview model-id alias cleanup before lookup |
|
|
464
|
+
| 4 | `normalizeTransport` | Provider-family `api` / `baseUrl` cleanup before generic model assembly |
|
|
465
|
+
| 5 | `normalizeConfig` | Normalize `models.providers.<id>` config |
|
|
466
|
+
| 6 | `applyNativeStreamingUsageCompat` | Native streaming-usage compat rewrites for config providers |
|
|
467
|
+
| 7 | `resolveConfigApiKey` | Provider-owned env-marker auth resolution |
|
|
468
|
+
| 8 | `resolveSyntheticAuth` | Local/self-hosted or config-backed synthetic auth |
|
|
469
|
+
| 9 | `shouldDeferSyntheticProfileAuth` | Lower synthetic stored-profile placeholders behind env/config auth |
|
|
470
|
+
| 10 | `resolveDynamicModel` | Accept arbitrary upstream model IDs |
|
|
471
|
+
| 11 | `prepareDynamicModel` | Async metadata fetch before resolving |
|
|
472
|
+
| 12 | `normalizeResolvedModel` | Transport rewrites before the runner |
|
|
473
|
+
|
|
474
|
+
Runtime fallback notes:
|
|
475
|
+
|
|
476
|
+
- `normalizeConfig` checks the matched provider first, then other
|
|
477
|
+
hook-capable provider plugins until one actually changes the config.
|
|
478
|
+
If no provider hook rewrites a supported Google-family config entry, the
|
|
479
|
+
bundled Google config normalizer still applies.
|
|
480
|
+
- `resolveConfigApiKey` uses the provider hook when exposed. The bundled
|
|
481
|
+
`amazon-bedrock` path also has a built-in AWS env-marker resolver here,
|
|
482
|
+
even though Bedrock runtime auth itself still uses the AWS SDK default
|
|
483
|
+
chain.
|
|
484
|
+
| 13 | `contributeResolvedModelCompat` | Compat flags for vendor models behind another compatible transport |
|
|
485
|
+
| 14 | `capabilities` | Legacy static capability bag; compatibility only |
|
|
486
|
+
| 15 | `normalizeToolSchemas` | Provider-owned tool-schema cleanup before registration |
|
|
487
|
+
| 16 | `inspectToolSchemas` | Provider-owned tool-schema diagnostics |
|
|
488
|
+
| 17 | `resolveReasoningOutputMode` | Tagged vs native reasoning-output contract |
|
|
489
|
+
| 18 | `prepareExtraParams` | Default request params |
|
|
490
|
+
| 19 | `createStreamFn` | Fully custom StreamFn transport |
|
|
491
|
+
| 20 | `wrapStreamFn` | Custom headers/body wrappers on the normal stream path |
|
|
492
|
+
| 21 | `resolveTransportTurnState` | Native per-turn headers/metadata |
|
|
493
|
+
| 22 | `resolveWebSocketSessionPolicy` | Native WS session headers/cool-down |
|
|
494
|
+
| 23 | `formatApiKey` | Custom runtime token shape |
|
|
495
|
+
| 24 | `refreshOAuth` | Custom OAuth refresh |
|
|
496
|
+
| 25 | `buildAuthDoctorHint` | Auth repair guidance |
|
|
497
|
+
| 26 | `matchesContextOverflowError` | Provider-owned overflow detection |
|
|
498
|
+
| 27 | `classifyFailoverReason` | Provider-owned rate-limit/overload classification |
|
|
499
|
+
| 28 | `isCacheTtlEligible` | Prompt cache TTL gating |
|
|
500
|
+
| 29 | `buildMissingAuthMessage` | Custom missing-auth hint |
|
|
501
|
+
| 30 | `suppressBuiltInModel` | Hide stale upstream rows |
|
|
502
|
+
| 31 | `augmentModelCatalog` | Synthetic forward-compat rows |
|
|
503
|
+
| 32 | `isBinaryThinking` | Binary thinking on/off |
|
|
504
|
+
| 33 | `supportsXHighThinking` | `xhigh` reasoning support |
|
|
505
|
+
| 34 | `resolveDefaultThinkingLevel` | Default `/think` policy |
|
|
506
|
+
| 35 | `isModernModelRef` | Live/smoke model matching |
|
|
507
|
+
| 36 | `prepareRuntimeAuth` | Token exchange before inference |
|
|
508
|
+
| 37 | `resolveUsageAuth` | Custom usage credential parsing |
|
|
509
|
+
| 38 | `fetchUsageSnapshot` | Custom usage endpoint |
|
|
510
|
+
| 39 | `createEmbeddingProvider` | Provider-owned embedding adapter for memory/search |
|
|
511
|
+
| 40 | `buildReplayPolicy` | Custom transcript replay/compaction policy |
|
|
512
|
+
| 41 | `sanitizeReplayHistory` | Provider-specific replay rewrites after generic cleanup |
|
|
513
|
+
| 42 | `validateReplayTurns` | Strict replay-turn validation before the embedded runner |
|
|
514
|
+
| 43 | `onModelSelected` | Post-selection callback (e.g. telemetry) |
|
|
515
|
+
|
|
516
|
+
Prompt tuning note:
|
|
517
|
+
|
|
518
|
+
- `resolveSystemPromptContribution` lets a provider inject cache-aware
|
|
519
|
+
system-prompt guidance for a model family. Prefer it over
|
|
520
|
+
`before_prompt_build` when the behavior belongs to one provider/model
|
|
521
|
+
family and should preserve the stable/dynamic cache split.
|
|
522
|
+
|
|
523
|
+
For detailed descriptions and real-world examples, see
|
|
524
|
+
[Internals: Provider Runtime Hooks](/plugins/architecture#provider-runtime-hooks).
|
|
525
|
+
</Accordion>
|
|
526
|
+
|
|
527
|
+
</Step>
|
|
528
|
+
|
|
529
|
+
<Step title="Add extra capabilities (optional)">
|
|
530
|
+
<a id="step-5-add-extra-capabilities"></a>
|
|
531
|
+
A provider plugin can register speech, realtime transcription, realtime
|
|
532
|
+
voice, media understanding, image generation, video generation, web fetch,
|
|
533
|
+
and web search alongside text inference:
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
register(api) {
|
|
537
|
+
api.registerProvider({ id: "acme-ai", /* ... */ });
|
|
538
|
+
|
|
539
|
+
api.registerSpeechProvider({
|
|
540
|
+
id: "acme-ai",
|
|
541
|
+
label: "Acme Speech",
|
|
542
|
+
isConfigured: ({ config }) => Boolean(config.messages?.tts),
|
|
543
|
+
synthesize: async (req) => ({
|
|
544
|
+
audioBuffer: Buffer.from(/* PCM data */),
|
|
545
|
+
outputFormat: "mp3",
|
|
546
|
+
fileExtension: ".mp3",
|
|
547
|
+
voiceCompatible: false,
|
|
548
|
+
}),
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
api.registerRealtimeTranscriptionProvider({
|
|
552
|
+
id: "acme-ai",
|
|
553
|
+
label: "Acme Realtime Transcription",
|
|
554
|
+
isConfigured: () => true,
|
|
555
|
+
createSession: (req) => ({
|
|
556
|
+
connect: async () => {},
|
|
557
|
+
sendAudio: () => {},
|
|
558
|
+
close: () => {},
|
|
559
|
+
isConnected: () => true,
|
|
560
|
+
}),
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
api.registerRealtimeVoiceProvider({
|
|
564
|
+
id: "acme-ai",
|
|
565
|
+
label: "Acme Realtime Voice",
|
|
566
|
+
isConfigured: ({ providerConfig }) => Boolean(providerConfig.apiKey),
|
|
567
|
+
createBridge: (req) => ({
|
|
568
|
+
connect: async () => {},
|
|
569
|
+
sendAudio: () => {},
|
|
570
|
+
setMediaTimestamp: () => {},
|
|
571
|
+
submitToolResult: () => {},
|
|
572
|
+
acknowledgeMark: () => {},
|
|
573
|
+
close: () => {},
|
|
574
|
+
isConnected: () => true,
|
|
575
|
+
}),
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
api.registerMediaUnderstandingProvider({
|
|
579
|
+
id: "acme-ai",
|
|
580
|
+
capabilities: ["image", "audio"],
|
|
581
|
+
describeImage: async (req) => ({ text: "A photo of..." }),
|
|
582
|
+
transcribeAudio: async (req) => ({ text: "Transcript..." }),
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
api.registerImageGenerationProvider({
|
|
586
|
+
id: "acme-ai",
|
|
587
|
+
label: "Acme Images",
|
|
588
|
+
generate: async (req) => ({ /* image result */ }),
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
api.registerVideoGenerationProvider({
|
|
592
|
+
id: "acme-ai",
|
|
593
|
+
label: "Acme Video",
|
|
594
|
+
capabilities: {
|
|
595
|
+
maxVideos: 1,
|
|
596
|
+
maxDurationSeconds: 10,
|
|
597
|
+
supportsResolution: true,
|
|
598
|
+
},
|
|
599
|
+
generateVideo: async (req) => ({ videos: [] }),
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
api.registerWebFetchProvider({
|
|
603
|
+
id: "acme-ai-fetch",
|
|
604
|
+
label: "Acme Fetch",
|
|
605
|
+
hint: "Fetch pages through Acme's rendering backend.",
|
|
606
|
+
envVars: ["ACME_FETCH_API_KEY"],
|
|
607
|
+
placeholder: "acme-...",
|
|
608
|
+
signupUrl: "https://acme.example.com/fetch",
|
|
609
|
+
credentialPath: "plugins.entries.acme.config.webFetch.apiKey",
|
|
610
|
+
getCredentialValue: (fetchConfig) => fetchConfig?.acme?.apiKey,
|
|
611
|
+
setCredentialValue: (fetchConfigTarget, value) => {
|
|
612
|
+
const acme = (fetchConfigTarget.acme ??= {});
|
|
613
|
+
acme.apiKey = value;
|
|
614
|
+
},
|
|
615
|
+
createTool: () => ({
|
|
616
|
+
description: "Fetch a page through Acme Fetch.",
|
|
617
|
+
parameters: {},
|
|
618
|
+
execute: async (args) => ({ content: [] }),
|
|
619
|
+
}),
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
api.registerWebSearchProvider({
|
|
623
|
+
id: "acme-ai-search",
|
|
624
|
+
label: "Acme Search",
|
|
625
|
+
search: async (req) => ({ content: [] }),
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
Durar classifies this as a **hybrid-capability** plugin. This is the
|
|
631
|
+
recommended pattern for company plugins (one plugin per vendor). See
|
|
632
|
+
[Internals: Capability Ownership](/plugins/architecture#capability-ownership-model).
|
|
633
|
+
|
|
634
|
+
</Step>
|
|
635
|
+
|
|
636
|
+
<Step title="Test">
|
|
637
|
+
<a id="step-6-test"></a>
|
|
638
|
+
```typescript src/provider.test.ts
|
|
639
|
+
import { describe, it, expect } from "vitest";
|
|
640
|
+
// Export your provider config object from index.ts or a dedicated file
|
|
641
|
+
import { acmeProvider } from "./provider.js";
|
|
642
|
+
|
|
643
|
+
describe("acme-ai provider", () => {
|
|
644
|
+
it("resolves dynamic models", () => {
|
|
645
|
+
const model = acmeProvider.resolveDynamicModel!({
|
|
646
|
+
modelId: "acme-beta-v3",
|
|
647
|
+
} as any);
|
|
648
|
+
expect(model.id).toBe("acme-beta-v3");
|
|
649
|
+
expect(model.provider).toBe("acme-ai");
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it("returns catalog when key is available", async () => {
|
|
653
|
+
const result = await acmeProvider.catalog!.run({
|
|
654
|
+
resolveProviderApiKey: () => ({ apiKey: "test-key" }),
|
|
655
|
+
} as any);
|
|
656
|
+
expect(result?.provider?.models).toHaveLength(2);
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
it("returns null catalog when no key", async () => {
|
|
660
|
+
const result = await acmeProvider.catalog!.run({
|
|
661
|
+
resolveProviderApiKey: () => ({ apiKey: undefined }),
|
|
662
|
+
} as any);
|
|
663
|
+
expect(result).toBeNull();
|
|
664
|
+
});
|
|
665
|
+
});
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
</Step>
|
|
669
|
+
</Steps>
|
|
670
|
+
|
|
671
|
+
## Publish to Durar Gateway
|
|
672
|
+
|
|
673
|
+
Provider plugins publish the same way as any other external code plugin:
|
|
674
|
+
|
|
675
|
+
```bash
|
|
676
|
+
Durar Gateway package publish your-org/your-plugin --dry-run
|
|
677
|
+
Durar Gateway package publish your-org/your-plugin
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
Do not use the legacy skill-only publish alias here; plugin packages should use
|
|
681
|
+
`Durar Gateway package publish`.
|
|
682
|
+
|
|
683
|
+
## File structure
|
|
684
|
+
|
|
685
|
+
```
|
|
686
|
+
<bundled-plugin-root>/acme-ai/
|
|
687
|
+
├── package.json # Durar.providers metadata
|
|
688
|
+
├── Durar.plugin.json # Manifest with providerAuthEnvVars
|
|
689
|
+
├── index.ts # definePluginEntry + registerProvider
|
|
690
|
+
└── src/
|
|
691
|
+
├── provider.test.ts # Tests
|
|
692
|
+
└── usage.ts # Usage endpoint (optional)
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
## Catalog order reference
|
|
696
|
+
|
|
697
|
+
`catalog.order` controls when your catalog merges relative to built-in
|
|
698
|
+
providers:
|
|
699
|
+
|
|
700
|
+
| Order | When | Use case |
|
|
701
|
+
| --------- | ------------- | ----------------------------------------------- |
|
|
702
|
+
| `simple` | First pass | Plain API-key providers |
|
|
703
|
+
| `profile` | After simple | Providers gated on auth profiles |
|
|
704
|
+
| `paired` | After profile | Synthesize multiple related entries |
|
|
705
|
+
| `late` | Last pass | Override existing providers (wins on collision) |
|
|
706
|
+
|
|
707
|
+
## Next steps
|
|
708
|
+
|
|
709
|
+
- [Channel Plugins](/plugins/sdk-channel-plugins) — if your plugin also provides a channel
|
|
710
|
+
- [SDK Runtime](/plugins/sdk-runtime) — `api.runtime` helpers (TTS, search, subagent)
|
|
711
|
+
- [SDK Overview](/plugins/sdk-overview) — full subpath import reference
|
|
712
|
+
- [Plugin Internals](/plugins/architecture#provider-runtime-hooks) — hook details and bundled examples
|