@raybase/core 0.0.2 → 0.3.3
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/LICENSE +21 -0
- package/dist/.admin-build/AdminApp.css +2 -0
- package/dist/.admin-build/AdminApp.js +77 -0
- package/dist/.admin-docs-build/AdminDocsApp.css +2 -0
- package/dist/.admin-docs-build/AdminDocsApp.js +45 -0
- package/dist/actions.d.ts +347 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +661 -0
- package/dist/actions.js.map +1 -0
- package/dist/admin-assets/admin.7738f50720a8ed7b.css +2 -0
- package/dist/admin-assets/admin.8678cb61db34dc81.js +77 -0
- package/dist/admin-boot.d.ts +94 -0
- package/dist/admin-boot.d.ts.map +1 -0
- package/dist/admin-boot.js +139 -0
- package/dist/admin-boot.js.map +1 -0
- package/dist/admin-docs-content.d.ts +95 -0
- package/dist/admin-docs-content.d.ts.map +1 -0
- package/dist/admin-docs-content.js +463 -0
- package/dist/admin-docs-content.js.map +1 -0
- package/dist/admin-docs-ui.d.ts +11 -0
- package/dist/admin-docs-ui.d.ts.map +1 -0
- package/dist/admin-docs-ui.js +23 -0
- package/dist/admin-docs-ui.js.map +1 -0
- package/dist/admin-kit.d.ts +29 -0
- package/dist/admin-kit.js +1557 -0
- package/dist/admin-role.d.ts +8 -0
- package/dist/admin-role.d.ts.map +1 -0
- package/dist/admin-role.js +22 -0
- package/dist/admin-role.js.map +1 -0
- package/dist/admin-ui.d.ts +3 -0
- package/dist/admin-ui.d.ts.map +1 -0
- package/dist/admin-ui.js +16 -0
- package/dist/admin-ui.js.map +1 -0
- package/dist/agent-ai-chat.d.ts +127 -0
- package/dist/agent-ai-chat.d.ts.map +1 -0
- package/dist/agent-ai-chat.js +5975 -0
- package/dist/agent-ai-chat.js.map +1 -0
- package/dist/agent-chapter-archive.d.ts +214 -0
- package/dist/agent-chapter-archive.d.ts.map +1 -0
- package/dist/agent-chapter-archive.js +696 -0
- package/dist/agent-chapter-archive.js.map +1 -0
- package/dist/agent-chapter-search.d.ts +116 -0
- package/dist/agent-chapter-search.d.ts.map +1 -0
- package/dist/agent-chapter-search.js +605 -0
- package/dist/agent-chapter-search.js.map +1 -0
- package/dist/agent-chapter-tools.d.ts +45 -0
- package/dist/agent-chapter-tools.d.ts.map +1 -0
- package/dist/agent-chapter-tools.js +344 -0
- package/dist/agent-chapter-tools.js.map +1 -0
- package/dist/agent-chapters.d.ts +228 -0
- package/dist/agent-chapters.d.ts.map +1 -0
- package/dist/agent-chapters.js +737 -0
- package/dist/agent-chapters.js.map +1 -0
- package/dist/agent-data-parts.d.ts +167 -0
- package/dist/agent-data-parts.d.ts.map +1 -0
- package/dist/agent-data-parts.js +68 -0
- package/dist/agent-data-parts.js.map +1 -0
- package/dist/agent-errors.d.ts +41 -0
- package/dist/agent-errors.d.ts.map +1 -0
- package/dist/agent-errors.js +191 -0
- package/dist/agent-errors.js.map +1 -0
- package/dist/agent-follow-up-store.d.ts +273 -0
- package/dist/agent-follow-up-store.d.ts.map +1 -0
- package/dist/agent-follow-up-store.js +703 -0
- package/dist/agent-follow-up-store.js.map +1 -0
- package/dist/agent-follow-ups.d.ts +314 -0
- package/dist/agent-follow-ups.d.ts.map +1 -0
- package/dist/agent-follow-ups.js +840 -0
- package/dist/agent-follow-ups.js.map +1 -0
- package/dist/agent-memory.d.ts +323 -0
- package/dist/agent-memory.d.ts.map +1 -0
- package/dist/agent-memory.js +1270 -0
- package/dist/agent-memory.js.map +1 -0
- package/dist/agent-message-parts.d.ts +58 -0
- package/dist/agent-message-parts.d.ts.map +1 -0
- package/dist/agent-message-parts.js +163 -0
- package/dist/agent-message-parts.js.map +1 -0
- package/dist/agent-remote-mcp-credentials.d.ts +154 -0
- package/dist/agent-remote-mcp-credentials.d.ts.map +1 -0
- package/dist/agent-remote-mcp-credentials.js +287 -0
- package/dist/agent-remote-mcp-credentials.js.map +1 -0
- package/dist/agent-remote-mcp-crypto.d.ts +32 -0
- package/dist/agent-remote-mcp-crypto.d.ts.map +1 -0
- package/dist/agent-remote-mcp-crypto.js +101 -0
- package/dist/agent-remote-mcp-crypto.js.map +1 -0
- package/dist/agent-remote-mcp-install.d.ts +299 -0
- package/dist/agent-remote-mcp-install.d.ts.map +1 -0
- package/dist/agent-remote-mcp-install.js +785 -0
- package/dist/agent-remote-mcp-install.js.map +1 -0
- package/dist/agent-remote-mcp-oauth.d.ts +73 -0
- package/dist/agent-remote-mcp-oauth.d.ts.map +1 -0
- package/dist/agent-remote-mcp-oauth.js +344 -0
- package/dist/agent-remote-mcp-oauth.js.map +1 -0
- package/dist/agent-remote-mcp-transport.d.ts +46 -0
- package/dist/agent-remote-mcp-transport.d.ts.map +1 -0
- package/dist/agent-remote-mcp-transport.js +225 -0
- package/dist/agent-remote-mcp-transport.js.map +1 -0
- package/dist/agent-remote-mcp.d.ts +147 -0
- package/dist/agent-remote-mcp.d.ts.map +1 -0
- package/dist/agent-remote-mcp.js +681 -0
- package/dist/agent-remote-mcp.js.map +1 -0
- package/dist/agent-room-redaction.d.ts +66 -0
- package/dist/agent-room-redaction.d.ts.map +1 -0
- package/dist/agent-room-redaction.js +264 -0
- package/dist/agent-room-redaction.js.map +1 -0
- package/dist/agent-rooms.d.ts +209 -0
- package/dist/agent-rooms.d.ts.map +1 -0
- package/dist/agent-rooms.js +244 -0
- package/dist/agent-rooms.js.map +1 -0
- package/dist/agent-runtime.d.ts +541 -0
- package/dist/agent-runtime.d.ts.map +1 -0
- package/dist/agent-runtime.js +4260 -0
- package/dist/agent-runtime.js.map +1 -0
- package/dist/agent-skill-archive.d.ts +20 -0
- package/dist/agent-skill-archive.d.ts.map +1 -0
- package/dist/agent-skill-archive.js +316 -0
- package/dist/agent-skill-archive.js.map +1 -0
- package/dist/agent-skill-discovery.d.ts +96 -0
- package/dist/agent-skill-discovery.d.ts.map +1 -0
- package/dist/agent-skill-discovery.js +464 -0
- package/dist/agent-skill-discovery.js.map +1 -0
- package/dist/agent-skill-format.d.ts +57 -0
- package/dist/agent-skill-format.d.ts.map +1 -0
- package/dist/agent-skill-format.js +238 -0
- package/dist/agent-skill-format.js.map +1 -0
- package/dist/agent-skill-import.d.ts +64 -0
- package/dist/agent-skill-import.d.ts.map +1 -0
- package/dist/agent-skill-import.js +295 -0
- package/dist/agent-skill-import.js.map +1 -0
- package/dist/agent-skill-publish.d.ts +51 -0
- package/dist/agent-skill-publish.d.ts.map +1 -0
- package/dist/agent-skill-publish.js +136 -0
- package/dist/agent-skill-publish.js.map +1 -0
- package/dist/agent-skill-store.d.ts +252 -0
- package/dist/agent-skill-store.d.ts.map +1 -0
- package/dist/agent-skill-store.js +514 -0
- package/dist/agent-skill-store.js.map +1 -0
- package/dist/agent-skills.d.ts +85 -0
- package/dist/agent-skills.d.ts.map +1 -0
- package/dist/agent-skills.js +291 -0
- package/dist/agent-skills.js.map +1 -0
- package/dist/agent-transcript-pager.d.ts +77 -0
- package/dist/agent-transcript-pager.d.ts.map +1 -0
- package/dist/agent-transcript-pager.js +210 -0
- package/dist/agent-transcript-pager.js.map +1 -0
- package/dist/agent-user-data-parts.d.ts +20 -0
- package/dist/agent-user-data-parts.d.ts.map +1 -0
- package/dist/agent-user-data-parts.js +161 -0
- package/dist/agent-user-data-parts.js.map +1 -0
- package/dist/agent-user-transcript.d.ts +46 -0
- package/dist/agent-user-transcript.d.ts.map +1 -0
- package/dist/agent-user-transcript.js +106 -0
- package/dist/agent-user-transcript.js.map +1 -0
- package/dist/agent-voice-notes.d.ts +15 -0
- package/dist/agent-voice-notes.d.ts.map +1 -0
- package/dist/agent-voice-notes.js +17 -0
- package/dist/agent-voice-notes.js.map +1 -0
- package/dist/agent-wakes.d.ts +213 -0
- package/dist/agent-wakes.d.ts.map +1 -0
- package/dist/agent-wakes.js +534 -0
- package/dist/agent-wakes.js.map +1 -0
- package/dist/agent.d.ts +1780 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +3123 -0
- package/dist/agent.js.map +1 -0
- package/dist/ai.d.ts +583 -0
- package/dist/ai.d.ts.map +1 -0
- package/dist/ai.js +1858 -0
- package/dist/ai.js.map +1 -0
- package/dist/app-manifest.d.ts +255 -0
- package/dist/app-manifest.d.ts.map +1 -0
- package/dist/app-manifest.js +372 -0
- package/dist/app-manifest.js.map +1 -0
- package/dist/app-users.d.ts +30 -0
- package/dist/app-users.d.ts.map +1 -0
- package/dist/app-users.js +91 -0
- package/dist/app-users.js.map +1 -0
- package/dist/audit-archive.d.ts +85 -0
- package/dist/audit-archive.d.ts.map +1 -0
- package/dist/audit-archive.js +178 -0
- package/dist/audit-archive.js.map +1 -0
- package/dist/audit.d.ts +54 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +95 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth-logger.d.ts +28 -0
- package/dist/auth-logger.d.ts.map +1 -0
- package/dist/auth-logger.js +53 -0
- package/dist/auth-logger.js.map +1 -0
- package/dist/auth-password-secret.d.ts +59 -0
- package/dist/auth-password-secret.d.ts.map +1 -0
- package/dist/auth-password-secret.js +409 -0
- package/dist/auth-password-secret.js.map +1 -0
- package/dist/auth-schema.d.ts +120 -0
- package/dist/auth-schema.d.ts.map +1 -0
- package/dist/auth-schema.js +285 -0
- package/dist/auth-schema.js.map +1 -0
- package/dist/auth.d.ts +321 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +1562 -0
- package/dist/auth.js.map +1 -0
- package/dist/backup-archive.d.ts +257 -0
- package/dist/backup-archive.d.ts.map +1 -0
- package/dist/backup-archive.js +2068 -0
- package/dist/backup-archive.js.map +1 -0
- package/dist/backup-crypto.d.ts +313 -0
- package/dist/backup-crypto.d.ts.map +1 -0
- package/dist/backup-crypto.js +693 -0
- package/dist/backup-crypto.js.map +1 -0
- package/dist/backup-format.d.ts +200 -0
- package/dist/backup-format.d.ts.map +1 -0
- package/dist/backup-format.js +1247 -0
- package/dist/backup-format.js.map +1 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -0
- package/dist/clock.d.ts +35 -0
- package/dist/clock.d.ts.map +1 -0
- package/dist/clock.js +83 -0
- package/dist/clock.js.map +1 -0
- package/dist/data-client.d.ts +187 -0
- package/dist/data-client.d.ts.map +1 -0
- package/dist/data-client.js +590 -0
- package/dist/data-client.js.map +1 -0
- package/dist/data-secrets.d.ts +164 -0
- package/dist/data-secrets.d.ts.map +1 -0
- package/dist/data-secrets.js +383 -0
- package/dist/data-secrets.js.map +1 -0
- package/dist/db.d.ts +453 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +324 -0
- package/dist/db.js.map +1 -0
- package/dist/dev-activity.d.ts +48 -0
- package/dist/dev-activity.d.ts.map +1 -0
- package/dist/dev-activity.js +50 -0
- package/dist/dev-activity.js.map +1 -0
- package/dist/do.d.ts +429 -0
- package/dist/do.d.ts.map +1 -0
- package/dist/do.js +4526 -0
- package/dist/do.js.map +1 -0
- package/dist/email.d.ts +137 -0
- package/dist/email.d.ts.map +1 -0
- package/dist/email.js +527 -0
- package/dist/email.js.map +1 -0
- package/dist/generated/admin-assets.d.ts +16 -0
- package/dist/generated/admin-assets.d.ts.map +1 -0
- package/dist/generated/admin-assets.js +16 -0
- package/dist/generated/admin-assets.js.map +1 -0
- package/dist/generated/admin-docs-assets.d.ts +3 -0
- package/dist/generated/admin-docs-assets.d.ts.map +1 -0
- package/dist/generated/admin-docs-assets.js +3 -0
- package/dist/generated/admin-docs-assets.js.map +1 -0
- package/dist/handoff-consumer.d.ts +79 -0
- package/dist/handoff-consumer.d.ts.map +1 -0
- package/dist/handoff-consumer.js +300 -0
- package/dist/handoff-consumer.js.map +1 -0
- package/dist/handoff.d.ts +70 -0
- package/dist/handoff.d.ts.map +1 -0
- package/dist/handoff.js +228 -0
- package/dist/handoff.js.map +1 -0
- package/dist/host-auth.d.ts +47 -0
- package/dist/host-auth.d.ts.map +1 -0
- package/dist/host-auth.js +59 -0
- package/dist/host-auth.js.map +1 -0
- package/dist/host-user.d.ts +15 -0
- package/dist/host-user.d.ts.map +1 -0
- package/dist/host-user.js +23 -0
- package/dist/host-user.js.map +1 -0
- package/dist/html.d.ts +35 -0
- package/dist/html.d.ts.map +1 -0
- package/dist/html.js +634 -0
- package/dist/html.js.map +1 -0
- package/dist/http-error.d.ts +13 -0
- package/dist/http-error.d.ts.map +1 -0
- package/dist/http-error.js +15 -0
- package/dist/http-error.js.map +1 -0
- package/dist/imports.d.ts +219 -0
- package/dist/imports.d.ts.map +1 -0
- package/dist/imports.js +571 -0
- package/dist/imports.js.map +1 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/inference-tape.d.ts +64 -0
- package/dist/inference-tape.d.ts.map +1 -0
- package/dist/inference-tape.js +278 -0
- package/dist/inference-tape.js.map +1 -0
- package/dist/internal.d.ts +15 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +48 -0
- package/dist/internal.js.map +1 -0
- package/dist/jobs.d.ts +796 -0
- package/dist/jobs.d.ts.map +1 -0
- package/dist/jobs.js +538 -0
- package/dist/jobs.js.map +1 -0
- package/dist/json-schema.d.ts +91 -0
- package/dist/json-schema.d.ts.map +1 -0
- package/dist/json-schema.js +884 -0
- package/dist/json-schema.js.map +1 -0
- package/dist/knowledge.d.ts +33 -0
- package/dist/knowledge.d.ts.map +1 -0
- package/dist/knowledge.js +348 -0
- package/dist/knowledge.js.map +1 -0
- package/dist/local-dev-chrome.d.ts +29 -0
- package/dist/local-dev-chrome.d.ts.map +1 -0
- package/dist/local-dev-chrome.js +514 -0
- package/dist/local-dev-chrome.js.map +1 -0
- package/dist/local-dev-personas.d.ts +15 -0
- package/dist/local-dev-personas.d.ts.map +1 -0
- package/dist/local-dev-personas.js +46 -0
- package/dist/local-dev-personas.js.map +1 -0
- package/dist/mcp.d.ts +567 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +2480 -0
- package/dist/mcp.js.map +1 -0
- package/dist/modules.d.ts +145 -0
- package/dist/modules.d.ts.map +1 -0
- package/dist/modules.js +164 -0
- package/dist/modules.js.map +1 -0
- package/dist/notification-client.d.ts +94 -0
- package/dist/notification-client.d.ts.map +1 -0
- package/dist/notification-client.js +127 -0
- package/dist/notification-client.js.map +1 -0
- package/dist/notifications.d.ts +447 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +1625 -0
- package/dist/notifications.js.map +1 -0
- package/dist/operator-data-query.d.ts +117 -0
- package/dist/operator-data-query.d.ts.map +1 -0
- package/dist/operator-data-query.js +431 -0
- package/dist/operator-data-query.js.map +1 -0
- package/dist/operator-mcp.d.ts +63 -0
- package/dist/operator-mcp.d.ts.map +1 -0
- package/dist/operator-mcp.js +2828 -0
- package/dist/operator-mcp.js.map +1 -0
- package/dist/outbound-https.d.ts +24 -0
- package/dist/outbound-https.d.ts.map +1 -0
- package/dist/outbound-https.js +172 -0
- package/dist/outbound-https.js.map +1 -0
- package/dist/pitr.d.ts +60 -0
- package/dist/pitr.d.ts.map +1 -0
- package/dist/pitr.js +94 -0
- package/dist/pitr.js.map +1 -0
- package/dist/platform.d.ts +199 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +196 -0
- package/dist/platform.js.map +1 -0
- package/dist/policies.d.ts +115 -0
- package/dist/policies.d.ts.map +1 -0
- package/dist/policies.js +158 -0
- package/dist/policies.js.map +1 -0
- package/dist/principal.d.ts +143 -0
- package/dist/principal.d.ts.map +1 -0
- package/dist/principal.js +174 -0
- package/dist/principal.js.map +1 -0
- package/dist/realtime.d.ts +280 -0
- package/dist/realtime.d.ts.map +1 -0
- package/dist/realtime.js +578 -0
- package/dist/realtime.js.map +1 -0
- package/dist/retrieval.d.ts +263 -0
- package/dist/retrieval.d.ts.map +1 -0
- package/dist/retrieval.js +602 -0
- package/dist/retrieval.js.map +1 -0
- package/dist/runtime-error-client.d.ts +10 -0
- package/dist/runtime-error-client.d.ts.map +1 -0
- package/dist/runtime-error-client.js +70 -0
- package/dist/runtime-error-client.js.map +1 -0
- package/dist/runtime-errors.d.ts +180 -0
- package/dist/runtime-errors.d.ts.map +1 -0
- package/dist/runtime-errors.js +572 -0
- package/dist/runtime-errors.js.map +1 -0
- package/dist/schema.d.ts +434 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +232 -0
- package/dist/schema.js.map +1 -0
- package/dist/search.d.ts +216 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +785 -0
- package/dist/search.js.map +1 -0
- package/dist/settings.d.ts +191 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +478 -0
- package/dist/settings.js.map +1 -0
- package/dist/sql.d.ts +7 -0
- package/dist/sql.d.ts.map +1 -0
- package/dist/sql.js +30 -0
- package/dist/sql.js.map +1 -0
- package/dist/storage-client.d.ts +69 -0
- package/dist/storage-client.d.ts.map +1 -0
- package/dist/storage-client.js +469 -0
- package/dist/storage-client.js.map +1 -0
- package/dist/storage.d.ts +21 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +82 -0
- package/dist/storage.js.map +1 -0
- package/dist/subject-domains.d.ts +15 -0
- package/dist/subject-domains.d.ts.map +1 -0
- package/dist/subject-domains.js +38 -0
- package/dist/subject-domains.js.map +1 -0
- package/dist/support.d.ts +132 -0
- package/dist/support.d.ts.map +1 -0
- package/dist/support.js +483 -0
- package/dist/support.js.map +1 -0
- package/dist/system-tables.d.ts +12 -0
- package/dist/system-tables.d.ts.map +1 -0
- package/dist/system-tables.js +225 -0
- package/dist/system-tables.js.map +1 -0
- package/dist/test.d.ts +57 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +454 -0
- package/dist/test.js.map +1 -0
- package/dist/vite.d.ts +82 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +82 -0
- package/dist/vite.js.map +1 -0
- package/dist/webpush-crypto.d.ts +39 -0
- package/dist/webpush-crypto.d.ts.map +1 -0
- package/dist/webpush-crypto.js +213 -0
- package/dist/webpush-crypto.js.map +1 -0
- package/dist/webpush-private.d.ts +11 -0
- package/dist/webpush-private.d.ts.map +1 -0
- package/dist/webpush-private.js +11 -0
- package/dist/webpush-private.js.map +1 -0
- package/dist/webpush.d.ts +86 -0
- package/dist/webpush.d.ts.map +1 -0
- package/dist/webpush.js +375 -0
- package/dist/webpush.js.map +1 -0
- package/dist/worker.d.ts +185 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +13021 -0
- package/dist/worker.js.map +1 -0
- package/package.json +161 -8
- package/README.md +0 -3
package/dist/actions.js
ADDED
|
@@ -0,0 +1,661 @@
|
|
|
1
|
+
import { disabledRaybaseAI } from "./ai.js";
|
|
2
|
+
import { authSecret, createRaybaseAuthContextRuntime, unsupportedRaybaseAuthContextRuntime } from "./auth.js";
|
|
3
|
+
import { normalizeNamedDefinitions } from "./internal.js";
|
|
4
|
+
import { jsonSchemaFor, parseWithSchema, restoreStrictModelValue, strictToolParameters } from "./json-schema.js";
|
|
5
|
+
import { unsupportedRaybaseJobsRuntime } from "./jobs.js";
|
|
6
|
+
import { evaluatePolicy, serializePolicyExpression } from "./policies.js";
|
|
7
|
+
import { principalOnBehalfOf } from "./principal.js";
|
|
8
|
+
/**
|
|
9
|
+
* Normalizes an agent's `actions` entries to
|
|
10
|
+
* `{ action, offerWhen?, disclosure? }`. Every consumer of an agent's
|
|
11
|
+
* registered actions routes through this, so no call site sees a binding
|
|
12
|
+
* where it expects a bare action. Registry, admin, and permission consumers
|
|
13
|
+
* use the full set; only model-facing tool assembly applies presentation
|
|
14
|
+
* rules. Malformed entries throw here, which surfaces at `defineAgent` time
|
|
15
|
+
* through the tool-name validators.
|
|
16
|
+
*/
|
|
17
|
+
export function agentActionRegistrations(actions) {
|
|
18
|
+
return (actions ?? []).map((entry) => {
|
|
19
|
+
if (entry && typeof entry === "object" && typeof entry.handler === "function") {
|
|
20
|
+
return { action: entry };
|
|
21
|
+
}
|
|
22
|
+
const binding = entry;
|
|
23
|
+
const action = binding && typeof binding === "object" ? binding.action : undefined;
|
|
24
|
+
if (!action || typeof action !== "object" || typeof action.name !== "string" || typeof action.handler !== "function") {
|
|
25
|
+
throw new Error("Raybase agent actions entries must be defined actions or { action, offerWhen?, disclosure? } bindings");
|
|
26
|
+
}
|
|
27
|
+
if (binding.offerWhen === undefined && binding.disclosure === undefined) {
|
|
28
|
+
throw new Error(`Raybase agent action binding for ${action.name} requires offerWhen or disclosure`);
|
|
29
|
+
}
|
|
30
|
+
if (binding.offerWhen !== undefined && typeof binding.offerWhen !== "function") {
|
|
31
|
+
throw new Error(`Raybase agent action binding for ${action.name} offerWhen must be a function`);
|
|
32
|
+
}
|
|
33
|
+
if (binding.disclosure !== undefined
|
|
34
|
+
&& binding.disclosure !== "full"
|
|
35
|
+
&& binding.disclosure !== "stub"
|
|
36
|
+
&& typeof binding.disclosure !== "function") {
|
|
37
|
+
throw new Error(`Raybase agent action binding for ${action.name} disclosure must be "full", "stub", or a function`);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
action,
|
|
41
|
+
...(binding.offerWhen ? { offerWhen: binding.offerWhen } : {}),
|
|
42
|
+
...(binding.disclosure !== undefined ? { disclosure: binding.disclosure } : {})
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const actionNamePattern = /^[A-Za-z][A-Za-z0-9_.:-]{0,127}$/;
|
|
47
|
+
export const unsupportedRaybaseAgentInvocationRuntime = {
|
|
48
|
+
memory: {
|
|
49
|
+
async list() {
|
|
50
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
51
|
+
},
|
|
52
|
+
async read() {
|
|
53
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
54
|
+
},
|
|
55
|
+
async readMany() {
|
|
56
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
runs: {
|
|
60
|
+
async recent() {
|
|
61
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
async run() {
|
|
65
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
66
|
+
},
|
|
67
|
+
async enqueue() {
|
|
68
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
69
|
+
},
|
|
70
|
+
async compose() {
|
|
71
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
72
|
+
},
|
|
73
|
+
async wake() {
|
|
74
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
75
|
+
},
|
|
76
|
+
async cancelWake() {
|
|
77
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
78
|
+
},
|
|
79
|
+
followUps() {
|
|
80
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
81
|
+
},
|
|
82
|
+
conversations: {
|
|
83
|
+
async readState() {
|
|
84
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
85
|
+
},
|
|
86
|
+
async resume() {
|
|
87
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
88
|
+
},
|
|
89
|
+
async composeOutcome() {
|
|
90
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
91
|
+
},
|
|
92
|
+
async removeMessage() {
|
|
93
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
94
|
+
},
|
|
95
|
+
async rewind() {
|
|
96
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
97
|
+
},
|
|
98
|
+
async rerun() {
|
|
99
|
+
throw new Error("ctx.agents is not available from this Raybase action adapter");
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
rooms: {
|
|
103
|
+
async create() { throw new Error("ctx.agents is not available from this Raybase action adapter"); },
|
|
104
|
+
async addMember() { throw new Error("ctx.agents is not available from this Raybase action adapter"); },
|
|
105
|
+
async removeMember() { throw new Error("ctx.agents is not available from this Raybase action adapter"); },
|
|
106
|
+
async leave() { throw new Error("ctx.agents is not available from this Raybase action adapter"); },
|
|
107
|
+
async redactMember() { throw new Error("ctx.agents is not available from this Raybase action adapter"); },
|
|
108
|
+
async transferOwner() { throw new Error("ctx.agents is not available from this Raybase action adapter"); },
|
|
109
|
+
async members() { throw new Error("ctx.agents is not available from this Raybase action adapter"); }
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
export class ActionDeniedError extends Error {
|
|
113
|
+
status = 403;
|
|
114
|
+
constructor(message) {
|
|
115
|
+
super(message);
|
|
116
|
+
this.name = "ActionDeniedError";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export class ActionInputValidationError extends Error {
|
|
120
|
+
status = 400;
|
|
121
|
+
constructor(actionName, cause) {
|
|
122
|
+
super(`Invalid input for action ${actionName}`);
|
|
123
|
+
this.name = "ActionInputValidationError";
|
|
124
|
+
this.cause = cause;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export class ActionOutputValidationError extends Error {
|
|
128
|
+
status = 500;
|
|
129
|
+
constructor(actionName, cause) {
|
|
130
|
+
super(`Action ${actionName} returned invalid output`);
|
|
131
|
+
this.name = "ActionOutputValidationError";
|
|
132
|
+
this.cause = cause;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
export class RaybaseActionError extends Error {
|
|
136
|
+
code;
|
|
137
|
+
status;
|
|
138
|
+
guidance;
|
|
139
|
+
retryable;
|
|
140
|
+
details;
|
|
141
|
+
constructor(message, options = {}) {
|
|
142
|
+
super(message);
|
|
143
|
+
this.name = "RaybaseActionError";
|
|
144
|
+
this.code = sanitizeActionErrorCode(options.code ?? "action_failed");
|
|
145
|
+
this.status = sanitizeStatus(options.status, 400);
|
|
146
|
+
this.guidance = options.guidance;
|
|
147
|
+
this.retryable = options.retryable;
|
|
148
|
+
this.details = options.details;
|
|
149
|
+
this.cause = options.cause;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export function actionError(message, options = {}) {
|
|
153
|
+
return new RaybaseActionError(message, options);
|
|
154
|
+
}
|
|
155
|
+
const ACTION_INPUT_ISSUE_LIMIT = 12;
|
|
156
|
+
const ACTION_INPUT_ISSUE_MESSAGE_LIMIT = 240;
|
|
157
|
+
function issuePath(path) {
|
|
158
|
+
if (!Array.isArray(path) || !path.length)
|
|
159
|
+
return "(root)";
|
|
160
|
+
return path.reduce((joined, segment) => {
|
|
161
|
+
if (typeof segment === "number")
|
|
162
|
+
return `${joined}[${segment}]`;
|
|
163
|
+
return joined ? `${joined}.${String(segment)}` : String(segment);
|
|
164
|
+
}, "");
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Why an input was refused, in a shape a caller can act on.
|
|
168
|
+
*
|
|
169
|
+
* A validation failure used to be reported as the bare sentence "Invalid input
|
|
170
|
+
* for action X" plus `retryable: true` — which tells a caller that something is
|
|
171
|
+
* wrong, that it should try again, and nothing whatsoever about what to change.
|
|
172
|
+
* An agent handed that has one move: guess. Birch spent six and a half minutes
|
|
173
|
+
* and nine model turns guessing at one rejected argument, on a request its
|
|
174
|
+
* schema could never have accepted, and the conversation grew by 84,000
|
|
175
|
+
* characters while it did (2026-08-25).
|
|
176
|
+
*
|
|
177
|
+
* So the issues travel. The validator throws either a zod error, whose
|
|
178
|
+
* `issues` already carry a path and a sentence each, or a plain `Error` from
|
|
179
|
+
* the JSON-schema walker, whose message names the path itself — both become the
|
|
180
|
+
* same list. It is capped in count and in length on purpose: this rides back
|
|
181
|
+
* into a model's context on a path that is already the tightest budget in the
|
|
182
|
+
* house, and a nested union can otherwise produce hundreds of them.
|
|
183
|
+
*
|
|
184
|
+
* Nothing here reads the *values* that were rejected, only where they sat and
|
|
185
|
+
* what the schema said. The caller supplied them; it does not need them back.
|
|
186
|
+
*/
|
|
187
|
+
export function actionInputIssues(cause) {
|
|
188
|
+
const raw = cause?.issues;
|
|
189
|
+
const issues = Array.isArray(raw)
|
|
190
|
+
? raw.flatMap((issue) => {
|
|
191
|
+
const value = issue;
|
|
192
|
+
return typeof value?.message === "string" && value.message.trim()
|
|
193
|
+
? [{ path: issuePath(value.path), message: value.message.trim().slice(0, ACTION_INPUT_ISSUE_MESSAGE_LIMIT) }]
|
|
194
|
+
: [];
|
|
195
|
+
})
|
|
196
|
+
: cause instanceof Error && cause.message.trim()
|
|
197
|
+
? [{ path: "(root)", message: cause.message.trim().slice(0, ACTION_INPUT_ISSUE_MESSAGE_LIMIT) }]
|
|
198
|
+
: [];
|
|
199
|
+
const seen = new Set();
|
|
200
|
+
return issues
|
|
201
|
+
.filter((issue) => {
|
|
202
|
+
const key = `${issue.path}:${issue.message}`;
|
|
203
|
+
if (seen.has(key))
|
|
204
|
+
return false;
|
|
205
|
+
seen.add(key);
|
|
206
|
+
return true;
|
|
207
|
+
})
|
|
208
|
+
.slice(0, ACTION_INPUT_ISSUE_LIMIT);
|
|
209
|
+
}
|
|
210
|
+
export function actionErrorPayload(actionName, error) {
|
|
211
|
+
if (error instanceof RaybaseActionError) {
|
|
212
|
+
return compactActionErrorPayload({
|
|
213
|
+
ok: false,
|
|
214
|
+
action: actionName,
|
|
215
|
+
code: error.code,
|
|
216
|
+
message: error.message,
|
|
217
|
+
guidance: error.guidance,
|
|
218
|
+
retryable: error.retryable,
|
|
219
|
+
details: error.details
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
if (error instanceof ActionInputValidationError) {
|
|
223
|
+
const issues = actionInputIssues(error.cause);
|
|
224
|
+
return compactActionErrorPayload({
|
|
225
|
+
ok: false,
|
|
226
|
+
action: actionName,
|
|
227
|
+
code: "invalid_input",
|
|
228
|
+
message: `Invalid input for action ${actionName}.`,
|
|
229
|
+
guidance: issues.length
|
|
230
|
+
? "Fix the fields listed in details and call again. If the schema has no shape for what was asked, say so instead of trying another arrangement of the same request."
|
|
231
|
+
: "Retry the action with arguments that match the input schema.",
|
|
232
|
+
retryable: true,
|
|
233
|
+
details: issues.length ? { issues } : undefined
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
if (error instanceof ActionDeniedError) {
|
|
237
|
+
return {
|
|
238
|
+
ok: false,
|
|
239
|
+
action: actionName,
|
|
240
|
+
code: "action_denied",
|
|
241
|
+
message: "The caller is not allowed to run this action.",
|
|
242
|
+
guidance: "Do not retry this action unless the caller changes permissions or signs in with an allowed account.",
|
|
243
|
+
retryable: false
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
export function defineAction(definition) {
|
|
249
|
+
validateActionName(definition.name);
|
|
250
|
+
validateActionPolicy(definition.policy, definition.name, definition.audience);
|
|
251
|
+
return { ...definition };
|
|
252
|
+
}
|
|
253
|
+
export function defineActions(actions) {
|
|
254
|
+
const normalized = normalizeNamedDefinitions("action", actions, validateActionName);
|
|
255
|
+
for (const action of Object.values(normalized)) {
|
|
256
|
+
validateActionPolicy(action.policy, action.name, action.audience);
|
|
257
|
+
}
|
|
258
|
+
return normalized;
|
|
259
|
+
}
|
|
260
|
+
export function validateActionName(name) {
|
|
261
|
+
if (!actionNamePattern.test(name)) {
|
|
262
|
+
throw new Error(`Invalid Raybase action name "${name}". Use letters, numbers, "_", "-", ".", or ":".`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
export function validateActionPolicy(policy, actionName = "action", audience = "signed-in") {
|
|
266
|
+
if (!policy) {
|
|
267
|
+
if (actionAudienceAllows(audience, "signed-in"))
|
|
268
|
+
throw new Error(`Raybase action ${actionName} must declare a policy`);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
switch (policy.kind) {
|
|
272
|
+
case "allowAll":
|
|
273
|
+
case "denyAll":
|
|
274
|
+
case "role":
|
|
275
|
+
return;
|
|
276
|
+
case "owns":
|
|
277
|
+
throw new Error(`Raybase action ${actionName} cannot use owns(); action policies must be row-independent`);
|
|
278
|
+
case "operator":
|
|
279
|
+
case "servicePrincipal":
|
|
280
|
+
throw new Error(`Raybase action ${actionName} cannot use ${policy.kind} policy in user-facing AI`);
|
|
281
|
+
case "anyOf":
|
|
282
|
+
case "allOf":
|
|
283
|
+
for (const candidate of policy.policies)
|
|
284
|
+
validateActionPolicy(candidate, actionName);
|
|
285
|
+
return;
|
|
286
|
+
case "not":
|
|
287
|
+
validateActionPolicy(policy.policy, actionName);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
export function validateActionExposure(action, options = {}) {
|
|
292
|
+
validateActionName(action.name);
|
|
293
|
+
if (action.requiresInternalControl && !options.allowInternalControl) {
|
|
294
|
+
throw new Error(`Raybase action ${action.name} requires internal control storage and cannot be adapted to this surface`);
|
|
295
|
+
}
|
|
296
|
+
const effectivePolicy = options.policy ?? action.policy;
|
|
297
|
+
if (options.requirePolicy && !effectivePolicy)
|
|
298
|
+
throw new Error(`Raybase action ${action.name} must declare an effective policy for this surface`);
|
|
299
|
+
validateActionPolicy(effectivePolicy, action.name, action.audience);
|
|
300
|
+
if (options.rowAuthorization) {
|
|
301
|
+
if (!options.rowAuthorization.table)
|
|
302
|
+
throw new Error(`Raybase action ${action.name} rowAuthorization.table is required`);
|
|
303
|
+
if (!options.rowAuthorization.action)
|
|
304
|
+
throw new Error(`Raybase action ${action.name} rowAuthorization.action is required`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
export function evaluateActionPolicy(policy, user) {
|
|
308
|
+
return evaluatePolicy(policy, { user, action: "create" });
|
|
309
|
+
}
|
|
310
|
+
export function actionAudienceAllows(audience, kind) {
|
|
311
|
+
const normalized = audience ?? "signed-in";
|
|
312
|
+
return Array.isArray(normalized) ? normalized.includes(kind) : normalized === kind;
|
|
313
|
+
}
|
|
314
|
+
export function parseActionInput(action, raw) {
|
|
315
|
+
return parseWithSchema(action.input, raw ?? {}, action.name);
|
|
316
|
+
}
|
|
317
|
+
export function parseActionOutput(action, raw) {
|
|
318
|
+
if (!action.output)
|
|
319
|
+
return raw;
|
|
320
|
+
return parseWithSchema(action.output, raw, `${action.name}.output`);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* The schema an agent tool advertises for this action.
|
|
324
|
+
*
|
|
325
|
+
* Strict providers validate every tool's `parameters` and refuse the whole
|
|
326
|
+
* request over one offender, with no indication of which — so a green build and
|
|
327
|
+
* a passing suite still produce a turn that never happens. An action may
|
|
328
|
+
* declare the exact schema to publish; otherwise the validator's schema is
|
|
329
|
+
* conformed to the strict subset, which changes nothing about validation.
|
|
330
|
+
*/
|
|
331
|
+
export function actionInputJsonSchema(action) {
|
|
332
|
+
return strictToolParameters(action.inputJsonSchema ?? jsonSchemaFor(action.input, { reused: "ref" }));
|
|
333
|
+
}
|
|
334
|
+
export function actionOutputJsonSchema(action) {
|
|
335
|
+
return action.output ? jsonSchemaFor(action.output) : undefined;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Model-supplied input with the strict wire form undone.
|
|
339
|
+
*
|
|
340
|
+
* What a strict provider will carry is not what an action accepts: an omitted
|
|
341
|
+
* optional arrives as an explicit `null`, an open record arrives as a string of
|
|
342
|
+
* JSON, and a discriminated union arrives flattened into one object holding
|
|
343
|
+
* every branch's properties with the other branch's padded out. Each of those
|
|
344
|
+
* is refused by the action's own schema a moment later, so the padding comes
|
|
345
|
+
* off here — against the schema the action *declared*, which still knows which
|
|
346
|
+
* properties were optional and which shape belongs to which branch.
|
|
347
|
+
*
|
|
348
|
+
* Anything not padded passes through untouched, so this is safe on input from
|
|
349
|
+
* any surface. It runs on the agent tool path only, because that is the only
|
|
350
|
+
* surface whose input had to be squeezed through the subset.
|
|
351
|
+
*/
|
|
352
|
+
export function restoreStrictActionInput(action, raw) {
|
|
353
|
+
if (raw === undefined || raw === null)
|
|
354
|
+
return raw;
|
|
355
|
+
try {
|
|
356
|
+
return restoreStrictModelValue(raw, action.inputJsonSchema ?? jsonSchemaFor(action.input));
|
|
357
|
+
}
|
|
358
|
+
catch {
|
|
359
|
+
// A schema Core cannot read is the action's own problem to report: let its
|
|
360
|
+
// validator produce the message rather than failing here with none.
|
|
361
|
+
return raw;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
export function actionManifestDescriptor(action) {
|
|
365
|
+
const requiredInputKeys = actionRequiredInputKeys(action);
|
|
366
|
+
return compactActionManifestDescriptor({
|
|
367
|
+
name: action.name,
|
|
368
|
+
policy: serializePolicyExpression(action.policy),
|
|
369
|
+
annotations: compactActionAnnotations(action.annotations),
|
|
370
|
+
resource: compactActionResource(action.resource),
|
|
371
|
+
requiredInputKeys: requiredInputKeys?.length ? requiredInputKeys : undefined,
|
|
372
|
+
hasOutputSchema: Boolean(action.output)
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
function actionRequiredInputKeys(action) {
|
|
376
|
+
try {
|
|
377
|
+
const inputSchema = actionInputJsonSchema(action);
|
|
378
|
+
return Array.isArray(inputSchema.required)
|
|
379
|
+
? inputSchema.required.filter((key) => typeof key === "string").sort()
|
|
380
|
+
: undefined;
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function compactActionManifestDescriptor(descriptor) {
|
|
387
|
+
const compacted = { name: descriptor.name };
|
|
388
|
+
if (descriptor.policy)
|
|
389
|
+
compacted.policy = descriptor.policy;
|
|
390
|
+
if (descriptor.annotations)
|
|
391
|
+
compacted.annotations = descriptor.annotations;
|
|
392
|
+
if (descriptor.resource)
|
|
393
|
+
compacted.resource = descriptor.resource;
|
|
394
|
+
if (descriptor.requiredInputKeys?.length)
|
|
395
|
+
compacted.requiredInputKeys = descriptor.requiredInputKeys;
|
|
396
|
+
if (descriptor.hasOutputSchema !== undefined)
|
|
397
|
+
compacted.hasOutputSchema = descriptor.hasOutputSchema;
|
|
398
|
+
return compacted;
|
|
399
|
+
}
|
|
400
|
+
function compactActionAnnotations(annotations) {
|
|
401
|
+
if (!annotations)
|
|
402
|
+
return undefined;
|
|
403
|
+
const compacted = {};
|
|
404
|
+
if (annotations.readOnlyHint === true)
|
|
405
|
+
compacted.readOnlyHint = true;
|
|
406
|
+
if (annotations.destructiveHint === true)
|
|
407
|
+
compacted.destructiveHint = true;
|
|
408
|
+
if (annotations.idempotentHint === true)
|
|
409
|
+
compacted.idempotentHint = true;
|
|
410
|
+
return Object.keys(compacted).length ? compacted : undefined;
|
|
411
|
+
}
|
|
412
|
+
function compactActionResource(resource) {
|
|
413
|
+
if (!resource?.name)
|
|
414
|
+
return undefined;
|
|
415
|
+
const compacted = { name: resource.name };
|
|
416
|
+
if (resource.operation)
|
|
417
|
+
compacted.operation = resource.operation;
|
|
418
|
+
if (resource.inputRefs && Object.keys(resource.inputRefs).length)
|
|
419
|
+
compacted.inputRefs = resource.inputRefs;
|
|
420
|
+
return compacted;
|
|
421
|
+
}
|
|
422
|
+
export function rowAuthorization(definition) {
|
|
423
|
+
return definition;
|
|
424
|
+
}
|
|
425
|
+
export async function executeAction(action, rawInput, ctx, options = {}) {
|
|
426
|
+
const surface = options.surface ?? ctx.surface ?? "action";
|
|
427
|
+
let input;
|
|
428
|
+
try {
|
|
429
|
+
input = parseActionInput(action, rawInput);
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
await actionAudit(ctx, "action.error", action.name, { surface, stage: "input", error: errorMessage(error) });
|
|
433
|
+
await options.onAccounting?.({ status: "error", actionName: action.name, actionInput: rawInput, error: errorMessage(error) });
|
|
434
|
+
throw new ActionInputValidationError(action.name, error);
|
|
435
|
+
}
|
|
436
|
+
const audience = options.audience ?? audienceFromPrincipal(ctx.principal);
|
|
437
|
+
if (!actionAudienceAllows(action.audience, audience)) {
|
|
438
|
+
await recordDenied(action, input, ctx, options, "Audience denied", surface);
|
|
439
|
+
throw new ActionDeniedError(`Audience denied action ${action.name}`);
|
|
440
|
+
}
|
|
441
|
+
const effectivePolicy = options.policy ?? action.policy;
|
|
442
|
+
if (options.requirePolicy && !effectivePolicy) {
|
|
443
|
+
await recordDenied(action, input, ctx, options, "Missing policy", surface);
|
|
444
|
+
throw new ActionDeniedError(`Missing policy for action ${action.name}`);
|
|
445
|
+
}
|
|
446
|
+
const authority = principalOnBehalfOf(ctx.principal);
|
|
447
|
+
if (authority.kind === "user" && !evaluateActionPolicy(effectivePolicy, authority.user)) {
|
|
448
|
+
await recordDenied(action, input, ctx, options, "Policy denied", surface);
|
|
449
|
+
throw new ActionDeniedError(`Policy denied action ${action.name}`);
|
|
450
|
+
}
|
|
451
|
+
await authorizeRow(action, input, ctx, options, surface);
|
|
452
|
+
const idempotencyKey = await resolveIdempotencyKey(action, input, ctx, options);
|
|
453
|
+
await actionAudit(ctx, "action.invoke", action.name, { surface, idempotencyKey });
|
|
454
|
+
try {
|
|
455
|
+
const result = await action.handler(ctx, input);
|
|
456
|
+
let output;
|
|
457
|
+
try {
|
|
458
|
+
output = parseActionOutput(action, result);
|
|
459
|
+
}
|
|
460
|
+
catch (error) {
|
|
461
|
+
await actionAudit(ctx, "action.error", action.name, { surface, stage: "output", idempotencyKey, error: errorMessage(error) });
|
|
462
|
+
await options.onAccounting?.({ status: "error", actionName: action.name, actionInput: input, error: "Invalid action output" });
|
|
463
|
+
throw new ActionOutputValidationError(action.name, error);
|
|
464
|
+
}
|
|
465
|
+
await actionAudit(ctx, "action.success", action.name, { surface, idempotencyKey });
|
|
466
|
+
await options.onAccounting?.({ status: "ok", actionName: action.name, actionInput: input });
|
|
467
|
+
return output;
|
|
468
|
+
}
|
|
469
|
+
catch (error) {
|
|
470
|
+
if (error instanceof ActionOutputValidationError)
|
|
471
|
+
throw error;
|
|
472
|
+
await actionAudit(ctx, "action.error", action.name, { surface, idempotencyKey, error: errorMessage(error) });
|
|
473
|
+
await options.onAccounting?.({ status: "error", actionName: action.name, actionInput: input, error: errorMessage(error) });
|
|
474
|
+
throw error;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
export function mcpToolFromAction(action, options = {}) {
|
|
478
|
+
validateActionExposure(action, { policy: options.policy, rowAuthorization: options.rowAuthorization, requirePolicy: true, allowInternalControl: options.allowInternalControl });
|
|
479
|
+
return {
|
|
480
|
+
name: action.name,
|
|
481
|
+
description: action.description,
|
|
482
|
+
inputSchema: action.input,
|
|
483
|
+
annotations: action.annotations,
|
|
484
|
+
scopes: options.scopes ?? (action.annotations?.readOnlyHint ? ["mcp:read", `mcp:tool:${action.name}`] : ["mcp:write", `mcp:tool:${action.name}`]),
|
|
485
|
+
raybase: {
|
|
486
|
+
action: actionManifestDescriptor(action)
|
|
487
|
+
},
|
|
488
|
+
handler: async (args, ctx) => {
|
|
489
|
+
try {
|
|
490
|
+
const extraContext = await options.context?.(ctx);
|
|
491
|
+
const result = await executeAction(action, args, {
|
|
492
|
+
request: ctx.request,
|
|
493
|
+
env: ctx.env,
|
|
494
|
+
user: ctx.user,
|
|
495
|
+
principal: { kind: "user", user: ctx.user, userId: ctx.user.id, roles: ctx.user.appRoles },
|
|
496
|
+
db: ctx.db,
|
|
497
|
+
control: ctx.control,
|
|
498
|
+
auth: ctx.control
|
|
499
|
+
? createRaybaseAuthContextRuntime({
|
|
500
|
+
db: ctx.control,
|
|
501
|
+
secret: authSecret(ctx.env),
|
|
502
|
+
config: ctx.config,
|
|
503
|
+
sessionAuthenticated: false,
|
|
504
|
+
principal: { kind: "user", user: ctx.user, userId: ctx.user.id, roles: ctx.user.appRoles },
|
|
505
|
+
request: ctx.request,
|
|
506
|
+
audit: (event) => ctx.audit(event.action, {
|
|
507
|
+
target: event.target,
|
|
508
|
+
metadata: event.metadata,
|
|
509
|
+
source: event.source
|
|
510
|
+
})
|
|
511
|
+
})
|
|
512
|
+
: unsupportedRaybaseAuthContextRuntime,
|
|
513
|
+
agents: ctx.agents ?? unsupportedRaybaseAgentInvocationRuntime,
|
|
514
|
+
ai: ctx.ai ?? disabledRaybaseAI,
|
|
515
|
+
jobs: ctx.jobs ?? unsupportedRaybaseJobsRuntime,
|
|
516
|
+
route: "/mcp",
|
|
517
|
+
surface: "mcp",
|
|
518
|
+
audit: (event) => ctx.audit(event.action, { target: event.target, metadata: event.metadata, source: event.source }),
|
|
519
|
+
...extraContext
|
|
520
|
+
}, {
|
|
521
|
+
surface: "mcp",
|
|
522
|
+
policy: options.policy,
|
|
523
|
+
requirePolicy: true,
|
|
524
|
+
rowAuthorization: options.rowAuthorization
|
|
525
|
+
});
|
|
526
|
+
return actionResultToMcp(action.name, result);
|
|
527
|
+
}
|
|
528
|
+
catch (error) {
|
|
529
|
+
return actionErrorToMcp(action.name, error);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
export function actionRoute(action, options = {}) {
|
|
535
|
+
validateActionExposure(action, { policy: options.policy, rowAuthorization: options.rowAuthorization, requirePolicy: true });
|
|
536
|
+
return {
|
|
537
|
+
action,
|
|
538
|
+
path: options.path ?? `/api/actions/${encodeURIComponent(action.name)}`,
|
|
539
|
+
policy: options.policy,
|
|
540
|
+
audience: options.audience,
|
|
541
|
+
rowAuthorization: options.rowAuthorization
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
export function serverFnFromAction(action, options = {}) {
|
|
545
|
+
const endpoint = options.endpoint ?? `/api/actions/${encodeURIComponent(action.name)}`;
|
|
546
|
+
return async (input, callOptions = {}) => {
|
|
547
|
+
const runFetch = callOptions.fetch ?? options.fetch ?? fetch;
|
|
548
|
+
const headers = new Headers(options.headers);
|
|
549
|
+
for (const [key, value] of new Headers(callOptions.headers))
|
|
550
|
+
headers.set(key, value);
|
|
551
|
+
if (!headers.has("content-type"))
|
|
552
|
+
headers.set("content-type", "application/json");
|
|
553
|
+
const response = await runFetch(endpoint, {
|
|
554
|
+
method: "POST",
|
|
555
|
+
headers,
|
|
556
|
+
credentials: options.credentials,
|
|
557
|
+
signal: callOptions.signal,
|
|
558
|
+
body: JSON.stringify(input ?? {})
|
|
559
|
+
});
|
|
560
|
+
const body = await response.json().catch(() => ({}));
|
|
561
|
+
const bodyRecord = body && typeof body === "object" ? body : {};
|
|
562
|
+
if (!response.ok) {
|
|
563
|
+
const actionError = actionErrorFromResponseBody(bodyRecord, response.status);
|
|
564
|
+
if (actionError)
|
|
565
|
+
throw actionError;
|
|
566
|
+
throw new Error(typeof bodyRecord.error === "string" ? bodyRecord.error : `Action ${action.name} failed`);
|
|
567
|
+
}
|
|
568
|
+
return body;
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function actionResultToMcp(actionName, result) {
|
|
572
|
+
const text = typeof result === "string" ? result : `Action ${actionName} completed.`;
|
|
573
|
+
return { content: [{ type: "text", text }], structuredContent: result };
|
|
574
|
+
}
|
|
575
|
+
function actionErrorToMcp(actionName, error) {
|
|
576
|
+
const payload = actionErrorPayload(actionName, error) ?? {
|
|
577
|
+
ok: false,
|
|
578
|
+
action: actionName,
|
|
579
|
+
code: "action_failed",
|
|
580
|
+
message: `Action ${actionName} failed.`,
|
|
581
|
+
guidance: "The action failed before returning a usable result. Inspect server logs or retry later.",
|
|
582
|
+
retryable: false
|
|
583
|
+
};
|
|
584
|
+
return {
|
|
585
|
+
content: [{ type: "text", text: payload.message }],
|
|
586
|
+
structuredContent: payload,
|
|
587
|
+
isError: true
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
function audienceFromPrincipal(principal) {
|
|
591
|
+
return principalOnBehalfOf(principal).kind === "public" ? "public" : "signed-in";
|
|
592
|
+
}
|
|
593
|
+
async function authorizeRow(action, input, ctx, options, surface) {
|
|
594
|
+
const rowAuth = options.rowAuthorization;
|
|
595
|
+
if (!rowAuth)
|
|
596
|
+
return;
|
|
597
|
+
const id = rowAuth.id(input, ctx);
|
|
598
|
+
if (!id && rowAuth.action !== "create") {
|
|
599
|
+
await recordDenied(action, input, ctx, options, "Row authorization id missing", surface);
|
|
600
|
+
throw new ActionDeniedError(`Policy denied action ${action.name}`);
|
|
601
|
+
}
|
|
602
|
+
const rowPayload = input && typeof input === "object" && !Array.isArray(input)
|
|
603
|
+
? input
|
|
604
|
+
: undefined;
|
|
605
|
+
const authorized = await ctx.db.authorize(rowAuth.table, rowAuth.action, id ? String(id) : undefined, rowPayload);
|
|
606
|
+
if (!authorized) {
|
|
607
|
+
await recordDenied(action, input, ctx, options, "Row authorization denied", surface);
|
|
608
|
+
throw new ActionDeniedError(`Policy denied action ${action.name}`);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
async function resolveIdempotencyKey(action, input, ctx, options) {
|
|
612
|
+
const source = options.idempotencyKey ?? action.idempotencyKey;
|
|
613
|
+
if (typeof source === "string")
|
|
614
|
+
return source || null;
|
|
615
|
+
if (!source)
|
|
616
|
+
return null;
|
|
617
|
+
return (await source(input, ctx)) ?? null;
|
|
618
|
+
}
|
|
619
|
+
async function recordDenied(action, input, ctx, options, reason, surface) {
|
|
620
|
+
await actionAudit(ctx, "action.denied", action.name, { surface, reason });
|
|
621
|
+
await options.onAccounting?.({ status: "denied", actionName: action.name, actionInput: input, error: reason });
|
|
622
|
+
}
|
|
623
|
+
async function actionAudit(ctx, auditAction, actionName, metadata) {
|
|
624
|
+
// An agent-driven call is attributed to the agent, with the person it ran
|
|
625
|
+
// for as runAs, so the trail never confuses the two.
|
|
626
|
+
const agent = ctx.principal.kind === "agent" ? ctx.principal : null;
|
|
627
|
+
await ctx.audit({
|
|
628
|
+
action: auditAction,
|
|
629
|
+
target: { type: "raybase_action", id: actionName },
|
|
630
|
+
metadata: agent ? { ...metadata, agentName: agent.agent, ...(agent.runId ? { runId: agent.runId } : {}) } : metadata,
|
|
631
|
+
...(agent ? { actor: agent, runAs: agent.onBehalfOf.kind === "public" ? null : agent.onBehalfOf } : {})
|
|
632
|
+
}).catch(() => undefined);
|
|
633
|
+
}
|
|
634
|
+
function errorMessage(error) {
|
|
635
|
+
return error instanceof Error ? error.message : String(error);
|
|
636
|
+
}
|
|
637
|
+
function actionErrorFromResponseBody(body, status) {
|
|
638
|
+
const payload = body.actionError && typeof body.actionError === "object" && !Array.isArray(body.actionError)
|
|
639
|
+
? body.actionError
|
|
640
|
+
: null;
|
|
641
|
+
if (!payload || typeof payload.message !== "string")
|
|
642
|
+
return null;
|
|
643
|
+
return new RaybaseActionError(payload.message, {
|
|
644
|
+
code: typeof payload.code === "string" ? payload.code : undefined,
|
|
645
|
+
status,
|
|
646
|
+
guidance: typeof payload.guidance === "string" ? payload.guidance : undefined,
|
|
647
|
+
retryable: typeof payload.retryable === "boolean" ? payload.retryable : undefined,
|
|
648
|
+
details: payload.details
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
function sanitizeActionErrorCode(value) {
|
|
652
|
+
const code = value.trim().toLowerCase().replace(/[^a-z0-9_.:-]+/g, "_").replace(/^_+|_+$/g, "");
|
|
653
|
+
return code || "action_failed";
|
|
654
|
+
}
|
|
655
|
+
function sanitizeStatus(value, fallback) {
|
|
656
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 400 && value <= 599 ? Math.floor(value) : fallback;
|
|
657
|
+
}
|
|
658
|
+
function compactActionErrorPayload(payload) {
|
|
659
|
+
return Object.fromEntries(Object.entries(payload).filter(([, value]) => value !== undefined));
|
|
660
|
+
}
|
|
661
|
+
//# sourceMappingURL=actions.js.map
|