@ixo/oracle-runtime 0.0.1
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/dist/bootstrap/ambient-factory.d.ts +32 -0
- package/dist/bootstrap/ambient-factory.d.ts.map +1 -0
- package/dist/bootstrap/ambient-factory.js +128 -0
- package/dist/bootstrap/create-oracle-app.d.ts +112 -0
- package/dist/bootstrap/create-oracle-app.d.ts.map +1 -0
- package/dist/bootstrap/create-oracle-app.js +530 -0
- package/dist/bootstrap/graceful-shutdown.d.ts +20 -0
- package/dist/bootstrap/graceful-shutdown.d.ts.map +1 -0
- package/dist/bootstrap/graceful-shutdown.js +61 -0
- package/dist/bootstrap/index.d.ts +13 -0
- package/dist/bootstrap/index.d.ts.map +1 -0
- package/dist/bootstrap/index.js +6 -0
- package/dist/bootstrap/inspect.d.ts +74 -0
- package/dist/bootstrap/inspect.d.ts.map +1 -0
- package/dist/bootstrap/inspect.js +111 -0
- package/dist/bootstrap/plugin-loader.d.ts +50 -0
- package/dist/bootstrap/plugin-loader.d.ts.map +1 -0
- package/dist/bootstrap/plugin-loader.js +119 -0
- package/dist/bootstrap/runtime-app-module.d.ts +38 -0
- package/dist/bootstrap/runtime-app-module.d.ts.map +1 -0
- package/dist/bootstrap/runtime-app-module.js +114 -0
- package/dist/bootstrap/schema-composer.d.ts +46 -0
- package/dist/bootstrap/schema-composer.d.ts.map +1 -0
- package/dist/bootstrap/schema-composer.js +65 -0
- package/dist/config/base-env-config.d.ts +31 -0
- package/dist/config/base-env-config.d.ts.map +1 -0
- package/dist/config/base-env-config.js +70 -0
- package/dist/config/base-env-schema.d.ts +77 -0
- package/dist/config/base-env-schema.d.ts.map +1 -0
- package/dist/config/base-env-schema.js +102 -0
- package/dist/events/scoped-emitter.d.ts +33 -0
- package/dist/events/scoped-emitter.d.ts.map +1 -0
- package/dist/events/scoped-emitter.js +32 -0
- package/dist/graph/index.d.ts +8 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +6 -0
- package/dist/graph/main-agent-types.d.ts +88 -0
- package/dist/graph/main-agent-types.d.ts.map +1 -0
- package/dist/graph/main-agent-types.js +20 -0
- package/dist/graph/main-agent.d.ts +16 -0
- package/dist/graph/main-agent.d.ts.map +1 -0
- package/dist/graph/main-agent.js +251 -0
- package/dist/graph/middlewares/capability-gate-middleware.d.ts +35 -0
- package/dist/graph/middlewares/capability-gate-middleware.d.ts.map +1 -0
- package/dist/graph/middlewares/capability-gate-middleware.js +54 -0
- package/dist/graph/middlewares/index.d.ts +7 -0
- package/dist/graph/middlewares/index.d.ts.map +1 -0
- package/dist/graph/middlewares/index.js +6 -0
- package/dist/graph/middlewares/page-context-middleware.d.ts +23 -0
- package/dist/graph/middlewares/page-context-middleware.d.ts.map +1 -0
- package/dist/graph/middlewares/page-context-middleware.js +68 -0
- package/dist/graph/middlewares/safety-guardrail-middleware.d.ts +26 -0
- package/dist/graph/middlewares/safety-guardrail-middleware.d.ts.map +1 -0
- package/dist/graph/middlewares/safety-guardrail-middleware.js +88 -0
- package/dist/graph/middlewares/summarization-middleware.d.ts +17 -0
- package/dist/graph/middlewares/summarization-middleware.d.ts.map +1 -0
- package/dist/graph/middlewares/summarization-middleware.js +62 -0
- package/dist/graph/middlewares/tool-repetition-guard-middleware.d.ts +24 -0
- package/dist/graph/middlewares/tool-repetition-guard-middleware.d.ts.map +1 -0
- package/dist/graph/middlewares/tool-repetition-guard-middleware.js +112 -0
- package/dist/graph/middlewares/tool-validation-middleware.d.ts +24 -0
- package/dist/graph/middlewares/tool-validation-middleware.d.ts.map +1 -0
- package/dist/graph/middlewares/tool-validation-middleware.js +61 -0
- package/dist/graph/prompt-composer.d.ts +69 -0
- package/dist/graph/prompt-composer.d.ts.map +1 -0
- package/dist/graph/prompt-composer.js +315 -0
- package/dist/graph/state.d.ts +65 -0
- package/dist/graph/state.d.ts.map +1 -0
- package/dist/graph/state.js +60 -0
- package/dist/graph/sub-agent-fallback.d.ts +56 -0
- package/dist/graph/sub-agent-fallback.d.ts.map +1 -0
- package/dist/graph/sub-agent-fallback.js +79 -0
- package/dist/graph/subagent-as-tool.d.ts +77 -0
- package/dist/graph/subagent-as-tool.d.ts.map +1 -0
- package/dist/graph/subagent-as-tool.js +197 -0
- package/dist/graph/wrap-plugin-tool.d.ts +28 -0
- package/dist/graph/wrap-plugin-tool.d.ts.map +1 -0
- package/dist/graph/wrap-plugin-tool.js +30 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/llm/index.d.ts +2 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +1 -0
- package/dist/llm/llm-provider.d.ts +41 -0
- package/dist/llm/llm-provider.d.ts.map +1 -0
- package/dist/llm/llm-provider.js +130 -0
- package/dist/manifest/index.d.ts +6 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/manifest/index.js +3 -0
- package/dist/manifest/schema.d.ts +31 -0
- package/dist/manifest/schema.d.ts.map +1 -0
- package/dist/manifest/schema.js +44 -0
- package/dist/manifest/tier1-renderer.d.ts +31 -0
- package/dist/manifest/tier1-renderer.d.ts.map +1 -0
- package/dist/manifest/tier1-renderer.js +68 -0
- package/dist/manifest/validator.d.ts +34 -0
- package/dist/manifest/validator.d.ts.map +1 -0
- package/dist/manifest/validator.js +111 -0
- package/dist/matrix/checkpointer/matrix-upload-utils.d.ts +66 -0
- package/dist/matrix/checkpointer/matrix-upload-utils.d.ts.map +1 -0
- package/dist/matrix/checkpointer/matrix-upload-utils.js +228 -0
- package/dist/matrix/checkpointer/type.d.ts +4 -0
- package/dist/matrix/checkpointer/type.d.ts.map +1 -0
- package/dist/matrix/checkpointer/type.js +1 -0
- package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.module.d.ts +3 -0
- package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.module.d.ts.map +1 -0
- package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.module.js +22 -0
- package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.service.d.ts +93 -0
- package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.service.d.ts.map +1 -0
- package/dist/matrix/checkpointer/user-matrix-sqlite-sync-service.service.js +856 -0
- package/dist/matrix/room-membership.d.ts +11 -0
- package/dist/matrix/room-membership.d.ts.map +1 -0
- package/dist/matrix/room-membership.js +33 -0
- package/dist/meta-tools/index.d.ts +28 -0
- package/dist/meta-tools/index.d.ts.map +1 -0
- package/dist/meta-tools/index.js +24 -0
- package/dist/meta-tools/list-capabilities.d.ts +12 -0
- package/dist/meta-tools/list-capabilities.d.ts.map +1 -0
- package/dist/meta-tools/list-capabilities.js +55 -0
- package/dist/meta-tools/load-capability.d.ts +24 -0
- package/dist/meta-tools/load-capability.d.ts.map +1 -0
- package/dist/meta-tools/load-capability.js +82 -0
- package/dist/modules/auth/auth-header.middleware.d.ts +37 -0
- package/dist/modules/auth/auth-header.middleware.d.ts.map +1 -0
- package/dist/modules/auth/auth-header.middleware.js +115 -0
- package/dist/modules/auth/auth.module.d.ts +9 -0
- package/dist/modules/auth/auth.module.d.ts.map +1 -0
- package/dist/modules/auth/auth.module.js +27 -0
- package/dist/modules/auth/index.d.ts +3 -0
- package/dist/modules/auth/index.d.ts.map +1 -0
- package/dist/modules/auth/index.js +2 -0
- package/dist/modules/auth/validate-ucan-delegation.d.ts +31 -0
- package/dist/modules/auth/validate-ucan-delegation.d.ts.map +1 -0
- package/dist/modules/auth/validate-ucan-delegation.js +36 -0
- package/dist/modules/blob-store/blob-store.module.d.ts +10 -0
- package/dist/modules/blob-store/blob-store.module.d.ts.map +1 -0
- package/dist/modules/blob-store/blob-store.module.js +25 -0
- package/dist/modules/blob-store/blob-store.service.d.ts +66 -0
- package/dist/modules/blob-store/blob-store.service.d.ts.map +1 -0
- package/dist/modules/blob-store/blob-store.service.js +108 -0
- package/dist/modules/blob-store/index.d.ts +3 -0
- package/dist/modules/blob-store/index.d.ts.map +1 -0
- package/dist/modules/blob-store/index.js +2 -0
- package/dist/modules/health/health.controller.d.ts +18 -0
- package/dist/modules/health/health.controller.d.ts.map +1 -0
- package/dist/modules/health/health.controller.js +48 -0
- package/dist/modules/health/health.module.d.ts +8 -0
- package/dist/modules/health/health.module.d.ts.map +1 -0
- package/dist/modules/health/health.module.js +21 -0
- package/dist/modules/index.d.ts +8 -0
- package/dist/modules/index.d.ts.map +1 -0
- package/dist/modules/index.js +6 -0
- package/dist/modules/messages/__test-fixtures__/deps.d.ts +41 -0
- package/dist/modules/messages/__test-fixtures__/deps.d.ts.map +1 -0
- package/dist/modules/messages/__test-fixtures__/deps.js +73 -0
- package/dist/modules/messages/__test-fixtures__/fake-agent.d.ts +27 -0
- package/dist/modules/messages/__test-fixtures__/fake-agent.d.ts.map +1 -0
- package/dist/modules/messages/__test-fixtures__/fake-agent.js +41 -0
- package/dist/modules/messages/__test-fixtures__/fake-response.d.ts +29 -0
- package/dist/modules/messages/__test-fixtures__/fake-response.d.ts.map +1 -0
- package/dist/modules/messages/__test-fixtures__/fake-response.js +55 -0
- package/dist/modules/messages/agent-builder.d.ts +64 -0
- package/dist/modules/messages/agent-builder.d.ts.map +1 -0
- package/dist/modules/messages/agent-builder.js +219 -0
- package/dist/modules/messages/batch-invoker.d.ts +36 -0
- package/dist/modules/messages/batch-invoker.d.ts.map +1 -0
- package/dist/modules/messages/batch-invoker.js +58 -0
- package/dist/modules/messages/dto/list-messages.dto.d.ts +4 -0
- package/dist/modules/messages/dto/list-messages.dto.d.ts.map +1 -0
- package/dist/modules/messages/dto/list-messages.dto.js +17 -0
- package/dist/modules/messages/dto/send-message.dto.d.ts +73 -0
- package/dist/modules/messages/dto/send-message.dto.d.ts.map +1 -0
- package/dist/modules/messages/dto/send-message.dto.js +318 -0
- package/dist/modules/messages/file-processing-credit-sink.port.d.ts +20 -0
- package/dist/modules/messages/file-processing-credit-sink.port.d.ts.map +1 -0
- package/dist/modules/messages/file-processing-credit-sink.port.js +1 -0
- package/dist/modules/messages/file-processing.service.d.ts +195 -0
- package/dist/modules/messages/file-processing.service.d.ts.map +1 -0
- package/dist/modules/messages/file-processing.service.js +1278 -0
- package/dist/modules/messages/homeserver-cache.d.ts +11 -0
- package/dist/modules/messages/homeserver-cache.d.ts.map +1 -0
- package/dist/modules/messages/homeserver-cache.js +31 -0
- package/dist/modules/messages/matrix-listener-bridge.d.ts +63 -0
- package/dist/modules/messages/matrix-listener-bridge.d.ts.map +1 -0
- package/dist/modules/messages/matrix-listener-bridge.js +280 -0
- package/dist/modules/messages/messages.controller.d.ts +13 -0
- package/dist/modules/messages/messages.controller.d.ts.map +1 -0
- package/dist/modules/messages/messages.controller.js +95 -0
- package/dist/modules/messages/messages.module.d.ts +3 -0
- package/dist/modules/messages/messages.module.d.ts.map +1 -0
- package/dist/modules/messages/messages.module.js +75 -0
- package/dist/modules/messages/messages.service.d.ts +112 -0
- package/dist/modules/messages/messages.service.d.ts.map +1 -0
- package/dist/modules/messages/messages.service.js +279 -0
- package/dist/modules/messages/oracle-runtime-bundle.d.ts +38 -0
- package/dist/modules/messages/oracle-runtime-bundle.d.ts.map +1 -0
- package/dist/modules/messages/oracle-runtime-bundle.js +40 -0
- package/dist/modules/messages/post-message-syncer.d.ts +30 -0
- package/dist/modules/messages/post-message-syncer.d.ts.map +1 -0
- package/dist/modules/messages/post-message-syncer.js +73 -0
- package/dist/modules/messages/request-preparer.d.ts +53 -0
- package/dist/modules/messages/request-preparer.d.ts.map +1 -0
- package/dist/modules/messages/request-preparer.js +139 -0
- package/dist/modules/messages/sse-stream-runner.d.ts +73 -0
- package/dist/modules/messages/sse-stream-runner.d.ts.map +1 -0
- package/dist/modules/messages/sse-stream-runner.js +352 -0
- package/dist/modules/messages/sse.utils.d.ts +29 -0
- package/dist/modules/messages/sse.utils.d.ts.map +1 -0
- package/dist/modules/messages/sse.utils.js +77 -0
- package/dist/modules/messages/user-context-fetcher.d.ts +41 -0
- package/dist/modules/messages/user-context-fetcher.d.ts.map +1 -0
- package/dist/modules/messages/user-context-fetcher.js +117 -0
- package/dist/modules/secrets/index.d.ts +2 -0
- package/dist/modules/secrets/index.d.ts.map +1 -0
- package/dist/modules/secrets/index.js +1 -0
- package/dist/modules/secrets/secrets.service.d.ts +29 -0
- package/dist/modules/secrets/secrets.service.d.ts.map +1 -0
- package/dist/modules/secrets/secrets.service.js +107 -0
- package/dist/modules/sessions/dto/create-session.dto.d.ts +6 -0
- package/dist/modules/sessions/dto/create-session.dto.d.ts.map +1 -0
- package/dist/modules/sessions/dto/create-session.dto.js +5 -0
- package/dist/modules/sessions/dto/delete-session.dto.d.ts +6 -0
- package/dist/modules/sessions/dto/delete-session.dto.d.ts.map +1 -0
- package/dist/modules/sessions/dto/delete-session.dto.js +5 -0
- package/dist/modules/sessions/dto/list-sessions.dto.d.ts +7 -0
- package/dist/modules/sessions/dto/list-sessions.dto.d.ts.map +1 -0
- package/dist/modules/sessions/dto/list-sessions.dto.js +6 -0
- package/dist/modules/sessions/session-history-processor.service.d.ts +48 -0
- package/dist/modules/sessions/session-history-processor.service.d.ts.map +1 -0
- package/dist/modules/sessions/session-history-processor.service.js +254 -0
- package/dist/modules/sessions/sessions.controller.d.ts +13 -0
- package/dist/modules/sessions/sessions.controller.d.ts.map +1 -0
- package/dist/modules/sessions/sessions.controller.js +113 -0
- package/dist/modules/sessions/sessions.module.d.ts +3 -0
- package/dist/modules/sessions/sessions.module.d.ts.map +1 -0
- package/dist/modules/sessions/sessions.module.js +49 -0
- package/dist/modules/sessions/sessions.service.d.ts +23 -0
- package/dist/modules/sessions/sessions.service.d.ts.map +1 -0
- package/dist/modules/sessions/sessions.service.js +168 -0
- package/dist/modules/subscription/subscription.middleware.d.ts +37 -0
- package/dist/modules/subscription/subscription.middleware.d.ts.map +1 -0
- package/dist/modules/subscription/subscription.middleware.js +141 -0
- package/dist/modules/subscription/subscription.module.d.ts +12 -0
- package/dist/modules/subscription/subscription.module.d.ts.map +1 -0
- package/dist/modules/subscription/subscription.module.js +26 -0
- package/dist/modules/throttler/throttler.module.d.ts +11 -0
- package/dist/modules/throttler/throttler.module.d.ts.map +1 -0
- package/dist/modules/throttler/throttler.module.js +32 -0
- package/dist/modules/ucan/index.d.ts +7 -0
- package/dist/modules/ucan/index.d.ts.map +1 -0
- package/dist/modules/ucan/index.js +6 -0
- package/dist/modules/ucan/ucan.config.d.ts +87 -0
- package/dist/modules/ucan/ucan.config.d.ts.map +1 -0
- package/dist/modules/ucan/ucan.config.js +114 -0
- package/dist/modules/ucan/ucan.module.d.ts +29 -0
- package/dist/modules/ucan/ucan.module.d.ts.map +1 -0
- package/dist/modules/ucan/ucan.module.js +46 -0
- package/dist/modules/ucan/ucan.service.d.ts +129 -0
- package/dist/modules/ucan/ucan.service.d.ts.map +1 -0
- package/dist/modules/ucan/ucan.service.js +582 -0
- package/dist/modules/ws/emitter.d.ts +12 -0
- package/dist/modules/ws/emitter.d.ts.map +1 -0
- package/dist/modules/ws/emitter.js +12 -0
- package/dist/modules/ws/ws.gateway.d.ts +34 -0
- package/dist/modules/ws/ws.gateway.d.ts.map +1 -0
- package/dist/modules/ws/ws.gateway.js +241 -0
- package/dist/modules/ws/ws.module.d.ts +3 -0
- package/dist/modules/ws/ws.module.d.ts.map +1 -0
- package/dist/modules/ws/ws.module.js +21 -0
- package/dist/modules/ws/ws.service.d.ts +25 -0
- package/dist/modules/ws/ws.service.d.ts.map +1 -0
- package/dist/modules/ws/ws.service.js +113 -0
- package/dist/plugin-api/define-plugin.d.ts +19 -0
- package/dist/plugin-api/define-plugin.d.ts.map +1 -0
- package/dist/plugin-api/define-plugin.js +25 -0
- package/dist/plugin-api/oracle-plugin.d.ts +92 -0
- package/dist/plugin-api/oracle-plugin.d.ts.map +1 -0
- package/dist/plugin-api/oracle-plugin.js +15 -0
- package/dist/plugin-api/tool-helper.d.ts +33 -0
- package/dist/plugin-api/tool-helper.d.ts.map +1 -0
- package/dist/plugin-api/tool-helper.js +40 -0
- package/dist/plugin-api/types.d.ts +378 -0
- package/dist/plugin-api/types.d.ts.map +1 -0
- package/dist/plugin-api/types.js +1 -0
- package/dist/plugins/agui/agui-agent.d.ts +10 -0
- package/dist/plugins/agui/agui-agent.d.ts.map +1 -0
- package/dist/plugins/agui/agui-agent.js +110 -0
- package/dist/plugins/agui/agui.plugin.d.ts +16 -0
- package/dist/plugins/agui/agui.plugin.d.ts.map +1 -0
- package/dist/plugins/agui/agui.plugin.js +117 -0
- package/dist/plugins/agui/index.d.ts +3 -0
- package/dist/plugins/agui/index.d.ts.map +1 -0
- package/dist/plugins/agui/index.js +2 -0
- package/dist/plugins/composio/composio-tools.d.ts +59 -0
- package/dist/plugins/composio/composio-tools.d.ts.map +1 -0
- package/dist/plugins/composio/composio-tools.js +88 -0
- package/dist/plugins/composio/composio-ucan.d.ts +14 -0
- package/dist/plugins/composio/composio-ucan.d.ts.map +1 -0
- package/dist/plugins/composio/composio-ucan.js +32 -0
- package/dist/plugins/composio/composio.plugin.d.ts +45 -0
- package/dist/plugins/composio/composio.plugin.d.ts.map +1 -0
- package/dist/plugins/composio/composio.plugin.js +116 -0
- package/dist/plugins/composio/index.d.ts +4 -0
- package/dist/plugins/composio/index.d.ts.map +1 -0
- package/dist/plugins/composio/index.js +3 -0
- package/dist/plugins/credits/claim-processing.module.d.ts +28 -0
- package/dist/plugins/credits/claim-processing.module.d.ts.map +1 -0
- package/dist/plugins/credits/claim-processing.module.js +37 -0
- package/dist/plugins/credits/claim-processing.service.d.ts +92 -0
- package/dist/plugins/credits/claim-processing.service.d.ts.map +1 -0
- package/dist/plugins/credits/claim-processing.service.js +406 -0
- package/dist/plugins/credits/credits-middleware.d.ts +23 -0
- package/dist/plugins/credits/credits-middleware.d.ts.map +1 -0
- package/dist/plugins/credits/credits-middleware.js +154 -0
- package/dist/plugins/credits/credits.plugin.d.ts +55 -0
- package/dist/plugins/credits/credits.plugin.d.ts.map +1 -0
- package/dist/plugins/credits/credits.plugin.js +95 -0
- package/dist/plugins/credits/file-processing-sink.module.d.ts +18 -0
- package/dist/plugins/credits/file-processing-sink.module.d.ts.map +1 -0
- package/dist/plugins/credits/file-processing-sink.module.js +56 -0
- package/dist/plugins/credits/index.d.ts +8 -0
- package/dist/plugins/credits/index.d.ts.map +1 -0
- package/dist/plugins/credits/index.js +7 -0
- package/dist/plugins/credits/subscription-sink.module.d.ts +23 -0
- package/dist/plugins/credits/subscription-sink.module.d.ts.map +1 -0
- package/dist/plugins/credits/subscription-sink.module.js +54 -0
- package/dist/plugins/credits/token-limiter.d.ts +139 -0
- package/dist/plugins/credits/token-limiter.d.ts.map +1 -0
- package/dist/plugins/credits/token-limiter.js +302 -0
- package/dist/plugins/domain-indexer/domain-indexer-agent.d.ts +7 -0
- package/dist/plugins/domain-indexer/domain-indexer-agent.d.ts.map +1 -0
- package/dist/plugins/domain-indexer/domain-indexer-agent.js +48 -0
- package/dist/plugins/domain-indexer/domain-indexer-tools.d.ts +8 -0
- package/dist/plugins/domain-indexer/domain-indexer-tools.d.ts.map +1 -0
- package/dist/plugins/domain-indexer/domain-indexer-tools.js +194 -0
- package/dist/plugins/domain-indexer/domain-indexer.plugin.d.ts +20 -0
- package/dist/plugins/domain-indexer/domain-indexer.plugin.d.ts.map +1 -0
- package/dist/plugins/domain-indexer/domain-indexer.plugin.js +78 -0
- package/dist/plugins/domain-indexer/index.d.ts +2 -0
- package/dist/plugins/domain-indexer/index.d.ts.map +1 -0
- package/dist/plugins/domain-indexer/index.js +1 -0
- package/dist/plugins/editor/apply-sandbox-output.d.ts +33 -0
- package/dist/plugins/editor/apply-sandbox-output.d.ts.map +1 -0
- package/dist/plugins/editor/apply-sandbox-output.js +302 -0
- package/dist/plugins/editor/block-actions.d.ts +84 -0
- package/dist/plugins/editor/block-actions.d.ts.map +1 -0
- package/dist/plugins/editor/block-actions.js +471 -0
- package/dist/plugins/editor/blocknote-helper.d.ts +147 -0
- package/dist/plugins/editor/blocknote-helper.d.ts.map +1 -0
- package/dist/plugins/editor/blocknote-helper.js +542 -0
- package/dist/plugins/editor/blocknote-tools.d.ts +364 -0
- package/dist/plugins/editor/blocknote-tools.d.ts.map +1 -0
- package/dist/plugins/editor/blocknote-tools.js +2123 -0
- package/dist/plugins/editor/editor-agent.d.ts +47 -0
- package/dist/plugins/editor/editor-agent.d.ts.map +1 -0
- package/dist/plugins/editor/editor-agent.js +158 -0
- package/dist/plugins/editor/editor-mx.d.ts +56 -0
- package/dist/plugins/editor/editor-mx.d.ts.map +1 -0
- package/dist/plugins/editor/editor-mx.js +100 -0
- package/dist/plugins/editor/editor.plugin.d.ts +32 -0
- package/dist/plugins/editor/editor.plugin.d.ts.map +1 -0
- package/dist/plugins/editor/editor.plugin.js +189 -0
- package/dist/plugins/editor/index.d.ts +6 -0
- package/dist/plugins/editor/index.d.ts.map +1 -0
- package/dist/plugins/editor/index.js +5 -0
- package/dist/plugins/editor/mint-invocation-tool.d.ts +72 -0
- package/dist/plugins/editor/mint-invocation-tool.d.ts.map +1 -0
- package/dist/plugins/editor/mint-invocation-tool.js +173 -0
- package/dist/plugins/editor/page-functions.d.ts +100 -0
- package/dist/plugins/editor/page-functions.d.ts.map +1 -0
- package/dist/plugins/editor/page-functions.js +317 -0
- package/dist/plugins/editor/page-tools.d.ts +75 -0
- package/dist/plugins/editor/page-tools.d.ts.map +1 -0
- package/dist/plugins/editor/page-tools.js +238 -0
- package/dist/plugins/editor/prompts.d.ts +22 -0
- package/dist/plugins/editor/prompts.d.ts.map +1 -0
- package/dist/plugins/editor/prompts.js +451 -0
- package/dist/plugins/editor/provider.d.ts +101 -0
- package/dist/plugins/editor/provider.d.ts.map +1 -0
- package/dist/plugins/editor/provider.js +249 -0
- package/dist/plugins/editor/standalone-editor-tool.d.ts +17 -0
- package/dist/plugins/editor/standalone-editor-tool.d.ts.map +1 -0
- package/dist/plugins/editor/standalone-editor-tool.js +136 -0
- package/dist/plugins/editor/survey-helpers.d.ts +112 -0
- package/dist/plugins/editor/survey-helpers.d.ts.map +1 -0
- package/dist/plugins/editor/survey-helpers.js +358 -0
- package/dist/plugins/firecrawl/firecrawl-agent.d.ts +7 -0
- package/dist/plugins/firecrawl/firecrawl-agent.d.ts.map +1 -0
- package/dist/plugins/firecrawl/firecrawl-agent.js +84 -0
- package/dist/plugins/firecrawl/firecrawl-tools.d.ts +37 -0
- package/dist/plugins/firecrawl/firecrawl-tools.d.ts.map +1 -0
- package/dist/plugins/firecrawl/firecrawl-tools.js +129 -0
- package/dist/plugins/firecrawl/firecrawl.plugin.d.ts +32 -0
- package/dist/plugins/firecrawl/firecrawl.plugin.d.ts.map +1 -0
- package/dist/plugins/firecrawl/firecrawl.plugin.js +69 -0
- package/dist/plugins/firecrawl/index.d.ts +3 -0
- package/dist/plugins/firecrawl/index.d.ts.map +1 -0
- package/dist/plugins/firecrawl/index.js +2 -0
- package/dist/plugins/index.d.ts +47 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +69 -0
- package/dist/plugins/matrix-group-chats/channel-memory.module.d.ts +7 -0
- package/dist/plugins/matrix-group-chats/channel-memory.module.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/channel-memory.module.js +22 -0
- package/dist/plugins/matrix-group-chats/channel-memory.repo.d.ts +62 -0
- package/dist/plugins/matrix-group-chats/channel-memory.repo.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/channel-memory.repo.js +311 -0
- package/dist/plugins/matrix-group-chats/channel-memory.service.d.ts +89 -0
- package/dist/plugins/matrix-group-chats/channel-memory.service.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/channel-memory.service.js +565 -0
- package/dist/plugins/matrix-group-chats/channel-memory.summarizer.d.ts +18 -0
- package/dist/plugins/matrix-group-chats/channel-memory.summarizer.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/channel-memory.summarizer.js +128 -0
- package/dist/plugins/matrix-group-chats/channel-memory.types.d.ts +50 -0
- package/dist/plugins/matrix-group-chats/channel-memory.types.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/channel-memory.types.js +2 -0
- package/dist/plugins/matrix-group-chats/guard.d.ts +83 -0
- package/dist/plugins/matrix-group-chats/guard.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/guard.js +138 -0
- package/dist/plugins/matrix-group-chats/index.d.ts +59 -0
- package/dist/plugins/matrix-group-chats/index.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/index.js +140 -0
- package/dist/plugins/matrix-group-chats/middleware.d.ts +31 -0
- package/dist/plugins/matrix-group-chats/middleware.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/middleware.js +186 -0
- package/dist/plugins/matrix-group-chats/power-levels.d.ts +15 -0
- package/dist/plugins/matrix-group-chats/power-levels.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/power-levels.js +36 -0
- package/dist/plugins/matrix-group-chats/room-info.d.ts +23 -0
- package/dist/plugins/matrix-group-chats/room-info.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/room-info.js +34 -0
- package/dist/plugins/matrix-group-chats/tools.d.ts +8 -0
- package/dist/plugins/matrix-group-chats/tools.d.ts.map +1 -0
- package/dist/plugins/matrix-group-chats/tools.js +154 -0
- package/dist/plugins/memory/index.d.ts +5 -0
- package/dist/plugins/memory/index.d.ts.map +1 -0
- package/dist/plugins/memory/index.js +3 -0
- package/dist/plugins/memory/memory-tools.d.ts +52 -0
- package/dist/plugins/memory/memory-tools.d.ts.map +1 -0
- package/dist/plugins/memory/memory-tools.js +88 -0
- package/dist/plugins/memory/memory-ucan.d.ts +17 -0
- package/dist/plugins/memory/memory-ucan.d.ts.map +1 -0
- package/dist/plugins/memory/memory-ucan.js +43 -0
- package/dist/plugins/memory/memory.plugin.d.ts +51 -0
- package/dist/plugins/memory/memory.plugin.d.ts.map +1 -0
- package/dist/plugins/memory/memory.plugin.js +76 -0
- package/dist/plugins/memory/types.d.ts +75 -0
- package/dist/plugins/memory/types.d.ts.map +1 -0
- package/dist/plugins/memory/types.js +2 -0
- package/dist/plugins/portal/index.d.ts +3 -0
- package/dist/plugins/portal/index.d.ts.map +1 -0
- package/dist/plugins/portal/index.js +2 -0
- package/dist/plugins/portal/portal-agent.d.ts +9 -0
- package/dist/plugins/portal/portal-agent.d.ts.map +1 -0
- package/dist/plugins/portal/portal-agent.js +70 -0
- package/dist/plugins/portal/portal.plugin.d.ts +16 -0
- package/dist/plugins/portal/portal.plugin.d.ts.map +1 -0
- package/dist/plugins/portal/portal.plugin.js +115 -0
- package/dist/plugins/sandbox/index.d.ts +3 -0
- package/dist/plugins/sandbox/index.d.ts.map +1 -0
- package/dist/plugins/sandbox/index.js +2 -0
- package/dist/plugins/sandbox/sandbox-mcp.d.ts +46 -0
- package/dist/plugins/sandbox/sandbox-mcp.d.ts.map +1 -0
- package/dist/plugins/sandbox/sandbox-mcp.js +80 -0
- package/dist/plugins/sandbox/sandbox-write-blob.d.ts +22 -0
- package/dist/plugins/sandbox/sandbox-write-blob.d.ts.map +1 -0
- package/dist/plugins/sandbox/sandbox-write-blob.js +80 -0
- package/dist/plugins/sandbox/sandbox.plugin.d.ts +80 -0
- package/dist/plugins/sandbox/sandbox.plugin.d.ts.map +1 -0
- package/dist/plugins/sandbox/sandbox.plugin.js +204 -0
- package/dist/plugins/skills/index.d.ts +3 -0
- package/dist/plugins/skills/index.d.ts.map +1 -0
- package/dist/plugins/skills/index.js +2 -0
- package/dist/plugins/skills/skills-tools.d.ts +20 -0
- package/dist/plugins/skills/skills-tools.d.ts.map +1 -0
- package/dist/plugins/skills/skills-tools.js +204 -0
- package/dist/plugins/skills/skills-ucan.d.ts +24 -0
- package/dist/plugins/skills/skills-ucan.d.ts.map +1 -0
- package/dist/plugins/skills/skills-ucan.js +28 -0
- package/dist/plugins/skills/skills.plugin.d.ts +37 -0
- package/dist/plugins/skills/skills.plugin.d.ts.map +1 -0
- package/dist/plugins/skills/skills.plugin.js +82 -0
- package/dist/plugins/slack/index.d.ts +4 -0
- package/dist/plugins/slack/index.d.ts.map +1 -0
- package/dist/plugins/slack/index.js +3 -0
- package/dist/plugins/slack/slack.module.d.ts +9 -0
- package/dist/plugins/slack/slack.module.d.ts.map +1 -0
- package/dist/plugins/slack/slack.module.js +27 -0
- package/dist/plugins/slack/slack.plugin.d.ts +30 -0
- package/dist/plugins/slack/slack.plugin.d.ts.map +1 -0
- package/dist/plugins/slack/slack.plugin.js +40 -0
- package/dist/plugins/slack/slack.service.d.ts +32 -0
- package/dist/plugins/slack/slack.service.d.ts.map +1 -0
- package/dist/plugins/slack/slack.service.js +157 -0
- package/dist/plugins/user-preferences/index.d.ts +4 -0
- package/dist/plugins/user-preferences/index.d.ts.map +1 -0
- package/dist/plugins/user-preferences/index.js +3 -0
- package/dist/plugins/user-preferences/service/user-preferences.service.d.ts +61 -0
- package/dist/plugins/user-preferences/service/user-preferences.service.d.ts.map +1 -0
- package/dist/plugins/user-preferences/service/user-preferences.service.js +105 -0
- package/dist/plugins/user-preferences/user-preferences-http.module.d.ts +10 -0
- package/dist/plugins/user-preferences/user-preferences-http.module.d.ts.map +1 -0
- package/dist/plugins/user-preferences/user-preferences-http.module.js +23 -0
- package/dist/plugins/user-preferences/user-preferences-tool.d.ts +22 -0
- package/dist/plugins/user-preferences/user-preferences-tool.d.ts.map +1 -0
- package/dist/plugins/user-preferences/user-preferences-tool.js +103 -0
- package/dist/plugins/user-preferences/user-preferences.controller.d.ts +18 -0
- package/dist/plugins/user-preferences/user-preferences.controller.d.ts.map +1 -0
- package/dist/plugins/user-preferences/user-preferences.controller.js +72 -0
- package/dist/plugins/user-preferences/user-preferences.plugin.d.ts +27 -0
- package/dist/plugins/user-preferences/user-preferences.plugin.d.ts.map +1 -0
- package/dist/plugins/user-preferences/user-preferences.plugin.js +66 -0
- package/dist/registries/config-schema-registry.d.ts +24 -0
- package/dist/registries/config-schema-registry.d.ts.map +1 -0
- package/dist/registries/config-schema-registry.js +27 -0
- package/dist/registries/index.d.ts +13 -0
- package/dist/registries/index.d.ts.map +1 -0
- package/dist/registries/index.js +6 -0
- package/dist/registries/manifest-registry.d.ts +49 -0
- package/dist/registries/manifest-registry.d.ts.map +1 -0
- package/dist/registries/manifest-registry.js +53 -0
- package/dist/registries/middleware-registry.d.ts +41 -0
- package/dist/registries/middleware-registry.d.ts.map +1 -0
- package/dist/registries/middleware-registry.js +52 -0
- package/dist/registries/shared-state-registry.d.ts +41 -0
- package/dist/registries/shared-state-registry.d.ts.map +1 -0
- package/dist/registries/shared-state-registry.js +65 -0
- package/dist/registries/subagent-registry.d.ts +55 -0
- package/dist/registries/subagent-registry.d.ts.map +1 -0
- package/dist/registries/subagent-registry.js +106 -0
- package/dist/registries/test-fixtures.d.ts +47 -0
- package/dist/registries/test-fixtures.d.ts.map +1 -0
- package/dist/registries/test-fixtures.js +168 -0
- package/dist/registries/tool-registry.d.ts +74 -0
- package/dist/registries/tool-registry.d.ts.map +1 -0
- package/dist/registries/tool-registry.js +130 -0
- package/dist/runtime-context/ambient.d.ts +118 -0
- package/dist/runtime-context/ambient.d.ts.map +1 -0
- package/dist/runtime-context/ambient.js +1 -0
- package/dist/runtime-context/build-plugin.d.ts +20 -0
- package/dist/runtime-context/build-plugin.d.ts.map +1 -0
- package/dist/runtime-context/build-plugin.js +16 -0
- package/dist/runtime-context/build-runtime.d.ts +60 -0
- package/dist/runtime-context/build-runtime.d.ts.map +1 -0
- package/dist/runtime-context/build-runtime.js +81 -0
- package/dist/testing/create-test-runtime.d.ts +95 -0
- package/dist/testing/create-test-runtime.d.ts.map +1 -0
- package/dist/testing/create-test-runtime.js +302 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +5 -0
- package/dist/testing/integration/chat-client.d.ts +143 -0
- package/dist/testing/integration/chat-client.d.ts.map +1 -0
- package/dist/testing/integration/chat-client.js +238 -0
- package/dist/testing/integration/harness.d.ts +189 -0
- package/dist/testing/integration/harness.d.ts.map +1 -0
- package/dist/testing/integration/harness.js +461 -0
- package/dist/testing/integration/index.d.ts +14 -0
- package/dist/testing/integration/index.d.ts.map +1 -0
- package/dist/testing/integration/index.js +18 -0
- package/dist/testing/integration/setup.d.ts +2 -0
- package/dist/testing/integration/setup.d.ts.map +1 -0
- package/dist/testing/integration/setup.js +41 -0
- package/dist/testing/integration/sse-parser.d.ts +99 -0
- package/dist/testing/integration/sse-parser.d.ts.map +1 -0
- package/dist/testing/integration/sse-parser.js +125 -0
- package/dist/testing/integration/ucan.d.ts +74 -0
- package/dist/testing/integration/ucan.d.ts.map +1 -0
- package/dist/testing/integration/ucan.js +95 -0
- package/dist/testing/integration/wait-for-matrix-loaded.d.ts +19 -0
- package/dist/testing/integration/wait-for-matrix-loaded.d.ts.map +1 -0
- package/dist/testing/integration/wait-for-matrix-loaded.js +31 -0
- package/dist/testing/mocks.d.ts +64 -0
- package/dist/testing/mocks.d.ts.map +1 -0
- package/dist/testing/mocks.js +141 -0
- package/dist/testing/nest-doubles.d.ts +10 -0
- package/dist/testing/nest-doubles.d.ts.map +1 -0
- package/dist/testing/nest-doubles.js +19 -0
- package/dist/utils/emoji.d.ts +3 -0
- package/dist/utils/emoji.d.ts.map +1 -0
- package/dist/utils/emoji.js +36 -0
- package/package.json +102 -0
|
@@ -0,0 +1,2123 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
/**
|
|
3
|
+
* LangChain Tools for BlockNote Y.js Editing
|
|
4
|
+
*/
|
|
5
|
+
import { tool } from '@langchain/core/tools';
|
|
6
|
+
import * as z from 'zod';
|
|
7
|
+
import { Logger } from '@nestjs/common';
|
|
8
|
+
import { randomUUID } from 'node:crypto';
|
|
9
|
+
import * as Y from 'yjs';
|
|
10
|
+
import { appendBlock, collectAllBlocks, deleteBlock, editBlock, evaluateBlockConditions, extractBlockProperties, findBlockById, getBlockDetail, readAuditTrailForBlock, readDelegations, readFlowMetadata, readFlowNodes, readInvocations, readRuntimeState, resolveBlockReferences, simplifyBlockForAgent, updateRuntimeState, } from './blocknote-helper.js';
|
|
11
|
+
import { emojify, unemojify } from '../../utils/emoji.js';
|
|
12
|
+
import { findAndReplaceInDoc, insertBlock, moveBlock, } from './block-actions.js';
|
|
13
|
+
import { MatrixProviderManager } from './provider.js';
|
|
14
|
+
import { extractSurveyQuestions, getMissingRequiredFields, getVisibleQuestions, validateAnswersAgainstSchema, } from './survey-helpers.js';
|
|
15
|
+
import { getAction, getAllActions, buildFlowNodeFromBlock, executeNode, } from '@ixo/editor/core';
|
|
16
|
+
/**
|
|
17
|
+
* Build the default `BLOCKNOTE_TOOLS_CONFIG` from a runtime config bag. The
|
|
18
|
+
* editor plugin builds this once at boot from its declared env vars and
|
|
19
|
+
* threads it through every tool factory, replacing the legacy module-level
|
|
20
|
+
* `getConfig()` call.
|
|
21
|
+
*/
|
|
22
|
+
export function buildBlocknoteToolsConfig(matrix) {
|
|
23
|
+
return {
|
|
24
|
+
matrix: {
|
|
25
|
+
baseUrl: matrix.baseUrl,
|
|
26
|
+
accessToken: matrix.accessToken,
|
|
27
|
+
userId: matrix.userId,
|
|
28
|
+
initialSyncTimeoutMs: 30_000,
|
|
29
|
+
},
|
|
30
|
+
provider: {
|
|
31
|
+
docName: 'document',
|
|
32
|
+
enableAwareness: false,
|
|
33
|
+
retryAttempts: 3,
|
|
34
|
+
retryDelayMs: 5_000,
|
|
35
|
+
},
|
|
36
|
+
blocknote: {
|
|
37
|
+
defaultBlockId: undefined,
|
|
38
|
+
blockNamespace: undefined,
|
|
39
|
+
mutableAttributeKeys: [],
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Track active provider managers for cleanup
|
|
45
|
+
*/
|
|
46
|
+
const logger = new Logger('BlocknoteTools');
|
|
47
|
+
// ── Emoji Helpers ─────────────────────────────────────────────────────
|
|
48
|
+
/**
|
|
49
|
+
* Checks whether a string contains emoji shortcodes (e.g. `:tada:`) or
|
|
50
|
+
* actual emoji characters. Returns true if either form is present.
|
|
51
|
+
*/
|
|
52
|
+
function textContainsEmoji(text) {
|
|
53
|
+
return /:[a-z0-9_+-]+:/i.test(text) || text !== unemojify(text);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Performs a case-insensitive substring match that handles emoji/shortcode
|
|
57
|
+
* equivalence. The search term and the target text are compared in BOTH
|
|
58
|
+
* their emojified (🎉) and unemojified (:tada:) forms so that the agent
|
|
59
|
+
* can search using either representation.
|
|
60
|
+
*/
|
|
61
|
+
function emojiAwareIncludes(text, search) {
|
|
62
|
+
const textLower = text.toLowerCase();
|
|
63
|
+
const searchLower = search.toLowerCase();
|
|
64
|
+
// Fast path — direct match
|
|
65
|
+
if (textLower.includes(searchLower))
|
|
66
|
+
return true;
|
|
67
|
+
// Normalise both sides to emoji characters and compare
|
|
68
|
+
const textEmoji = emojify(textLower);
|
|
69
|
+
const searchEmoji = emojify(searchLower);
|
|
70
|
+
if (textEmoji.includes(searchEmoji))
|
|
71
|
+
return true;
|
|
72
|
+
// Normalise both sides to shortcodes and compare
|
|
73
|
+
const textCodes = unemojify(textLower);
|
|
74
|
+
const searchCodes = unemojify(searchLower);
|
|
75
|
+
if (textCodes.includes(searchCodes))
|
|
76
|
+
return true;
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
// ── DID Helpers ───────────────────────────────────────────────────────
|
|
80
|
+
/**
|
|
81
|
+
* Extracts a valid DID from a Matrix user ID.
|
|
82
|
+
* Matrix format: @did-ixo-ixo1abc123def:mx.server.com
|
|
83
|
+
* Result: did:ixo:ixo1abc123def
|
|
84
|
+
*
|
|
85
|
+
* The localpart encodes the DID with hyphens instead of colons.
|
|
86
|
+
*/
|
|
87
|
+
function matrixUserIdToDid(matrixUserId) {
|
|
88
|
+
// Strip leading @ and remove homeserver (:server.com)
|
|
89
|
+
const localpart = matrixUserId.replace(/^@/, '').replace(/:.*$/, '');
|
|
90
|
+
// Convert hyphens back to colons: did-ixo-ixo1abc → did:ixo:ixo1abc
|
|
91
|
+
// Only replace the first two hyphens (did-method-identifier)
|
|
92
|
+
const parts = localpart.split('-');
|
|
93
|
+
if (parts.length >= 3 && parts[0] === 'did') {
|
|
94
|
+
return `${parts[0]}:${parts[1]}:${parts.slice(2).join('-')}`;
|
|
95
|
+
}
|
|
96
|
+
// Fallback: return as-is if it doesn't match expected pattern
|
|
97
|
+
return localpart;
|
|
98
|
+
}
|
|
99
|
+
// ── Flow Engine Helpers ───────────────────────────────────────────────
|
|
100
|
+
/**
|
|
101
|
+
* Creates a FlowRuntimeStateManager backed by a Y.Doc's 'runtime' map.
|
|
102
|
+
* Mirrors the pattern from the editor's runtime.ts.
|
|
103
|
+
* Wraps mutations in doc.transact for reliable CRDT sync.
|
|
104
|
+
*/
|
|
105
|
+
function createYDocRuntimeManager(doc) {
|
|
106
|
+
const map = doc.getMap('runtime');
|
|
107
|
+
return {
|
|
108
|
+
get: (nodeId) => {
|
|
109
|
+
const stored = map.get(nodeId);
|
|
110
|
+
if (!stored || typeof stored !== 'object')
|
|
111
|
+
return {};
|
|
112
|
+
return { ...stored };
|
|
113
|
+
},
|
|
114
|
+
update: (nodeId, updates) => {
|
|
115
|
+
doc.transact(() => {
|
|
116
|
+
const current = map.get(nodeId);
|
|
117
|
+
const existing = current && typeof current === 'object'
|
|
118
|
+
? { ...current }
|
|
119
|
+
: {};
|
|
120
|
+
map.set(nodeId, { ...existing, ...updates });
|
|
121
|
+
}, 'oracle-runtime-update');
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Oracle-side ActionServices — MVP supports HTTP only.
|
|
127
|
+
* Additional services (email, notify) can be wired up when the oracle has those capabilities.
|
|
128
|
+
*/
|
|
129
|
+
const oracleActionServices = {
|
|
130
|
+
http: {
|
|
131
|
+
request: async (params) => {
|
|
132
|
+
const { url, method, headers = {}, body } = params;
|
|
133
|
+
const res = await fetch(url, {
|
|
134
|
+
method,
|
|
135
|
+
headers: { 'Content-Type': 'application/json', ...headers },
|
|
136
|
+
...(body !== undefined && { body: JSON.stringify(body) }),
|
|
137
|
+
});
|
|
138
|
+
const data = await res.text();
|
|
139
|
+
let parsed;
|
|
140
|
+
try {
|
|
141
|
+
parsed = JSON.parse(data);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
parsed = data;
|
|
145
|
+
}
|
|
146
|
+
const responseHeaders = {};
|
|
147
|
+
res.headers.forEach((v, k) => {
|
|
148
|
+
responseHeaders[k] = v;
|
|
149
|
+
});
|
|
150
|
+
return { status: res.status, headers: responseHeaders, data: parsed };
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Creates BlockNote tools that use a shared Matrix client
|
|
156
|
+
*
|
|
157
|
+
* @param matrixClient - The singleton Matrix client (already synced)
|
|
158
|
+
* @param config - Configuration for the provider and room
|
|
159
|
+
* @param readOnly - If true, only returns read-only tools (list_blocks). Write tools are disabled but code is preserved.
|
|
160
|
+
*/
|
|
161
|
+
export const createBlocknoteTools = async (matrixClient, config, readOnly = false) => {
|
|
162
|
+
logger.log(`🔧 Creating BlockNote tools with Matrix client: ${matrixClient.getUserId()}`);
|
|
163
|
+
logger.log(`🔧 Target room: ${JSON.stringify(config.matrix.room)}`);
|
|
164
|
+
let roomId;
|
|
165
|
+
if (config.matrix.room.type === 'id') {
|
|
166
|
+
roomId = config.matrix.room.value;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
// Resolve room alias to room ID
|
|
170
|
+
const ret = await matrixClient.getRoomIdForAlias(config.matrix.room.value);
|
|
171
|
+
roomId = ret.room_id;
|
|
172
|
+
}
|
|
173
|
+
// ============================================================================
|
|
174
|
+
// Tool 1: List Blocks
|
|
175
|
+
// ============================================================================
|
|
176
|
+
/**
|
|
177
|
+
* Lists all blocks in the BlockNote document
|
|
178
|
+
*
|
|
179
|
+
* Returns detailed information about each block including:
|
|
180
|
+
* - Block ID
|
|
181
|
+
* - Block type (paragraph, proposal, checkbox, etc.)
|
|
182
|
+
* - All attributes/properties
|
|
183
|
+
* - Text content
|
|
184
|
+
* - Nested structure
|
|
185
|
+
*/
|
|
186
|
+
const listBlocksTool = tool(async ({ includeText = true, blockType = null, start = 0, end = 10 }) => {
|
|
187
|
+
logger.log('📋 list_blocks tool invoked');
|
|
188
|
+
// Use the shared Matrix client (already synced)
|
|
189
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
190
|
+
try {
|
|
191
|
+
const { doc } = await providerManager.init();
|
|
192
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
193
|
+
if (!isInRoom) {
|
|
194
|
+
return JSON.stringify({
|
|
195
|
+
success: false,
|
|
196
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
// Get the document fragment
|
|
200
|
+
const fragment = doc.getXmlFragment('document');
|
|
201
|
+
// Collect all blocks using the working CLI logic
|
|
202
|
+
const blocks = collectAllBlocks(fragment, includeText);
|
|
203
|
+
// Filter by block type if specified
|
|
204
|
+
const filteredBlocks = blockType
|
|
205
|
+
? blocks.filter((b) => b.blockType === blockType)
|
|
206
|
+
: blocks;
|
|
207
|
+
// Add position index for agent awareness of document order
|
|
208
|
+
const indexedBlocks = filteredBlocks.map((b, i) => ({
|
|
209
|
+
position: i,
|
|
210
|
+
...b,
|
|
211
|
+
}));
|
|
212
|
+
// Apply pagination slice
|
|
213
|
+
const paginatedBlocks = indexedBlocks.slice(start, end);
|
|
214
|
+
return JSON.stringify({
|
|
215
|
+
success: true,
|
|
216
|
+
roomId,
|
|
217
|
+
total: indexedBlocks.length,
|
|
218
|
+
count: paginatedBlocks.length,
|
|
219
|
+
start,
|
|
220
|
+
end,
|
|
221
|
+
blocks: paginatedBlocks,
|
|
222
|
+
}, null, 2);
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
return JSON.stringify({
|
|
226
|
+
success: false,
|
|
227
|
+
error: error instanceof Error ? error.message : String(error),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
finally {
|
|
231
|
+
// Schedule cleanup after delay to allow Y.Doc to finish syncing
|
|
232
|
+
await providerManager.dispose();
|
|
233
|
+
}
|
|
234
|
+
}, {
|
|
235
|
+
name: 'list_blocks',
|
|
236
|
+
description: `Lists all blocks in the BlockNote document with their complete structure and UUIDs.
|
|
237
|
+
|
|
238
|
+
**⚠️ CRITICAL: Always call this tool FIRST before any edit operation to get valid UUID block IDs.**
|
|
239
|
+
|
|
240
|
+
Use this tool to:
|
|
241
|
+
- Get exact UUIDs needed for editing (UUIDs are like: 550e8400-e29b-41d4-a716-446655440000)
|
|
242
|
+
- View all blocks in the document
|
|
243
|
+
- Filter blocks by type
|
|
244
|
+
- Check current block properties and state
|
|
245
|
+
- Understand document structure
|
|
246
|
+
|
|
247
|
+
**Parameter Examples:**
|
|
248
|
+
|
|
249
|
+
List all blocks with text:
|
|
250
|
+
\`\`\`json
|
|
251
|
+
{"includeText": true}
|
|
252
|
+
\`\`\`
|
|
253
|
+
|
|
254
|
+
List only proposal blocks:
|
|
255
|
+
\`\`\`json
|
|
256
|
+
{"includeText": true, "blockType": "proposal"}
|
|
257
|
+
\`\`\`
|
|
258
|
+
|
|
259
|
+
List blocks without text content (faster):
|
|
260
|
+
\`\`\`json
|
|
261
|
+
{"includeText": false}
|
|
262
|
+
\`\`\`
|
|
263
|
+
|
|
264
|
+
List all blocks (override default pagination):
|
|
265
|
+
\`\`\`json
|
|
266
|
+
{"includeText": true, "start": 0, "end": 9999}
|
|
267
|
+
\`\`\`
|
|
268
|
+
|
|
269
|
+
List blocks 10-20:
|
|
270
|
+
\`\`\`json
|
|
271
|
+
{"start": 10, "end": 20}
|
|
272
|
+
\`\`\`
|
|
273
|
+
|
|
274
|
+
**Note:** By default, only the first 10 blocks are returned. Use start/end to paginate through larger documents. Check "total" in the response to see how many blocks exist.
|
|
275
|
+
|
|
276
|
+
**Returns clean JSON like:**
|
|
277
|
+
\`\`\`json
|
|
278
|
+
{
|
|
279
|
+
"success": true,
|
|
280
|
+
"total": 25,
|
|
281
|
+
"count": 3,
|
|
282
|
+
"blocks": [
|
|
283
|
+
{
|
|
284
|
+
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
285
|
+
"type": "proposal",
|
|
286
|
+
"properties": {
|
|
287
|
+
"status": "draft",
|
|
288
|
+
"title": "My Proposal",
|
|
289
|
+
"description": "Proposal details",
|
|
290
|
+
"icon": "square-check"
|
|
291
|
+
},
|
|
292
|
+
"text": "Optional text content"
|
|
293
|
+
}
|
|
294
|
+
]
|
|
295
|
+
}
|
|
296
|
+
\`\`\`
|
|
297
|
+
|
|
298
|
+
**Block types available:**
|
|
299
|
+
- paragraph: Simple text
|
|
300
|
+
- proposal: Blockchain proposals (status: draft/open/passed/rejected/executed/closed/execution_failed/veto_timelock)
|
|
301
|
+
- checkbox: Interactive checkboxes
|
|
302
|
+
- apiRequest: API calls (GET/POST/PUT/DELETE)
|
|
303
|
+
- list: Data lists
|
|
304
|
+
- domainCreator: Survey forms with surveySchema and answers (use read_survey, fill_survey_answers, validate_survey_answers tools)
|
|
305
|
+
|
|
306
|
+
**Note for domainCreator blocks:**
|
|
307
|
+
- surveySchema and answers are automatically parsed as structured JSON
|
|
308
|
+
- Use read_survey tool for detailed survey information
|
|
309
|
+
- Use fill_survey_answers to update answers
|
|
310
|
+
- Use validate_survey_answers to check completeness
|
|
311
|
+
|
|
312
|
+
**Important:** Block IDs are UUIDs - never guess them. Always extract exact IDs from this tool's response before calling edit_block.`,
|
|
313
|
+
schema: z.object({
|
|
314
|
+
includeText: z
|
|
315
|
+
.boolean()
|
|
316
|
+
.optional()
|
|
317
|
+
.default(true)
|
|
318
|
+
.describe('Whether to include text content in the response'),
|
|
319
|
+
blockType: z
|
|
320
|
+
.string()
|
|
321
|
+
.optional()
|
|
322
|
+
.nullable()
|
|
323
|
+
.describe('Optional: filter by block type (paragraph, proposal, checkbox, apiRequest, list, etc.)'),
|
|
324
|
+
start: z
|
|
325
|
+
.number()
|
|
326
|
+
.int()
|
|
327
|
+
.optional()
|
|
328
|
+
.default(0)
|
|
329
|
+
.describe('Starting index (0-based) for pagination. Defaults to 0.'),
|
|
330
|
+
end: z
|
|
331
|
+
.number()
|
|
332
|
+
.int()
|
|
333
|
+
.optional()
|
|
334
|
+
.default(10)
|
|
335
|
+
.describe('Ending index (exclusive) for pagination. Defaults to 10.'),
|
|
336
|
+
}),
|
|
337
|
+
});
|
|
338
|
+
// ============================================================================
|
|
339
|
+
// Tool 2: Edit Block
|
|
340
|
+
// ============================================================================
|
|
341
|
+
/**
|
|
342
|
+
* Edits an existing block's properties
|
|
343
|
+
*
|
|
344
|
+
* Uses the production-tested editBlock helper from blockActions.ts
|
|
345
|
+
* which includes:
|
|
346
|
+
* - Dual-storage pattern (attrs.props + direct attributes)
|
|
347
|
+
* - Proper attribute merging
|
|
348
|
+
* - Text update handling
|
|
349
|
+
* - Consistent with CLI edit-block command
|
|
350
|
+
*
|
|
351
|
+
* Can update:
|
|
352
|
+
* - Block attributes (status, title, description, etc.)
|
|
353
|
+
* - Text content
|
|
354
|
+
* - Remove specific attributes
|
|
355
|
+
*
|
|
356
|
+
* Changes are synced to all connected clients via Matrix CRDT
|
|
357
|
+
*/
|
|
358
|
+
const editBlockTool = tool(async ({ blockId, updates, removeAttributes = [], text = null, runtimeUpdates = undefined, }) => {
|
|
359
|
+
Logger.log(`✏️ edit_block tool invoked for block: ${blockId}`);
|
|
360
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
361
|
+
if (!isInRoom) {
|
|
362
|
+
return JSON.stringify({
|
|
363
|
+
success: false,
|
|
364
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
// Use the shared Matrix client (already synced)
|
|
368
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
369
|
+
try {
|
|
370
|
+
const { doc } = await providerManager.init();
|
|
371
|
+
// Guard: reject runtimeUpdates on action blocks in flow mode — use execute_action instead
|
|
372
|
+
if (runtimeUpdates &&
|
|
373
|
+
typeof runtimeUpdates === 'object' &&
|
|
374
|
+
Object.keys(runtimeUpdates).length > 0) {
|
|
375
|
+
const blockDetail = getBlockDetail(doc, blockId, false);
|
|
376
|
+
const blockProps = blockDetail
|
|
377
|
+
? extractBlockProperties(blockDetail)
|
|
378
|
+
: {};
|
|
379
|
+
const hasActionType = blockProps.actionType !== undefined;
|
|
380
|
+
if (hasActionType) {
|
|
381
|
+
const flowMeta = readFlowMetadata(doc);
|
|
382
|
+
const isFlow = flowMeta['_type'] === 'ixo.flow.crdt';
|
|
383
|
+
if (isFlow) {
|
|
384
|
+
return JSON.stringify({
|
|
385
|
+
success: false,
|
|
386
|
+
blockId,
|
|
387
|
+
error: `Cannot apply runtimeUpdates directly to action block "${blockId}" in a flow document. ` +
|
|
388
|
+
`Use the execute_action tool instead — it runs the action through the flow engine ` +
|
|
389
|
+
`(activation → authorization → execution → runtime state update) for a proper audit trail.`,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// Snapshot before changes for diff
|
|
395
|
+
const beforeBlock = getBlockDetail(doc, blockId, true);
|
|
396
|
+
const beforeProps = beforeBlock
|
|
397
|
+
? extractBlockProperties(beforeBlock)
|
|
398
|
+
: {};
|
|
399
|
+
const beforeText = beforeBlock?.text;
|
|
400
|
+
// Wrap updates in 'props' for consistency with CLI pattern
|
|
401
|
+
const attributes = Object.keys(updates).length > 0 ? { props: updates } : {};
|
|
402
|
+
// Use the production-tested editBlock helper
|
|
403
|
+
const _snapshot = editBlock(doc, {
|
|
404
|
+
blockId,
|
|
405
|
+
attributes,
|
|
406
|
+
removeAttributes,
|
|
407
|
+
text: text === null ? undefined : text,
|
|
408
|
+
docName: 'document',
|
|
409
|
+
});
|
|
410
|
+
// Apply runtime state updates if provided
|
|
411
|
+
let updatedRuntimeState;
|
|
412
|
+
if (runtimeUpdates &&
|
|
413
|
+
typeof runtimeUpdates === 'object' &&
|
|
414
|
+
Object.keys(runtimeUpdates).length > 0) {
|
|
415
|
+
doc.transact(() => {
|
|
416
|
+
updatedRuntimeState = updateRuntimeState(doc, blockId, runtimeUpdates);
|
|
417
|
+
}, 'blocknote-crdt-playground');
|
|
418
|
+
}
|
|
419
|
+
// Create simplified response for agents with change tracking
|
|
420
|
+
const updatedBlock = getBlockDetail(doc, blockId, true);
|
|
421
|
+
const afterProps = updatedBlock
|
|
422
|
+
? extractBlockProperties(updatedBlock)
|
|
423
|
+
: {};
|
|
424
|
+
// Build list of what actually changed
|
|
425
|
+
const updatedFields = [];
|
|
426
|
+
for (const key of Object.keys(updates)) {
|
|
427
|
+
if (JSON.stringify(beforeProps[key]) !== JSON.stringify(afterProps[key])) {
|
|
428
|
+
updatedFields.push(key);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (text !== null && text !== beforeText) {
|
|
432
|
+
updatedFields.push('text');
|
|
433
|
+
}
|
|
434
|
+
if (removeAttributes.length > 0) {
|
|
435
|
+
updatedFields.push(...removeAttributes.map((k) => `-${k}`));
|
|
436
|
+
}
|
|
437
|
+
if (updatedRuntimeState) {
|
|
438
|
+
updatedFields.push(...Object.keys(runtimeUpdates).map((k) => `runtime.${k}`));
|
|
439
|
+
}
|
|
440
|
+
const simplified = updatedBlock
|
|
441
|
+
? simplifyBlockForAgent(updatedBlock)
|
|
442
|
+
: null;
|
|
443
|
+
// Build diff from before/after state
|
|
444
|
+
const diff = {};
|
|
445
|
+
for (const field of updatedFields) {
|
|
446
|
+
if (field.startsWith('-') || field.startsWith('runtime.'))
|
|
447
|
+
continue;
|
|
448
|
+
if (field === 'text') {
|
|
449
|
+
diff.text = {
|
|
450
|
+
old: beforeText ?? '',
|
|
451
|
+
new: updatedBlock?.text ?? '',
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
diff[field] = { old: beforeProps[field], new: afterProps[field] };
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
// Changes are automatically synced by your Matrix provider
|
|
459
|
+
return JSON.stringify({
|
|
460
|
+
success: true,
|
|
461
|
+
roomId,
|
|
462
|
+
blockId,
|
|
463
|
+
blockType: simplified?.type,
|
|
464
|
+
message: `Updated ${updatedFields.length} field(s): ${updatedFields.join(', ') || 'no changes detected'}`,
|
|
465
|
+
updatedFields,
|
|
466
|
+
block: simplified,
|
|
467
|
+
diff,
|
|
468
|
+
...(updatedRuntimeState && { runtimeState: updatedRuntimeState }),
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
catch (error) {
|
|
472
|
+
Logger.error('Error editing block:', error);
|
|
473
|
+
return JSON.stringify({
|
|
474
|
+
success: false,
|
|
475
|
+
error: error instanceof Error ? error.message : String(error),
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
finally {
|
|
479
|
+
// Schedule cleanup after delay to allow Y.Doc to finish syncing
|
|
480
|
+
await providerManager.dispose();
|
|
481
|
+
}
|
|
482
|
+
}, {
|
|
483
|
+
name: 'edit_block',
|
|
484
|
+
description: `Edits an existing block's properties, content, and/or runtime state.
|
|
485
|
+
|
|
486
|
+
**CRITICAL WORKFLOW:**
|
|
487
|
+
1. Call list_blocks FIRST to get the exact UUID
|
|
488
|
+
2. Pass updates as plain key-value pairs (tool wraps them automatically)
|
|
489
|
+
3. Never guess or invent block IDs
|
|
490
|
+
|
|
491
|
+
**How Updates Work:**
|
|
492
|
+
- Pass properties as plain objects like \`{status: "open", title: "New Title"}\`
|
|
493
|
+
- Tool automatically wraps them in the internal \`props\` structure
|
|
494
|
+
- Use \`runtimeUpdates\` to update runtime state (execution status, timestamps, etc.) — merges with existing state, never overwrites
|
|
495
|
+
- Changes sync to all clients via CRDT
|
|
496
|
+
|
|
497
|
+
**JSON-string properties (inputs, links):**
|
|
498
|
+
- Some properties like \`inputs\` and \`links\` are stored as JSON strings internally
|
|
499
|
+
- When reading blocks, these are returned as parsed objects/arrays
|
|
500
|
+
- When updating, pass them as objects/arrays — they are auto-serialized back to JSON strings
|
|
501
|
+
- For \`inputs\` (object): your updates are MERGED with existing values. Example: \`{"inputs": {"credential": "abc"}}\` merges into existing inputs
|
|
502
|
+
- For \`links\` (array): your value REPLACES the existing array. Each link item needs: \`id\`, \`title\`, \`description\`, \`position\`. For external URLs add \`externalUrl\`. For internal flow links add \`docRoomId\`.
|
|
503
|
+
|
|
504
|
+
**Examples:**
|
|
505
|
+
- Update status: \`{"blockId": "uuid", "updates": {"status": "open"}}\`
|
|
506
|
+
- Update text: \`{"blockId": "uuid", "updates": {}, "text": "New content"}\`
|
|
507
|
+
- Update action inputs: \`{"blockId": "uuid", "updates": {"inputs": {"credential": "data", "roomId": "!room:server"}}}\`
|
|
508
|
+
- Update flowLink with external URL: \`{"blockId": "uuid", "updates": {"links": [{"id": "link-1", "title": "Verify Identity", "description": "Click to verify", "captionText": "", "position": 0, "externalUrl": "https://example.com/verify"}]}}\`
|
|
509
|
+
- Update runtime: \`{"blockId": "uuid", "updates": {}, "runtimeUpdates": {"evaluationStatus": "approved"}}\`
|
|
510
|
+
- Remove attrs: \`{"blockId": "uuid", "updates": {}, "removeAttributes": ["oldProp"]}\`
|
|
511
|
+
|
|
512
|
+
**Note:** Block properties vary by block type and may evolve. Use \`list_blocks\` or \`read_block_by_id\` to discover current properties for any block type.
|
|
513
|
+
|
|
514
|
+
**Returns:** Block details including id, type, properties, text, and runtimeState (if updated).
|
|
515
|
+
|
|
516
|
+
**Example response:**
|
|
517
|
+
\`\`\`json
|
|
518
|
+
{
|
|
519
|
+
"success": true,
|
|
520
|
+
"message": "Successfully updated block...",
|
|
521
|
+
"block": {
|
|
522
|
+
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
523
|
+
"type": "proposal",
|
|
524
|
+
"properties": {
|
|
525
|
+
"status": "open",
|
|
526
|
+
"title": "Updated Title",
|
|
527
|
+
"description": "Updated description"
|
|
528
|
+
},
|
|
529
|
+
"text": "Optional text content"
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
\`\`\``,
|
|
533
|
+
schema: z.object({
|
|
534
|
+
blockId: z
|
|
535
|
+
.string()
|
|
536
|
+
.describe('The exact ID of the block to edit (get from list_blocks)'),
|
|
537
|
+
updates: z
|
|
538
|
+
.record(z.any(), z.any())
|
|
539
|
+
.describe("Object with property updates. Example: {status: 'open', title: 'New Title'}"),
|
|
540
|
+
removeAttributes: z
|
|
541
|
+
.array(z.string())
|
|
542
|
+
.optional()
|
|
543
|
+
.default([])
|
|
544
|
+
.describe("Array of attribute keys to remove. Example: ['oldProp', 'tempData']"),
|
|
545
|
+
text: z
|
|
546
|
+
.string()
|
|
547
|
+
.nullable()
|
|
548
|
+
.optional()
|
|
549
|
+
.describe('New text content for the block. Use null to keep existing, empty string to clear'),
|
|
550
|
+
runtimeUpdates: z
|
|
551
|
+
.record(z.any(), z.any())
|
|
552
|
+
.optional()
|
|
553
|
+
.describe('Optional: merge updates into the block runtime state (execution status, claims, timestamps, etc.). Merges with existing state — never overwrites.'),
|
|
554
|
+
}),
|
|
555
|
+
});
|
|
556
|
+
// ============================================================================
|
|
557
|
+
// Tool 3: Create Block
|
|
558
|
+
// ============================================================================
|
|
559
|
+
/**
|
|
560
|
+
* Creates a new block in the document using appendBlock from blockActions.ts.
|
|
561
|
+
* Supports all block types — new blocks are appended to the end of the document.
|
|
562
|
+
*/
|
|
563
|
+
const createBlockTool = tool(async ({ blockType, text = '', attributes = {}, blockId = null, referenceBlockId = null, placement = null, }) => {
|
|
564
|
+
Logger.log(`➕ create_block tool invoked for type: ${blockType}`);
|
|
565
|
+
// Use the shared Matrix client (already synced)
|
|
566
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
567
|
+
try {
|
|
568
|
+
const { doc } = await providerManager.init();
|
|
569
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
570
|
+
if (!isInRoom) {
|
|
571
|
+
return JSON.stringify({
|
|
572
|
+
success: false,
|
|
573
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
// Wrap attributes in 'props' for consistency with edit_block and BlockNote schema
|
|
577
|
+
const wrappedAttributes = Object.keys(attributes).length > 0 ? { props: attributes } : {};
|
|
578
|
+
const resolvedBlockId = blockId ?? randomUUID();
|
|
579
|
+
let snapshot;
|
|
580
|
+
// Use positional insertion if reference block is provided
|
|
581
|
+
if (referenceBlockId && placement) {
|
|
582
|
+
snapshot = insertBlock(doc, {
|
|
583
|
+
referenceBlockId,
|
|
584
|
+
placement,
|
|
585
|
+
blockId: resolvedBlockId,
|
|
586
|
+
blockType,
|
|
587
|
+
text,
|
|
588
|
+
attributes: wrappedAttributes,
|
|
589
|
+
docName: 'document',
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
snapshot = appendBlock(doc, {
|
|
594
|
+
blockId: resolvedBlockId,
|
|
595
|
+
blockType,
|
|
596
|
+
text,
|
|
597
|
+
attributes: wrappedAttributes,
|
|
598
|
+
docName: 'document',
|
|
599
|
+
namespace: undefined,
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
// Get simplified view for agents
|
|
603
|
+
const createdBlock = getBlockDetail(doc, snapshot.id, true);
|
|
604
|
+
const simplified = createdBlock
|
|
605
|
+
? simplifyBlockForAgent(createdBlock)
|
|
606
|
+
: null;
|
|
607
|
+
// Count blocks to determine position
|
|
608
|
+
const fragment = doc.getXmlFragment('document');
|
|
609
|
+
const allBlocks = collectAllBlocks(fragment);
|
|
610
|
+
const position = allBlocks.findIndex((b) => b.id === snapshot.id);
|
|
611
|
+
const diff = {
|
|
612
|
+
block: {
|
|
613
|
+
old: null,
|
|
614
|
+
new: simplified ?? {
|
|
615
|
+
id: snapshot.id,
|
|
616
|
+
type: blockType,
|
|
617
|
+
properties: attributes,
|
|
618
|
+
text,
|
|
619
|
+
},
|
|
620
|
+
},
|
|
621
|
+
};
|
|
622
|
+
return JSON.stringify({
|
|
623
|
+
success: true,
|
|
624
|
+
roomId,
|
|
625
|
+
blockId: snapshot.id,
|
|
626
|
+
blockType,
|
|
627
|
+
position: position >= 0 ? position : allBlocks.length - 1,
|
|
628
|
+
message: referenceBlockId
|
|
629
|
+
? `Created ${blockType} block ${placement} block ${referenceBlockId}`
|
|
630
|
+
: `Created ${blockType} block at end of document`,
|
|
631
|
+
block: simplified || snapshot,
|
|
632
|
+
diff,
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
catch (error) {
|
|
636
|
+
Logger.error('Error creating block:', error);
|
|
637
|
+
return JSON.stringify({
|
|
638
|
+
success: false,
|
|
639
|
+
error: error instanceof Error ? error.message : String(error),
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
finally {
|
|
643
|
+
// Schedule cleanup after delay to allow Y.Doc to finish syncing
|
|
644
|
+
await providerManager.dispose();
|
|
645
|
+
}
|
|
646
|
+
}, {
|
|
647
|
+
name: 'create_block',
|
|
648
|
+
description: `Creates a new block in the BlockNote document.
|
|
649
|
+
|
|
650
|
+
**Usage:**
|
|
651
|
+
- By default, appends new blocks to the end of the document
|
|
652
|
+
- Use \`referenceBlockId\` + \`placement\` to insert before/after a specific block
|
|
653
|
+
- Initialize blocks with specific properties as key-value pairs
|
|
654
|
+
- Block ID (UUID) is auto-generated unless you provide one
|
|
655
|
+
- Use \`read_block_by_id\` on existing blocks to discover available properties for any block type
|
|
656
|
+
|
|
657
|
+
**Examples:**
|
|
658
|
+
- Append paragraph: \`{"blockType": "paragraph", "text": "Hello world"}\`
|
|
659
|
+
- Insert before a block: \`{"blockType": "paragraph", "text": "Inserted text", "referenceBlockId": "uuid-here", "placement": "before"}\`
|
|
660
|
+
- Insert after a block: \`{"blockType": "proposal", "attributes": {"status": "draft"}, "referenceBlockId": "uuid-here", "placement": "after"}\`
|
|
661
|
+
|
|
662
|
+
**Note:** Block attributes vary by type and may evolve. Use \`read_block_by_id\` on existing blocks to discover available properties.
|
|
663
|
+
|
|
664
|
+
**Returns:**
|
|
665
|
+
\`\`\`json
|
|
666
|
+
{
|
|
667
|
+
"success": true,
|
|
668
|
+
"message": "Successfully created proposal block",
|
|
669
|
+
"block": {
|
|
670
|
+
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
671
|
+
"type": "proposal",
|
|
672
|
+
"properties": {...},
|
|
673
|
+
"text": ""
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
\`\`\`
|
|
677
|
+
|
|
678
|
+
The returned block includes the auto-generated UUID that you can use for future edits.`,
|
|
679
|
+
schema: z.object({
|
|
680
|
+
blockType: z
|
|
681
|
+
.string()
|
|
682
|
+
.describe('Type of block to create: paragraph, proposal, checkbox, apiRequest, list, etc.'),
|
|
683
|
+
text: z
|
|
684
|
+
.string()
|
|
685
|
+
.optional()
|
|
686
|
+
.default('')
|
|
687
|
+
.describe('Text content for the block (mainly for paragraphs)'),
|
|
688
|
+
attributes: z
|
|
689
|
+
.record(z.any(), z.any())
|
|
690
|
+
.optional()
|
|
691
|
+
.default({})
|
|
692
|
+
.describe("Block-specific attributes as key-value pairs. Example: {status: 'draft', title: 'My Proposal'}"),
|
|
693
|
+
blockId: z
|
|
694
|
+
.string()
|
|
695
|
+
.optional()
|
|
696
|
+
.nullable()
|
|
697
|
+
.describe('Optional: custom block ID. If not provided, one will be generated automatically'),
|
|
698
|
+
referenceBlockId: z
|
|
699
|
+
.string()
|
|
700
|
+
.optional()
|
|
701
|
+
.nullable()
|
|
702
|
+
.describe('Optional: ID of an existing block to insert relative to. Must be used with placement.'),
|
|
703
|
+
placement: z
|
|
704
|
+
.enum(['before', 'after'])
|
|
705
|
+
.optional()
|
|
706
|
+
.nullable()
|
|
707
|
+
.describe('Optional: insert "before" or "after" the referenceBlockId. Required when referenceBlockId is provided.'),
|
|
708
|
+
}),
|
|
709
|
+
});
|
|
710
|
+
const readBlockByIdTool = tool(async ({ blockId, evaluateConditions: evalConds = false, resolveReferences: resolveRefs = false, }) => {
|
|
711
|
+
Logger.log(`📄 read_block_by_id tool invoked for block: ${blockId}`);
|
|
712
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
713
|
+
try {
|
|
714
|
+
const { doc } = await providerManager.init();
|
|
715
|
+
const block = getBlockDetail(doc, blockId, true);
|
|
716
|
+
if (!block) {
|
|
717
|
+
return JSON.stringify({
|
|
718
|
+
success: false,
|
|
719
|
+
blockId,
|
|
720
|
+
error: `Block with id ${blockId} not found`,
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
const simplified = simplifyBlockForAgent(block);
|
|
724
|
+
const result = {
|
|
725
|
+
success: true,
|
|
726
|
+
blockId,
|
|
727
|
+
blockType: simplified.type,
|
|
728
|
+
block: simplified,
|
|
729
|
+
};
|
|
730
|
+
// Include runtime state for this block if it exists
|
|
731
|
+
const blockRuntimeState = readRuntimeState(doc, blockId);
|
|
732
|
+
const runtimeData = blockRuntimeState[blockId];
|
|
733
|
+
if (runtimeData && Object.keys(runtimeData).length > 0) {
|
|
734
|
+
result.runtimeState = runtimeData;
|
|
735
|
+
}
|
|
736
|
+
// Optional: evaluate conditions
|
|
737
|
+
if (evalConds) {
|
|
738
|
+
const attrs = block.attributes || {};
|
|
739
|
+
const attrsObj = attrs.attrs || {};
|
|
740
|
+
const props = attrsObj.props || {};
|
|
741
|
+
const conditionsJson = props.conditions || attrs.conditions || '';
|
|
742
|
+
if (conditionsJson) {
|
|
743
|
+
try {
|
|
744
|
+
const conditionConfig = JSON.parse(conditionsJson);
|
|
745
|
+
const fragment = doc.getXmlFragment('document');
|
|
746
|
+
const allBlocks = collectAllBlocks(fragment);
|
|
747
|
+
result.conditionEvaluation = evaluateBlockConditions(conditionConfig, allBlocks);
|
|
748
|
+
}
|
|
749
|
+
catch {
|
|
750
|
+
result.conditionEvaluation = {
|
|
751
|
+
error: 'Failed to parse conditions JSON',
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
result.conditionEvaluation = {
|
|
757
|
+
isVisible: true,
|
|
758
|
+
isEnabled: true,
|
|
759
|
+
actions: [],
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
// Optional: resolve references in string props
|
|
764
|
+
if (resolveRefs) {
|
|
765
|
+
const fragment = doc.getXmlFragment('document');
|
|
766
|
+
const allBlocks = collectAllBlocks(fragment);
|
|
767
|
+
const resolvedProps = {};
|
|
768
|
+
const blockProps = simplified.properties || {};
|
|
769
|
+
for (const [key, val] of Object.entries(blockProps)) {
|
|
770
|
+
if (typeof val === 'string' &&
|
|
771
|
+
val.includes('{{') &&
|
|
772
|
+
val.includes('}}')) {
|
|
773
|
+
resolvedProps[key] = resolveBlockReferences(val, allBlocks);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
if (Object.keys(resolvedProps).length > 0) {
|
|
777
|
+
result.resolvedReferences = resolvedProps;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
return JSON.stringify(result, null, 2);
|
|
781
|
+
}
|
|
782
|
+
catch (error) {
|
|
783
|
+
return JSON.stringify({
|
|
784
|
+
success: false,
|
|
785
|
+
error: error instanceof Error ? error.message : String(error),
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
finally {
|
|
789
|
+
await providerManager.dispose();
|
|
790
|
+
}
|
|
791
|
+
}, {
|
|
792
|
+
name: 'read_block_by_id',
|
|
793
|
+
description: `Reads a block by its ID. Returns block properties AND runtime state (execution status, claims, timestamps, etc.) in a single call.
|
|
794
|
+
|
|
795
|
+
Automatically includes runtimeState from Y.Map('runtime') when data exists for this block. Parses surveySchema and answers for survey blocks.
|
|
796
|
+
|
|
797
|
+
Optional flags:
|
|
798
|
+
- evaluateConditions: true → evaluates the block's condition config against all blocks, returns { isVisible, isEnabled, conditionActions[] }
|
|
799
|
+
- resolveReferences: true → resolves {{blockId.prop}} patterns in block props, returns resolved values`,
|
|
800
|
+
schema: z.object({
|
|
801
|
+
blockId: z.string().describe('The ID of the block to read'),
|
|
802
|
+
evaluateConditions: z
|
|
803
|
+
.boolean()
|
|
804
|
+
.optional()
|
|
805
|
+
.default(false)
|
|
806
|
+
.describe('If true, evaluates the block conditions and returns visibility/enabled state'),
|
|
807
|
+
resolveReferences: z
|
|
808
|
+
.boolean()
|
|
809
|
+
.optional()
|
|
810
|
+
.default(false)
|
|
811
|
+
.describe('If true, resolves {{blockId.prop}} template references in block props'),
|
|
812
|
+
}),
|
|
813
|
+
});
|
|
814
|
+
// ============================================================================
|
|
815
|
+
// Tool 5: Read Survey
|
|
816
|
+
// ============================================================================
|
|
817
|
+
const readSurveyTool = tool(async ({ blockId }) => {
|
|
818
|
+
Logger.log(`📋 read_survey tool invoked for block: ${blockId}`);
|
|
819
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
820
|
+
try {
|
|
821
|
+
const { doc } = await providerManager.init();
|
|
822
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
823
|
+
if (!isInRoom) {
|
|
824
|
+
return JSON.stringify({
|
|
825
|
+
success: false,
|
|
826
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
const block = getBlockDetail(doc, blockId, true);
|
|
830
|
+
console.log('🚀 ~ createBlocknoteTools ~ block:', block);
|
|
831
|
+
if (!block) {
|
|
832
|
+
return JSON.stringify({
|
|
833
|
+
success: false,
|
|
834
|
+
error: `Block with id ${blockId} not found`,
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
const properties = extractBlockProperties(block);
|
|
838
|
+
const surveySchema = properties.surveySchema;
|
|
839
|
+
const answers = (properties.answers || {});
|
|
840
|
+
if (!surveySchema) {
|
|
841
|
+
Logger.error('Block does not contain a surveySchema:', block);
|
|
842
|
+
return JSON.stringify({
|
|
843
|
+
success: false,
|
|
844
|
+
error: `Block ${blockId} does not contain a surveySchema property. This tool works with any block that has a surveySchema (domainCreator, form, governanceGroup, bid, claim, etc.)`,
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
// Extract all questions with visibility computed inline
|
|
848
|
+
const allQuestions = await extractSurveyQuestions(surveySchema, answers);
|
|
849
|
+
const missingRequired = await getMissingRequiredFields(answers, surveySchema);
|
|
850
|
+
return JSON.stringify({
|
|
851
|
+
success: true,
|
|
852
|
+
survey: {
|
|
853
|
+
title: surveySchema.title,
|
|
854
|
+
description: surveySchema.description,
|
|
855
|
+
},
|
|
856
|
+
questions: allQuestions,
|
|
857
|
+
answers,
|
|
858
|
+
missingRequiredFields: missingRequired,
|
|
859
|
+
totalQuestions: allQuestions.length,
|
|
860
|
+
}, null, 2);
|
|
861
|
+
}
|
|
862
|
+
catch (error) {
|
|
863
|
+
Logger.error('Error reading survey:', error);
|
|
864
|
+
return JSON.stringify({
|
|
865
|
+
success: false,
|
|
866
|
+
error: error instanceof Error ? error.message : String(error),
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
finally {
|
|
870
|
+
await providerManager.dispose();
|
|
871
|
+
}
|
|
872
|
+
}, {
|
|
873
|
+
name: 'read_survey',
|
|
874
|
+
description: `Reads survey schema and current answers from any block with a surveySchema property.
|
|
875
|
+
|
|
876
|
+
**Purpose:**
|
|
877
|
+
- View complete survey structure (ALL questions including hidden ones)
|
|
878
|
+
- See current answers as structured JSON
|
|
879
|
+
- Identify which questions are currently visible vs hidden
|
|
880
|
+
- Understand why fields are hidden (via visibleIf conditions)
|
|
881
|
+
- Find missing required fields (only for visible questions)
|
|
882
|
+
- Automatically fetches choices from choicesByUrl for dropdown questions
|
|
883
|
+
- Works with any block type that has a surveySchema (domainCreator, form, governanceGroup, bid, claim, etc.)
|
|
884
|
+
|
|
885
|
+
**Important:**
|
|
886
|
+
- ALL questions are returned (both visible and hidden), not just visible ones
|
|
887
|
+
- \`isVisible: true\` means the field is currently shown in the UI
|
|
888
|
+
- \`isVisible: false\` means the field is hidden by a \`visibleIf\` condition
|
|
889
|
+
- \`visibleIf\` field shows the condition that controls visibility
|
|
890
|
+
- Hidden fields can be made visible by changing the controlling answer
|
|
891
|
+
- Nested dynamic panel template elements are included in the questions array
|
|
892
|
+
- Choices from choicesByUrl are automatically fetched and included
|
|
893
|
+
|
|
894
|
+
**Note:** The \`answers\` object may contain data for hidden fields — use the \`questions\` array to understand the schema for those fields.`,
|
|
895
|
+
schema: z.object({
|
|
896
|
+
blockId: z
|
|
897
|
+
.string()
|
|
898
|
+
.describe('The ID of the block containing the survey'),
|
|
899
|
+
}),
|
|
900
|
+
});
|
|
901
|
+
// ============================================================================
|
|
902
|
+
// Tool 6: Fill Survey Answers
|
|
903
|
+
// ============================================================================
|
|
904
|
+
const fillSurveyAnswersTool = tool(async ({ blockId, answers, merge = true }) => {
|
|
905
|
+
Logger.log(`✏️ fill_survey_answers tool invoked for block: ${blockId}`);
|
|
906
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
907
|
+
try {
|
|
908
|
+
const { doc } = await providerManager.init();
|
|
909
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
910
|
+
if (!isInRoom) {
|
|
911
|
+
return JSON.stringify({
|
|
912
|
+
success: false,
|
|
913
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
const block = getBlockDetail(doc, blockId, true);
|
|
917
|
+
if (!block) {
|
|
918
|
+
return JSON.stringify({
|
|
919
|
+
success: false,
|
|
920
|
+
error: `Block with id ${blockId} not found`,
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
const properties = extractBlockProperties(block);
|
|
924
|
+
const surveySchema = properties.surveySchema;
|
|
925
|
+
if (!surveySchema) {
|
|
926
|
+
return JSON.stringify({
|
|
927
|
+
success: false,
|
|
928
|
+
error: `Block ${blockId} does not contain a surveySchema property. This tool works with any block that has a surveySchema (domainCreator, form, governanceGroup, bid, claim, etc.)`,
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
// Get current answers
|
|
932
|
+
const currentAnswers = (properties.answers || {});
|
|
933
|
+
// Merge or replace answers
|
|
934
|
+
const updatedAnswers = merge
|
|
935
|
+
? { ...currentAnswers, ...answers }
|
|
936
|
+
: answers;
|
|
937
|
+
// Validate the updated answers
|
|
938
|
+
const validation = await validateAnswersAgainstSchema(updatedAnswers, surveySchema);
|
|
939
|
+
// Update the block's answers attribute
|
|
940
|
+
const fragment = doc.getXmlFragment('document');
|
|
941
|
+
const blockContainer = findBlockById(fragment, blockId);
|
|
942
|
+
if (!blockContainer) {
|
|
943
|
+
return JSON.stringify({
|
|
944
|
+
success: false,
|
|
945
|
+
error: `Block container not found`,
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
// Use Y.js transaction to update the answers
|
|
949
|
+
doc.transact(() => {
|
|
950
|
+
// Find the content child element that has an answers attribute
|
|
951
|
+
const contentElement = blockContainer
|
|
952
|
+
.toArray()
|
|
953
|
+
.find((node) => node instanceof Y.XmlElement &&
|
|
954
|
+
node.nodeName !== 'blockGroup' &&
|
|
955
|
+
node.nodeName !== 'blockContainer');
|
|
956
|
+
if (contentElement) {
|
|
957
|
+
// Update the answers attribute as JSON string directly on the child element
|
|
958
|
+
contentElement.setAttribute('answers', JSON.stringify(updatedAnswers));
|
|
959
|
+
}
|
|
960
|
+
else {
|
|
961
|
+
logger.error('Content element not found, falling back to edit_block helper');
|
|
962
|
+
// Fallback: use edit_block helper which handles the structure properly
|
|
963
|
+
editBlock(doc, {
|
|
964
|
+
blockId,
|
|
965
|
+
attributes: {
|
|
966
|
+
props: { answers: JSON.stringify(updatedAnswers) },
|
|
967
|
+
},
|
|
968
|
+
docName: 'document',
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
}, 'blocknote-crdt-playground');
|
|
972
|
+
return JSON.stringify({
|
|
973
|
+
success: true,
|
|
974
|
+
message: `Successfully ${merge ? 'merged' : 'replaced'} survey answers`,
|
|
975
|
+
answers: updatedAnswers,
|
|
976
|
+
validation,
|
|
977
|
+
missingRequiredFields: await getMissingRequiredFields(updatedAnswers, surveySchema),
|
|
978
|
+
}, null, 2);
|
|
979
|
+
}
|
|
980
|
+
catch (error) {
|
|
981
|
+
Logger.error('Error filling survey answers:', error);
|
|
982
|
+
return JSON.stringify({
|
|
983
|
+
success: false,
|
|
984
|
+
error: error instanceof Error ? error.message : String(error),
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
finally {
|
|
988
|
+
await providerManager.dispose();
|
|
989
|
+
}
|
|
990
|
+
}, {
|
|
991
|
+
name: 'fill_survey_answers',
|
|
992
|
+
description: `Fills in survey answers for any block with a surveySchema. Intelligently merges with existing answers and validates against schema.
|
|
993
|
+
|
|
994
|
+
**Purpose:**
|
|
995
|
+
- Fill in partial or complete survey answers
|
|
996
|
+
- Merge with existing answers (default) or replace them
|
|
997
|
+
- Automatically validates answers against schema
|
|
998
|
+
- Respects visibility conditions
|
|
999
|
+
- Works with any block type that has a surveySchema (domainCreator, form, governanceGroup, bid, claim, etc.)
|
|
1000
|
+
|
|
1001
|
+
**Example 1 - Fill single answer:**
|
|
1002
|
+
\`\`\`json
|
|
1003
|
+
{
|
|
1004
|
+
"blockId": "271fc5de-bcd8-4de0-8dd7-fb3dd5c13785",
|
|
1005
|
+
"answers": {
|
|
1006
|
+
"schema:name": "My New Domain",
|
|
1007
|
+
"schema.description": "A description of my domain"
|
|
1008
|
+
},
|
|
1009
|
+
"merge": true
|
|
1010
|
+
}
|
|
1011
|
+
\`\`\`
|
|
1012
|
+
|
|
1013
|
+
**Example 2 - Replace all answers:**
|
|
1014
|
+
\`\`\`json
|
|
1015
|
+
{
|
|
1016
|
+
"blockId": "271fc5de-bcd8-4de0-8dd7-fb3dd5c13785",
|
|
1017
|
+
"answers": {
|
|
1018
|
+
"schema:name": "New Domain",
|
|
1019
|
+
"type_2": "dao",
|
|
1020
|
+
"schema:validFrom": "2025-01-01"
|
|
1021
|
+
},
|
|
1022
|
+
"merge": false
|
|
1023
|
+
}
|
|
1024
|
+
\`\`\`
|
|
1025
|
+
|
|
1026
|
+
**Note:**
|
|
1027
|
+
- Use merge=true (default) to keep existing answers and only update specified fields
|
|
1028
|
+
- Use merge=false to replace all answers
|
|
1029
|
+
- Answers are validated automatically
|
|
1030
|
+
- Only visible questions (based on visibility conditions) are considered`,
|
|
1031
|
+
schema: z.object({
|
|
1032
|
+
blockId: z
|
|
1033
|
+
.string()
|
|
1034
|
+
.describe('The ID of the block containing the survey'),
|
|
1035
|
+
answers: z
|
|
1036
|
+
.record(z.any(), z.any())
|
|
1037
|
+
.describe('Object with answer key-value pairs. Keys should match question names from the schema.'),
|
|
1038
|
+
merge: z
|
|
1039
|
+
.boolean()
|
|
1040
|
+
.optional()
|
|
1041
|
+
.default(true)
|
|
1042
|
+
.describe('If true, merge with existing answers. If false, replace all answers.'),
|
|
1043
|
+
}),
|
|
1044
|
+
});
|
|
1045
|
+
// ============================================================================
|
|
1046
|
+
// Tool 7: Validate Survey Answers
|
|
1047
|
+
// ============================================================================
|
|
1048
|
+
const validateSurveyAnswersTool = tool(async ({ blockId }) => {
|
|
1049
|
+
Logger.log(`✅ validate_survey_answers tool invoked for block: ${blockId}`);
|
|
1050
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
1051
|
+
try {
|
|
1052
|
+
const { doc } = await providerManager.init();
|
|
1053
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
1054
|
+
if (!isInRoom) {
|
|
1055
|
+
return JSON.stringify({
|
|
1056
|
+
success: false,
|
|
1057
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
const block = getBlockDetail(doc, blockId, true);
|
|
1061
|
+
if (!block) {
|
|
1062
|
+
return JSON.stringify({
|
|
1063
|
+
success: false,
|
|
1064
|
+
error: `Block with id ${blockId} not found`,
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
const properties = extractBlockProperties(block);
|
|
1068
|
+
const surveySchema = properties.surveySchema;
|
|
1069
|
+
const answers = (properties.answers || {});
|
|
1070
|
+
if (!surveySchema) {
|
|
1071
|
+
return JSON.stringify({
|
|
1072
|
+
success: false,
|
|
1073
|
+
error: `Block ${blockId} does not contain a surveySchema property. This tool works with any block that has a surveySchema (domainCreator, form, governanceGroup, bid, claim, etc.)`,
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
const validation = await validateAnswersAgainstSchema(answers, surveySchema);
|
|
1077
|
+
const missingRequired = await getMissingRequiredFields(answers, surveySchema);
|
|
1078
|
+
const visibleQuestions = await getVisibleQuestions(answers, surveySchema);
|
|
1079
|
+
return JSON.stringify({
|
|
1080
|
+
success: true,
|
|
1081
|
+
valid: validation.valid,
|
|
1082
|
+
errors: validation.errors,
|
|
1083
|
+
warnings: validation.warnings,
|
|
1084
|
+
missingRequiredFields: missingRequired,
|
|
1085
|
+
answeredQuestions: Object.keys(answers).length,
|
|
1086
|
+
visibleQuestionsCount: visibleQuestions.length,
|
|
1087
|
+
totalRequiredFields: visibleQuestions.filter((q) => q.isRequired)
|
|
1088
|
+
.length,
|
|
1089
|
+
completionPercentage: visibleQuestions.length > 0
|
|
1090
|
+
? Math.round(((visibleQuestions.length - missingRequired.length) /
|
|
1091
|
+
visibleQuestions.length) *
|
|
1092
|
+
100)
|
|
1093
|
+
: 0,
|
|
1094
|
+
}, null, 2);
|
|
1095
|
+
}
|
|
1096
|
+
catch (error) {
|
|
1097
|
+
Logger.error('Error validating survey answers:', error);
|
|
1098
|
+
return JSON.stringify({
|
|
1099
|
+
success: false,
|
|
1100
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
finally {
|
|
1104
|
+
await providerManager.dispose();
|
|
1105
|
+
}
|
|
1106
|
+
}, {
|
|
1107
|
+
name: 'validate_survey_answers',
|
|
1108
|
+
description: `Validates current survey answers against the schema requirements. Works with any block that has a surveySchema.
|
|
1109
|
+
|
|
1110
|
+
**Purpose:**
|
|
1111
|
+
- Check if all required fields are filled
|
|
1112
|
+
- Validate answer types and formats
|
|
1113
|
+
- Identify validation errors and warnings
|
|
1114
|
+
- Calculate completion percentage
|
|
1115
|
+
- Works with any block type that has a surveySchema (domainCreator, form, governanceGroup, bid, claim, etc.)
|
|
1116
|
+
|
|
1117
|
+
**Validation Types:**
|
|
1118
|
+
- required: Field is required but missing or empty
|
|
1119
|
+
- type: Answer type doesn't match expected type (e.g., boolean vs string)
|
|
1120
|
+
- choice: Answer value not in allowed choices (for dropdowns)
|
|
1121
|
+
- format: Answer format is invalid (e.g., invalid email or URL)
|
|
1122
|
+
|
|
1123
|
+
**Note:** Only validates visible questions based on current answers and visibility conditions.`,
|
|
1124
|
+
schema: z.object({
|
|
1125
|
+
blockId: z
|
|
1126
|
+
.string()
|
|
1127
|
+
.describe('The ID of the block to validate survey answers for'),
|
|
1128
|
+
}),
|
|
1129
|
+
});
|
|
1130
|
+
// ============================================================================
|
|
1131
|
+
// Tool 8: Read Flow Context
|
|
1132
|
+
// ============================================================================
|
|
1133
|
+
const readFlowContextTool = tool(async () => {
|
|
1134
|
+
logger.log('📊 read_flow_context tool invoked');
|
|
1135
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
1136
|
+
try {
|
|
1137
|
+
const { doc } = await providerManager.init();
|
|
1138
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
1139
|
+
if (!isInRoom) {
|
|
1140
|
+
return JSON.stringify({
|
|
1141
|
+
success: false,
|
|
1142
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
const flowMetadata = readFlowMetadata(doc);
|
|
1146
|
+
const fragment = doc.getXmlFragment('document');
|
|
1147
|
+
const blocks = collectAllBlocks(fragment);
|
|
1148
|
+
const flowNodes = readFlowNodes(doc);
|
|
1149
|
+
const runtimeMap = doc.getMap('runtime');
|
|
1150
|
+
const delegationsMap = doc.getMap('delegations');
|
|
1151
|
+
return JSON.stringify({
|
|
1152
|
+
success: true,
|
|
1153
|
+
roomId,
|
|
1154
|
+
flowMetadata,
|
|
1155
|
+
summary: {
|
|
1156
|
+
blockCount: blocks.length,
|
|
1157
|
+
flowNodeCount: flowNodes.length,
|
|
1158
|
+
isFlowDocument: flowMetadata['_type'] === 'ixo.flow.crdt',
|
|
1159
|
+
hasRuntimeState: runtimeMap.size > 0,
|
|
1160
|
+
hasDelegations: delegationsMap.size > 1,
|
|
1161
|
+
},
|
|
1162
|
+
}, null, 2);
|
|
1163
|
+
}
|
|
1164
|
+
catch (error) {
|
|
1165
|
+
return JSON.stringify({
|
|
1166
|
+
success: false,
|
|
1167
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
finally {
|
|
1171
|
+
await providerManager.dispose();
|
|
1172
|
+
}
|
|
1173
|
+
}, {
|
|
1174
|
+
name: 'read_flow_context',
|
|
1175
|
+
description: `Reads flow-level metadata and document context. **Call this FIRST** in any new conversation to understand what document you're working with.
|
|
1176
|
+
|
|
1177
|
+
Returns: flow metadata (title, owner DID, doc type, schema version, creation date), block count, flow node count, and whether runtime state/delegations exist.
|
|
1178
|
+
|
|
1179
|
+
This is a lightweight call that gives you the full picture before diving into specific blocks.`,
|
|
1180
|
+
schema: z.object({}),
|
|
1181
|
+
});
|
|
1182
|
+
// ============================================================================
|
|
1183
|
+
// Tool 9: Read Flow Status
|
|
1184
|
+
// ============================================================================
|
|
1185
|
+
const readFlowStatusTool = tool(async ({ nodeId = null }) => {
|
|
1186
|
+
logger.log(`📈 read_flow_status tool invoked${nodeId ? ` for node: ${nodeId}` : ''}`);
|
|
1187
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
1188
|
+
try {
|
|
1189
|
+
const { doc } = await providerManager.init();
|
|
1190
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
1191
|
+
if (!isInRoom) {
|
|
1192
|
+
return JSON.stringify({
|
|
1193
|
+
success: false,
|
|
1194
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
const flowNodes = readFlowNodes(doc);
|
|
1198
|
+
const runtimeState = readRuntimeState(doc, nodeId ?? undefined);
|
|
1199
|
+
// Enrich runtime state with human-readable dates
|
|
1200
|
+
const enrichedState = {};
|
|
1201
|
+
for (const [id, state] of Object.entries(runtimeState)) {
|
|
1202
|
+
const enriched = { ...state };
|
|
1203
|
+
const ts = state['executionTimestamp'];
|
|
1204
|
+
if (typeof ts === 'number') {
|
|
1205
|
+
enriched['executionDate'] = new Date(ts).toISOString();
|
|
1206
|
+
}
|
|
1207
|
+
enrichedState[id] = enriched;
|
|
1208
|
+
}
|
|
1209
|
+
// Build summary — graceful field checks for generic data
|
|
1210
|
+
const allRuntimeState = readRuntimeState(doc);
|
|
1211
|
+
const stateValues = Object.values(allRuntimeState);
|
|
1212
|
+
const executedNodes = stateValues.filter((s) => s['executionTimestamp']).length;
|
|
1213
|
+
return JSON.stringify({
|
|
1214
|
+
success: true,
|
|
1215
|
+
flowNodes,
|
|
1216
|
+
runtimeState: enrichedState,
|
|
1217
|
+
summary: {
|
|
1218
|
+
totalNodes: flowNodes.length,
|
|
1219
|
+
executedNodes,
|
|
1220
|
+
pendingNodes: stateValues.filter((s) => s['evaluationStatus'] === 'pending').length,
|
|
1221
|
+
approvedNodes: stateValues.filter((s) => s['evaluationStatus'] === 'approved').length,
|
|
1222
|
+
rejectedNodes: stateValues.filter((s) => s['evaluationStatus'] === 'rejected').length,
|
|
1223
|
+
},
|
|
1224
|
+
}, null, 2);
|
|
1225
|
+
}
|
|
1226
|
+
catch (error) {
|
|
1227
|
+
return JSON.stringify({
|
|
1228
|
+
success: false,
|
|
1229
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
finally {
|
|
1233
|
+
await providerManager.dispose();
|
|
1234
|
+
}
|
|
1235
|
+
}, {
|
|
1236
|
+
name: 'read_flow_status',
|
|
1237
|
+
description: `Reads the execution status of flow nodes. Shows which blocks have been executed, by whom, when, and their evaluation status (pending/approved/rejected).
|
|
1238
|
+
|
|
1239
|
+
Use this to answer: "What's the status of this flow?", "Which steps are done?", "Who executed block X?"
|
|
1240
|
+
|
|
1241
|
+
Pass nodeId to check a specific node, or omit to get all nodes.`,
|
|
1242
|
+
schema: z.object({
|
|
1243
|
+
nodeId: z
|
|
1244
|
+
.string()
|
|
1245
|
+
.optional()
|
|
1246
|
+
.nullable()
|
|
1247
|
+
.describe('Optional: specific node ID to check. Omit for all nodes.'),
|
|
1248
|
+
}),
|
|
1249
|
+
});
|
|
1250
|
+
// ============================================================================
|
|
1251
|
+
// Tool 10: Read Block History
|
|
1252
|
+
// ============================================================================
|
|
1253
|
+
const readBlockHistoryTool = tool(async ({ blockId }) => {
|
|
1254
|
+
logger.log(`📜 read_block_history tool invoked for block: ${blockId}`);
|
|
1255
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
1256
|
+
try {
|
|
1257
|
+
const { doc } = await providerManager.init();
|
|
1258
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
1259
|
+
if (!isInRoom) {
|
|
1260
|
+
return JSON.stringify({
|
|
1261
|
+
success: false,
|
|
1262
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
const auditEvents = readAuditTrailForBlock(doc, blockId);
|
|
1266
|
+
const invocations = readInvocations(doc, blockId);
|
|
1267
|
+
const successfulInvocations = invocations.filter((i) => i['result'] === 'success').length;
|
|
1268
|
+
const failedInvocations = invocations.filter((i) => i['result'] === 'failure').length;
|
|
1269
|
+
// Enrich invocations with human-readable dates
|
|
1270
|
+
const enrichedInvocations = invocations.map((inv) => {
|
|
1271
|
+
const enriched = { ...inv };
|
|
1272
|
+
const executedAt = inv['executedAt'];
|
|
1273
|
+
if (typeof executedAt === 'number') {
|
|
1274
|
+
enriched['executedDate'] = new Date(executedAt).toISOString();
|
|
1275
|
+
}
|
|
1276
|
+
else if (typeof executedAt === 'string') {
|
|
1277
|
+
enriched['executedDate'] = new Date(executedAt).toISOString();
|
|
1278
|
+
}
|
|
1279
|
+
return enriched;
|
|
1280
|
+
});
|
|
1281
|
+
// Find most recent activity
|
|
1282
|
+
const lastAuditEvent = auditEvents.length > 0
|
|
1283
|
+
? auditEvents[auditEvents.length - 1]
|
|
1284
|
+
: undefined;
|
|
1285
|
+
const lastAuditMeta = lastAuditEvent &&
|
|
1286
|
+
typeof lastAuditEvent['meta'] === 'object' &&
|
|
1287
|
+
lastAuditEvent['meta'] !== null
|
|
1288
|
+
? lastAuditEvent['meta']
|
|
1289
|
+
: undefined;
|
|
1290
|
+
const lastAuditTs = lastAuditMeta?.['timestamp'];
|
|
1291
|
+
const firstInv = enrichedInvocations.length > 0 ? enrichedInvocations[0] : undefined;
|
|
1292
|
+
const lastInvTs = firstInv?.['executedDate'];
|
|
1293
|
+
const lastActivity = lastAuditTs && lastInvTs
|
|
1294
|
+
? lastAuditTs > lastInvTs
|
|
1295
|
+
? lastAuditTs
|
|
1296
|
+
: lastInvTs
|
|
1297
|
+
: lastAuditTs || lastInvTs;
|
|
1298
|
+
return JSON.stringify({
|
|
1299
|
+
success: true,
|
|
1300
|
+
blockId,
|
|
1301
|
+
auditEvents,
|
|
1302
|
+
invocations: enrichedInvocations,
|
|
1303
|
+
summary: {
|
|
1304
|
+
totalAuditEvents: auditEvents.length,
|
|
1305
|
+
totalInvocations: invocations.length,
|
|
1306
|
+
successfulInvocations,
|
|
1307
|
+
failedInvocations,
|
|
1308
|
+
lastActivity,
|
|
1309
|
+
},
|
|
1310
|
+
}, null, 2);
|
|
1311
|
+
}
|
|
1312
|
+
catch (error) {
|
|
1313
|
+
return JSON.stringify({
|
|
1314
|
+
success: false,
|
|
1315
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
finally {
|
|
1319
|
+
await providerManager.dispose();
|
|
1320
|
+
}
|
|
1321
|
+
}, {
|
|
1322
|
+
name: 'read_block_history',
|
|
1323
|
+
description: `Reads the complete history for a specific block: audit trail events and UCAN invocations.
|
|
1324
|
+
|
|
1325
|
+
Use this to answer: "What happened with block X?", "Who executed this?", "When was this last updated?"
|
|
1326
|
+
|
|
1327
|
+
Returns audit events (timestamped actions) and invocations (UCAN-authorized executions with results and transaction hashes).`,
|
|
1328
|
+
schema: z.object({
|
|
1329
|
+
blockId: z.string().describe('The block ID to read history for'),
|
|
1330
|
+
}),
|
|
1331
|
+
});
|
|
1332
|
+
// ============================================================================
|
|
1333
|
+
// Tool 11: Read Permissions
|
|
1334
|
+
// ============================================================================
|
|
1335
|
+
const readPermissionsTool = tool(async ({ audienceDid = null, capability = null }) => {
|
|
1336
|
+
logger.log('🔐 read_permissions tool invoked');
|
|
1337
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
1338
|
+
try {
|
|
1339
|
+
const { doc } = await providerManager.init();
|
|
1340
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
1341
|
+
if (!isInRoom) {
|
|
1342
|
+
return JSON.stringify({
|
|
1343
|
+
success: false,
|
|
1344
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
const { rootCid, delegations } = readDelegations(doc);
|
|
1348
|
+
// Apply filters using bracket notation on generic records
|
|
1349
|
+
let filtered = delegations;
|
|
1350
|
+
if (audienceDid) {
|
|
1351
|
+
filtered = filtered.filter((d) => d['audienceDid'] === audienceDid);
|
|
1352
|
+
}
|
|
1353
|
+
if (capability) {
|
|
1354
|
+
filtered = filtered.filter((d) => {
|
|
1355
|
+
const caps = d['capabilities'];
|
|
1356
|
+
if (!Array.isArray(caps))
|
|
1357
|
+
return false;
|
|
1358
|
+
return caps.some((c) => c['can'] === capability ||
|
|
1359
|
+
(typeof c['can'] === 'string' &&
|
|
1360
|
+
c['can'].endsWith('/*') &&
|
|
1361
|
+
capability.startsWith(c['can'].slice(0, -2))));
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
const now = Date.now();
|
|
1365
|
+
const enriched = filtered.map((d) => {
|
|
1366
|
+
const expiration = d['expiration'];
|
|
1367
|
+
const enrichedDelegation = { ...d };
|
|
1368
|
+
if (typeof expiration === 'number') {
|
|
1369
|
+
enrichedDelegation['expirationDate'] = new Date(expiration).toISOString();
|
|
1370
|
+
enrichedDelegation['isExpired'] = expiration < now;
|
|
1371
|
+
}
|
|
1372
|
+
else {
|
|
1373
|
+
enrichedDelegation['isExpired'] = false;
|
|
1374
|
+
}
|
|
1375
|
+
return enrichedDelegation;
|
|
1376
|
+
});
|
|
1377
|
+
const activeDelegations = enriched.filter((d) => !d['isExpired']);
|
|
1378
|
+
const uniqueActors = [
|
|
1379
|
+
...new Set(filtered
|
|
1380
|
+
.map((d) => d['audienceDid'])
|
|
1381
|
+
.filter((v) => typeof v === 'string')),
|
|
1382
|
+
];
|
|
1383
|
+
return JSON.stringify({
|
|
1384
|
+
success: true,
|
|
1385
|
+
rootDelegationCid: rootCid,
|
|
1386
|
+
delegations: enriched,
|
|
1387
|
+
summary: {
|
|
1388
|
+
totalDelegations: enriched.length,
|
|
1389
|
+
activeDelegations: activeDelegations.length,
|
|
1390
|
+
expiredDelegations: enriched.length - activeDelegations.length,
|
|
1391
|
+
uniqueActors,
|
|
1392
|
+
},
|
|
1393
|
+
}, null, 2);
|
|
1394
|
+
}
|
|
1395
|
+
catch (error) {
|
|
1396
|
+
return JSON.stringify({
|
|
1397
|
+
success: false,
|
|
1398
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
finally {
|
|
1402
|
+
await providerManager.dispose();
|
|
1403
|
+
}
|
|
1404
|
+
}, {
|
|
1405
|
+
name: 'read_permissions',
|
|
1406
|
+
description: `Reads the UCAN delegation chain — who has permission to do what in this flow.
|
|
1407
|
+
|
|
1408
|
+
Use this to answer: "Who can execute block X?", "What permissions does user Y have?", "Show me the delegation chain."
|
|
1409
|
+
|
|
1410
|
+
Optionally filter by audienceDid (recipient) or capability action (e.g., "flow/block/execute"). Supports wildcard matching (e.g., "flow/*" covers "flow/block/execute").`,
|
|
1411
|
+
schema: z.object({
|
|
1412
|
+
audienceDid: z
|
|
1413
|
+
.string()
|
|
1414
|
+
.optional()
|
|
1415
|
+
.nullable()
|
|
1416
|
+
.describe('Optional: filter by recipient DID'),
|
|
1417
|
+
capability: z
|
|
1418
|
+
.string()
|
|
1419
|
+
.optional()
|
|
1420
|
+
.nullable()
|
|
1421
|
+
.describe('Optional: filter by capability action, e.g. "flow/block/execute"'),
|
|
1422
|
+
}),
|
|
1423
|
+
});
|
|
1424
|
+
// ============================================================================
|
|
1425
|
+
// Tool 12: Delete Block
|
|
1426
|
+
// ============================================================================
|
|
1427
|
+
const deleteBlockTool = tool(async ({ blockId, confirm }) => {
|
|
1428
|
+
logger.log(`🗑️ delete_block tool invoked for block: ${blockId}`);
|
|
1429
|
+
if (!confirm) {
|
|
1430
|
+
return JSON.stringify({
|
|
1431
|
+
success: false,
|
|
1432
|
+
error: 'Deletion requires confirm: true. Set confirm to true to proceed with deletion.',
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
1436
|
+
if (!isInRoom) {
|
|
1437
|
+
return JSON.stringify({
|
|
1438
|
+
success: false,
|
|
1439
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
1443
|
+
try {
|
|
1444
|
+
const { doc } = await providerManager.init();
|
|
1445
|
+
// Snapshot before deletion so we can report what was removed
|
|
1446
|
+
const beforeBlock = getBlockDetail(doc, blockId, true);
|
|
1447
|
+
const beforeSimplified = beforeBlock
|
|
1448
|
+
? simplifyBlockForAgent(beforeBlock)
|
|
1449
|
+
: null;
|
|
1450
|
+
const deleted = deleteBlock(doc, {
|
|
1451
|
+
blockId,
|
|
1452
|
+
docName: 'document',
|
|
1453
|
+
});
|
|
1454
|
+
if (!deleted) {
|
|
1455
|
+
return JSON.stringify({
|
|
1456
|
+
success: false,
|
|
1457
|
+
blockId,
|
|
1458
|
+
error: `Block with id ${blockId} not found`,
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
// Count remaining blocks
|
|
1462
|
+
const fragment = doc.getXmlFragment('document');
|
|
1463
|
+
const remaining = collectAllBlocks(fragment);
|
|
1464
|
+
return JSON.stringify({
|
|
1465
|
+
success: true,
|
|
1466
|
+
blockId,
|
|
1467
|
+
blockType: beforeSimplified?.type,
|
|
1468
|
+
message: `Deleted ${beforeSimplified?.type || 'unknown'} block "${beforeSimplified?.text?.slice(0, 60) || '(no text)'}"`,
|
|
1469
|
+
deletedBlock: beforeSimplified,
|
|
1470
|
+
remainingBlockCount: remaining.length,
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1473
|
+
catch (error) {
|
|
1474
|
+
Logger.error('Error deleting block:', error);
|
|
1475
|
+
return JSON.stringify({
|
|
1476
|
+
success: false,
|
|
1477
|
+
blockId,
|
|
1478
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
finally {
|
|
1482
|
+
await providerManager.dispose();
|
|
1483
|
+
}
|
|
1484
|
+
}, {
|
|
1485
|
+
name: 'delete_block',
|
|
1486
|
+
description: `Removes a block from the document. Requires confirm: true as a safety check.
|
|
1487
|
+
|
|
1488
|
+
**CRITICAL:** Always call list_blocks first to verify the block ID. This action cannot be undone.`,
|
|
1489
|
+
schema: z.object({
|
|
1490
|
+
blockId: z
|
|
1491
|
+
.string()
|
|
1492
|
+
.describe('The exact UUID of the block to delete (get from list_blocks)'),
|
|
1493
|
+
confirm: z
|
|
1494
|
+
.boolean()
|
|
1495
|
+
.describe('Must be true to confirm deletion. Safety check to prevent accidental deletions.'),
|
|
1496
|
+
}),
|
|
1497
|
+
});
|
|
1498
|
+
// ============================================================================
|
|
1499
|
+
// Tool 13: Search Blocks
|
|
1500
|
+
// ============================================================================
|
|
1501
|
+
const searchBlocksTool = tool(async ({ blockType = null, propKey = null, propValue = null, textContains = null, }) => {
|
|
1502
|
+
logger.log('🔍 search_blocks tool invoked');
|
|
1503
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
1504
|
+
try {
|
|
1505
|
+
const { doc } = await providerManager.init();
|
|
1506
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
1507
|
+
if (!isInRoom) {
|
|
1508
|
+
return JSON.stringify({
|
|
1509
|
+
success: false,
|
|
1510
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
1513
|
+
const fragment = doc.getXmlFragment('document');
|
|
1514
|
+
let blocks = collectAllBlocks(fragment);
|
|
1515
|
+
// Apply filters (AND logic)
|
|
1516
|
+
if (blockType) {
|
|
1517
|
+
blocks = blocks.filter((b) => {
|
|
1518
|
+
const simplified = simplifyBlockForAgent(b);
|
|
1519
|
+
return simplified.type === blockType;
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1522
|
+
if (propKey && propValue !== null) {
|
|
1523
|
+
blocks = blocks.filter((b) => {
|
|
1524
|
+
const props = extractBlockProperties(b);
|
|
1525
|
+
const actual = String(props[propKey]);
|
|
1526
|
+
const expected = String(propValue);
|
|
1527
|
+
// Exact match or emoji-equivalent match
|
|
1528
|
+
return (actual === expected ||
|
|
1529
|
+
emojify(actual) === emojify(expected) ||
|
|
1530
|
+
unemojify(actual) === unemojify(expected));
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
if (textContains) {
|
|
1534
|
+
blocks = blocks.filter((b) => b.text && emojiAwareIncludes(b.text, textContains));
|
|
1535
|
+
}
|
|
1536
|
+
const simplified = blocks.map(simplifyBlockForAgent);
|
|
1537
|
+
// Build query echo so the agent knows what filters were applied
|
|
1538
|
+
const appliedFilters = [];
|
|
1539
|
+
if (blockType)
|
|
1540
|
+
appliedFilters.push(`type=${blockType}`);
|
|
1541
|
+
if (propKey)
|
|
1542
|
+
appliedFilters.push(`${propKey}=${propValue}`);
|
|
1543
|
+
if (textContains)
|
|
1544
|
+
appliedFilters.push(`text~"${textContains}"`);
|
|
1545
|
+
return JSON.stringify({
|
|
1546
|
+
success: true,
|
|
1547
|
+
roomId,
|
|
1548
|
+
query: appliedFilters.join(' AND ') || '(all blocks)',
|
|
1549
|
+
count: simplified.length,
|
|
1550
|
+
blocks: simplified,
|
|
1551
|
+
}, null, 2);
|
|
1552
|
+
}
|
|
1553
|
+
catch (error) {
|
|
1554
|
+
return JSON.stringify({
|
|
1555
|
+
success: false,
|
|
1556
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
finally {
|
|
1560
|
+
await providerManager.dispose();
|
|
1561
|
+
}
|
|
1562
|
+
}, {
|
|
1563
|
+
name: 'search_blocks',
|
|
1564
|
+
description: `Search blocks by type, property value, or text content. Filters combine with AND logic.
|
|
1565
|
+
|
|
1566
|
+
Examples:
|
|
1567
|
+
- Find all proposals: {"blockType": "proposal"}
|
|
1568
|
+
- Find executed blocks: {"propKey": "status", "propValue": "executed"}
|
|
1569
|
+
- Find blocks mentioning "KYC": {"textContains": "KYC"}
|
|
1570
|
+
- Combine: {"blockType": "checkbox", "propKey": "checked", "propValue": "true"}`,
|
|
1571
|
+
schema: z.object({
|
|
1572
|
+
blockType: z
|
|
1573
|
+
.string()
|
|
1574
|
+
.optional()
|
|
1575
|
+
.nullable()
|
|
1576
|
+
.describe('Filter by block type (proposal, checkbox, form, etc.)'),
|
|
1577
|
+
propKey: z
|
|
1578
|
+
.string()
|
|
1579
|
+
.optional()
|
|
1580
|
+
.nullable()
|
|
1581
|
+
.describe('Property key to search on (e.g., "status", "title")'),
|
|
1582
|
+
propValue: z
|
|
1583
|
+
.string()
|
|
1584
|
+
.optional()
|
|
1585
|
+
.nullable()
|
|
1586
|
+
.describe('Property value to match (exact string match)'),
|
|
1587
|
+
textContains: z
|
|
1588
|
+
.string()
|
|
1589
|
+
.optional()
|
|
1590
|
+
.nullable()
|
|
1591
|
+
.describe('Search text content of blocks (case-insensitive substring match)'),
|
|
1592
|
+
}),
|
|
1593
|
+
});
|
|
1594
|
+
// ============================================================================
|
|
1595
|
+
// Tool 14: Execute Action (flow engine integration)
|
|
1596
|
+
// ============================================================================
|
|
1597
|
+
/**
|
|
1598
|
+
* Executes an action block through the flow engine pipeline:
|
|
1599
|
+
* activation → authorization → execution → runtime state update.
|
|
1600
|
+
*
|
|
1601
|
+
* Supports: http.request, email.send, notification.push,
|
|
1602
|
+
* human.checkbox.set, form.submit, protocol.select
|
|
1603
|
+
*/
|
|
1604
|
+
const executeActionTool = tool(async ({ blockId, inputOverrides = {} }) => {
|
|
1605
|
+
Logger.log(`⚡ execute_action tool invoked for block: ${blockId}`);
|
|
1606
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
1607
|
+
if (!isInRoom) {
|
|
1608
|
+
return JSON.stringify({
|
|
1609
|
+
success: false,
|
|
1610
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
1614
|
+
try {
|
|
1615
|
+
const { doc } = await providerManager.init();
|
|
1616
|
+
// 1. Verify this is a flow document (not a template)
|
|
1617
|
+
const flowMeta = readFlowMetadata(doc);
|
|
1618
|
+
if (flowMeta['_type'] !== 'ixo.flow.crdt') {
|
|
1619
|
+
return JSON.stringify({
|
|
1620
|
+
success: false,
|
|
1621
|
+
error: 'execute_action is only supported on flow documents, not templates.',
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
// 2. Read the block and extract actionType
|
|
1625
|
+
const blockDetail = getBlockDetail(doc, blockId, false);
|
|
1626
|
+
if (!blockDetail) {
|
|
1627
|
+
return JSON.stringify({
|
|
1628
|
+
success: false,
|
|
1629
|
+
error: `Block "${blockId}" not found.`,
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
const blockProps = extractBlockProperties(blockDetail);
|
|
1633
|
+
const actionType = blockProps.actionType;
|
|
1634
|
+
if (!actionType) {
|
|
1635
|
+
return JSON.stringify({
|
|
1636
|
+
success: false,
|
|
1637
|
+
error: `Block "${blockId}" is not an action block (no actionType property).`,
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
// 3. Look up registered action
|
|
1641
|
+
const actionDef = getAction(actionType);
|
|
1642
|
+
if (!actionDef) {
|
|
1643
|
+
const available = getAllActions().map((a) => a.type);
|
|
1644
|
+
return JSON.stringify({
|
|
1645
|
+
success: false,
|
|
1646
|
+
error: `Unknown action type "${actionType}". Available: ${available.join(', ')}`,
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
// 4. Parse inputs from block props and merge with overrides
|
|
1650
|
+
let inputs = {};
|
|
1651
|
+
if (blockProps.inputs) {
|
|
1652
|
+
try {
|
|
1653
|
+
inputs =
|
|
1654
|
+
typeof blockProps.inputs === 'string'
|
|
1655
|
+
? JSON.parse(blockProps.inputs)
|
|
1656
|
+
: blockProps.inputs;
|
|
1657
|
+
}
|
|
1658
|
+
catch {
|
|
1659
|
+
inputs = {};
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
if (inputOverrides && Object.keys(inputOverrides).length > 0) {
|
|
1663
|
+
inputs = { ...inputs, ...inputOverrides };
|
|
1664
|
+
}
|
|
1665
|
+
// 5. Resolve {{blockId.prop}} references in input values
|
|
1666
|
+
const allBlocks = collectAllBlocks(doc.getXmlFragment('document'));
|
|
1667
|
+
for (const [key, val] of Object.entries(inputs)) {
|
|
1668
|
+
if (typeof val === 'string' &&
|
|
1669
|
+
val.includes('{{') &&
|
|
1670
|
+
val.includes('}}')) {
|
|
1671
|
+
inputs[key] = resolveBlockReferences(val, allBlocks);
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
// 6. Build FlowNode from block
|
|
1675
|
+
const flowNode = buildFlowNodeFromBlock({
|
|
1676
|
+
id: blockId,
|
|
1677
|
+
type: blockDetail.blockType || 'action',
|
|
1678
|
+
props: blockProps,
|
|
1679
|
+
});
|
|
1680
|
+
// 7. Build runtime state manager from Y.Doc
|
|
1681
|
+
const runtimeManager = createYDocRuntimeManager(doc);
|
|
1682
|
+
// 8. Derive oracle DID from Matrix user ID
|
|
1683
|
+
// Matrix format: @did-ixo-ixo1abc123:mx.server.com → did:ixo:ixo1abc123
|
|
1684
|
+
const actorDid = matrixUserIdToDid(config.matrix.userId ?? '');
|
|
1685
|
+
const flowId = flowMeta.doc_id ?? roomId;
|
|
1686
|
+
// 9. Execute through the flow engine (V1 — no UCAN invocation for MVP)
|
|
1687
|
+
// executeNode handles: activation check → authorization check → action() → runtime update
|
|
1688
|
+
const outcome = await executeNode({
|
|
1689
|
+
node: flowNode,
|
|
1690
|
+
actorDid,
|
|
1691
|
+
context: {
|
|
1692
|
+
runtime: runtimeManager,
|
|
1693
|
+
},
|
|
1694
|
+
action: async () => {
|
|
1695
|
+
const result = await actionDef.run(inputs, {
|
|
1696
|
+
actorDid,
|
|
1697
|
+
flowId,
|
|
1698
|
+
nodeId: blockId,
|
|
1699
|
+
services: oracleActionServices,
|
|
1700
|
+
});
|
|
1701
|
+
return { payload: result.output };
|
|
1702
|
+
},
|
|
1703
|
+
});
|
|
1704
|
+
// Supplement runtime with V1 lifecycle fields + action output
|
|
1705
|
+
// (executeNode's updateRuntimeAfterSuccess only writes legacy compat fields)
|
|
1706
|
+
if (outcome.success && outcome.result) {
|
|
1707
|
+
runtimeManager.update(blockId, {
|
|
1708
|
+
state: 'completed',
|
|
1709
|
+
output: outcome.result.payload,
|
|
1710
|
+
executedByDid: actorDid,
|
|
1711
|
+
executedAt: Date.now(),
|
|
1712
|
+
});
|
|
1713
|
+
}
|
|
1714
|
+
else if (!outcome.success) {
|
|
1715
|
+
runtimeManager.update(blockId, {
|
|
1716
|
+
state: 'failed',
|
|
1717
|
+
error: {
|
|
1718
|
+
message: outcome.error ?? 'Unknown error',
|
|
1719
|
+
at: Date.now(),
|
|
1720
|
+
},
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
// Include the final runtime state so the agent doesn't need a separate read
|
|
1724
|
+
const finalRuntime = runtimeManager.get(blockId);
|
|
1725
|
+
return JSON.stringify({
|
|
1726
|
+
success: outcome.success,
|
|
1727
|
+
blockId,
|
|
1728
|
+
actionType,
|
|
1729
|
+
stage: outcome.stage,
|
|
1730
|
+
message: outcome.success
|
|
1731
|
+
? `Action ${actionType} completed successfully`
|
|
1732
|
+
: `Action ${actionType} failed at stage: ${outcome.stage}`,
|
|
1733
|
+
...(outcome.error && { error: outcome.error }),
|
|
1734
|
+
...(outcome.result && { result: outcome.result }),
|
|
1735
|
+
runtimeState: finalRuntime,
|
|
1736
|
+
});
|
|
1737
|
+
}
|
|
1738
|
+
catch (error) {
|
|
1739
|
+
Logger.error('Error executing action:', error);
|
|
1740
|
+
return JSON.stringify({
|
|
1741
|
+
success: false,
|
|
1742
|
+
blockId,
|
|
1743
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1744
|
+
});
|
|
1745
|
+
}
|
|
1746
|
+
finally {
|
|
1747
|
+
await providerManager.dispose();
|
|
1748
|
+
}
|
|
1749
|
+
}, {
|
|
1750
|
+
name: 'execute_action',
|
|
1751
|
+
description: `Executes an action block through the flow engine pipeline.
|
|
1752
|
+
|
|
1753
|
+
**Flow engine gates:** activation → authorization → execution → runtime state update
|
|
1754
|
+
|
|
1755
|
+
**Supported actions:** http.request, email.send, notification.push, human.checkbox.set, form.submit, protocol.select
|
|
1756
|
+
|
|
1757
|
+
**Usage:**
|
|
1758
|
+
- Pass the blockId of an action block (a block with an \`actionType\` property)
|
|
1759
|
+
- Optionally provide inputOverrides to override/supplement the block's stored inputs
|
|
1760
|
+
- The tool resolves \`{{blockId.prop}}\` references in inputs automatically
|
|
1761
|
+
- Returns the execution outcome including success/failure, stage reached, and result data
|
|
1762
|
+
|
|
1763
|
+
**Example:**
|
|
1764
|
+
\`\`\`json
|
|
1765
|
+
{"blockId": "550e8400-e29b-41d4-a716-446655440000"}
|
|
1766
|
+
\`\`\`
|
|
1767
|
+
|
|
1768
|
+
**With input overrides:**
|
|
1769
|
+
\`\`\`json
|
|
1770
|
+
{"blockId": "550e8400-e29b-41d4-a716-446655440000", "inputOverrides": {"url": "https://api.example.com/data"}}
|
|
1771
|
+
\`\`\`
|
|
1772
|
+
|
|
1773
|
+
**Returns:**
|
|
1774
|
+
\`\`\`json
|
|
1775
|
+
{
|
|
1776
|
+
"success": true,
|
|
1777
|
+
"stage": "execution",
|
|
1778
|
+
"result": {"status": 200, "data": {...}},
|
|
1779
|
+
"blockId": "...",
|
|
1780
|
+
"actionType": "http.request"
|
|
1781
|
+
}
|
|
1782
|
+
\`\`\``,
|
|
1783
|
+
schema: z.object({
|
|
1784
|
+
blockId: z
|
|
1785
|
+
.string()
|
|
1786
|
+
.describe('The exact ID of the action block to execute (must have actionType property)'),
|
|
1787
|
+
inputOverrides: z
|
|
1788
|
+
.record(z.any(), z.any())
|
|
1789
|
+
.optional()
|
|
1790
|
+
.default({})
|
|
1791
|
+
.describe('Optional: override or supplement the block\'s stored inputs. Example: {"url": "https://..."}'),
|
|
1792
|
+
}),
|
|
1793
|
+
});
|
|
1794
|
+
// ============================================================================
|
|
1795
|
+
// Tool 15: Find and Replace
|
|
1796
|
+
// ============================================================================
|
|
1797
|
+
const findAndReplaceTool = tool(async ({ searchText, replaceText, caseSensitive = true, replaceAll = true, }) => {
|
|
1798
|
+
Logger.log(`🔄 find_and_replace tool invoked: "${searchText}" → "${replaceText}"`);
|
|
1799
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
1800
|
+
if (!isInRoom) {
|
|
1801
|
+
return JSON.stringify({
|
|
1802
|
+
success: false,
|
|
1803
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
1804
|
+
});
|
|
1805
|
+
}
|
|
1806
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
1807
|
+
try {
|
|
1808
|
+
const { doc } = await providerManager.init();
|
|
1809
|
+
// Try the original search text first
|
|
1810
|
+
let result = findAndReplaceInDoc(doc, {
|
|
1811
|
+
searchText,
|
|
1812
|
+
replaceText,
|
|
1813
|
+
caseSensitive,
|
|
1814
|
+
replaceAll,
|
|
1815
|
+
docName: 'document',
|
|
1816
|
+
});
|
|
1817
|
+
// If no matches, try the emoji-normalised form (shortcode → emoji or emoji → shortcode)
|
|
1818
|
+
if (!result.success && textContainsEmoji(searchText)) {
|
|
1819
|
+
const emojified = emojify(searchText);
|
|
1820
|
+
const unemojified = unemojify(searchText);
|
|
1821
|
+
const altSearch = emojified !== searchText ? emojified : unemojified;
|
|
1822
|
+
if (altSearch !== searchText) {
|
|
1823
|
+
result = findAndReplaceInDoc(doc, {
|
|
1824
|
+
searchText: altSearch,
|
|
1825
|
+
replaceText,
|
|
1826
|
+
caseSensitive,
|
|
1827
|
+
replaceAll,
|
|
1828
|
+
docName: 'document',
|
|
1829
|
+
});
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
return JSON.stringify({
|
|
1833
|
+
success: result.success,
|
|
1834
|
+
message: result.success
|
|
1835
|
+
? `Replaced ${result.replacementCount} occurrence(s) across ${result.affectedBlockIds.length} block(s)`
|
|
1836
|
+
: `No occurrences of "${searchText}" found`,
|
|
1837
|
+
replacementCount: result.replacementCount,
|
|
1838
|
+
affectedBlockIds: result.affectedBlockIds,
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
catch (error) {
|
|
1842
|
+
Logger.error('Error in find and replace:', error);
|
|
1843
|
+
return JSON.stringify({
|
|
1844
|
+
success: false,
|
|
1845
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
finally {
|
|
1849
|
+
await providerManager.dispose();
|
|
1850
|
+
}
|
|
1851
|
+
}, {
|
|
1852
|
+
name: 'find_and_replace',
|
|
1853
|
+
description: `Finds and replaces text across all blocks in the document. All replacements happen atomically in a single transaction.
|
|
1854
|
+
|
|
1855
|
+
**Examples:**
|
|
1856
|
+
- Replace all: \`{"searchText": "old text", "replaceText": "new text"}\`
|
|
1857
|
+
- Case-insensitive: \`{"searchText": "OLD", "replaceText": "new", "caseSensitive": false}\`
|
|
1858
|
+
- Replace first only: \`{"searchText": "duplicate", "replaceText": "unique", "replaceAll": false}\`
|
|
1859
|
+
|
|
1860
|
+
**Returns:** Count of replacements and IDs of affected blocks.`,
|
|
1861
|
+
schema: z.object({
|
|
1862
|
+
searchText: z.string().describe('The text to search for'),
|
|
1863
|
+
replaceText: z.string().describe('The text to replace matches with'),
|
|
1864
|
+
caseSensitive: z
|
|
1865
|
+
.boolean()
|
|
1866
|
+
.optional()
|
|
1867
|
+
.default(true)
|
|
1868
|
+
.describe('Whether the search is case-sensitive (default: true)'),
|
|
1869
|
+
replaceAll: z
|
|
1870
|
+
.boolean()
|
|
1871
|
+
.optional()
|
|
1872
|
+
.default(true)
|
|
1873
|
+
.describe('Whether to replace all occurrences or just the first (default: true)'),
|
|
1874
|
+
}),
|
|
1875
|
+
});
|
|
1876
|
+
// ============================================================================
|
|
1877
|
+
// Tool 16: Move Block
|
|
1878
|
+
// ============================================================================
|
|
1879
|
+
const moveBlockTool = tool(async ({ blockId, referenceBlockId, placement }) => {
|
|
1880
|
+
Logger.log(`↕️ move_block tool invoked: move ${blockId} ${placement} ${referenceBlockId}`);
|
|
1881
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
1882
|
+
if (!isInRoom) {
|
|
1883
|
+
return JSON.stringify({
|
|
1884
|
+
success: false,
|
|
1885
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
1889
|
+
try {
|
|
1890
|
+
const { doc } = await providerManager.init();
|
|
1891
|
+
const snapshot = moveBlock(doc, {
|
|
1892
|
+
blockId,
|
|
1893
|
+
referenceBlockId,
|
|
1894
|
+
placement,
|
|
1895
|
+
docName: 'document',
|
|
1896
|
+
});
|
|
1897
|
+
const movedBlock = getBlockDetail(doc, snapshot.id, true);
|
|
1898
|
+
const simplified = movedBlock
|
|
1899
|
+
? simplifyBlockForAgent(movedBlock)
|
|
1900
|
+
: null;
|
|
1901
|
+
// Get new position
|
|
1902
|
+
const fragment = doc.getXmlFragment('document');
|
|
1903
|
+
const allBlocks = collectAllBlocks(fragment);
|
|
1904
|
+
const newPosition = allBlocks.findIndex((b) => b.id === blockId);
|
|
1905
|
+
return JSON.stringify({
|
|
1906
|
+
success: true,
|
|
1907
|
+
blockId,
|
|
1908
|
+
blockType: simplified?.type,
|
|
1909
|
+
newPosition: newPosition >= 0 ? newPosition : undefined,
|
|
1910
|
+
message: `Moved ${simplified?.type || 'block'} ${placement} block ${referenceBlockId}`,
|
|
1911
|
+
block: simplified || snapshot,
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
catch (error) {
|
|
1915
|
+
Logger.error('Error moving block:', error);
|
|
1916
|
+
return JSON.stringify({
|
|
1917
|
+
success: false,
|
|
1918
|
+
blockId,
|
|
1919
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1922
|
+
finally {
|
|
1923
|
+
await providerManager.dispose();
|
|
1924
|
+
}
|
|
1925
|
+
}, {
|
|
1926
|
+
name: 'move_block',
|
|
1927
|
+
description: `Moves a block to a new position relative to another block. Preserves block ID, content, and runtime state.
|
|
1928
|
+
|
|
1929
|
+
**Usage:**
|
|
1930
|
+
1. Call \`list_blocks\` to get block IDs
|
|
1931
|
+
2. Specify the block to move and the reference block
|
|
1932
|
+
|
|
1933
|
+
**Example:**
|
|
1934
|
+
\`{"blockId": "uuid-to-move", "referenceBlockId": "uuid-target", "placement": "before"}\``,
|
|
1935
|
+
schema: z.object({
|
|
1936
|
+
blockId: z.string().describe('The ID of the block to move'),
|
|
1937
|
+
referenceBlockId: z
|
|
1938
|
+
.string()
|
|
1939
|
+
.describe('The ID of the block to position relative to'),
|
|
1940
|
+
placement: z
|
|
1941
|
+
.enum(['before', 'after'])
|
|
1942
|
+
.describe('Place the moved block "before" or "after" the reference block'),
|
|
1943
|
+
}),
|
|
1944
|
+
});
|
|
1945
|
+
// ============================================================================
|
|
1946
|
+
// Tool 17: Bulk Edit Blocks
|
|
1947
|
+
// ============================================================================
|
|
1948
|
+
const bulkEditBlocksTool = tool(async ({ edits }) => {
|
|
1949
|
+
Logger.log(`📦 bulk_edit_blocks tool invoked for ${edits.length} edit(s)`);
|
|
1950
|
+
const isInRoom = await checkIfInRoomAndJoinPublicRoom(matrixClient, roomId);
|
|
1951
|
+
if (!isInRoom) {
|
|
1952
|
+
return JSON.stringify({
|
|
1953
|
+
success: false,
|
|
1954
|
+
error: `Companion is not in the room ${roomId}, please invite companion to the room. companion user id: ${matrixClient.getUserId()}`,
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
const providerManager = new MatrixProviderManager(matrixClient, config);
|
|
1958
|
+
try {
|
|
1959
|
+
const { doc } = await providerManager.init();
|
|
1960
|
+
const results = [];
|
|
1961
|
+
doc.transact(() => {
|
|
1962
|
+
for (const edit of edits) {
|
|
1963
|
+
try {
|
|
1964
|
+
// Apply property updates
|
|
1965
|
+
if (edit.updates &&
|
|
1966
|
+
Object.keys(edit.updates).length > 0) {
|
|
1967
|
+
const attributes = {
|
|
1968
|
+
props: edit.updates,
|
|
1969
|
+
};
|
|
1970
|
+
editBlock(doc, {
|
|
1971
|
+
blockId: edit.blockId,
|
|
1972
|
+
attributes,
|
|
1973
|
+
docName: 'document',
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
// Apply text update
|
|
1977
|
+
if (typeof edit.text === 'string') {
|
|
1978
|
+
editBlock(doc, {
|
|
1979
|
+
blockId: edit.blockId,
|
|
1980
|
+
text: edit.text,
|
|
1981
|
+
docName: 'document',
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
// Apply runtime updates
|
|
1985
|
+
if (edit.runtimeUpdates &&
|
|
1986
|
+
Object.keys(edit.runtimeUpdates)
|
|
1987
|
+
.length > 0) {
|
|
1988
|
+
updateRuntimeState(doc, edit.blockId, edit.runtimeUpdates);
|
|
1989
|
+
}
|
|
1990
|
+
// Track what was updated per edit
|
|
1991
|
+
const updatedFields = [];
|
|
1992
|
+
if (edit.updates)
|
|
1993
|
+
updatedFields.push(...Object.keys(edit.updates));
|
|
1994
|
+
if (typeof edit.text === 'string')
|
|
1995
|
+
updatedFields.push('text');
|
|
1996
|
+
if (edit.runtimeUpdates)
|
|
1997
|
+
updatedFields.push(...Object.keys(edit.runtimeUpdates).map((k) => `runtime.${k}`));
|
|
1998
|
+
results.push({
|
|
1999
|
+
blockId: edit.blockId,
|
|
2000
|
+
success: true,
|
|
2001
|
+
updatedFields,
|
|
2002
|
+
});
|
|
2003
|
+
}
|
|
2004
|
+
catch (error) {
|
|
2005
|
+
results.push({
|
|
2006
|
+
blockId: edit.blockId,
|
|
2007
|
+
success: false,
|
|
2008
|
+
updatedFields: [],
|
|
2009
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
}, 'blocknote-crdt-playground');
|
|
2014
|
+
const successCount = results.filter((r) => r.success).length;
|
|
2015
|
+
const failCount = results.filter((r) => !r.success).length;
|
|
2016
|
+
return JSON.stringify({
|
|
2017
|
+
success: failCount === 0,
|
|
2018
|
+
message: `${successCount}/${edits.length} edit(s) succeeded${failCount > 0 ? `, ${failCount} failed` : ''}`,
|
|
2019
|
+
totalEdits: edits.length,
|
|
2020
|
+
successCount,
|
|
2021
|
+
failCount,
|
|
2022
|
+
results,
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
catch (error) {
|
|
2026
|
+
Logger.error('Error in bulk edit:', error);
|
|
2027
|
+
return JSON.stringify({
|
|
2028
|
+
success: false,
|
|
2029
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2030
|
+
});
|
|
2031
|
+
}
|
|
2032
|
+
finally {
|
|
2033
|
+
await providerManager.dispose();
|
|
2034
|
+
}
|
|
2035
|
+
}, {
|
|
2036
|
+
name: 'bulk_edit_blocks',
|
|
2037
|
+
description: `Edits multiple blocks in a single atomic transaction. Much more efficient than calling edit_block multiple times — uses one provider init/dispose cycle and one Y.js transaction.
|
|
2038
|
+
|
|
2039
|
+
**Usage:**
|
|
2040
|
+
\`\`\`json
|
|
2041
|
+
{
|
|
2042
|
+
"edits": [
|
|
2043
|
+
{"blockId": "uuid-1", "updates": {"status": "open"}},
|
|
2044
|
+
{"blockId": "uuid-2", "text": "Updated text"},
|
|
2045
|
+
{"blockId": "uuid-3", "updates": {"title": "New"}, "runtimeUpdates": {"state": "completed"}}
|
|
2046
|
+
]
|
|
2047
|
+
}
|
|
2048
|
+
\`\`\`
|
|
2049
|
+
|
|
2050
|
+
**Features:**
|
|
2051
|
+
- Single transaction for all edits (atomic)
|
|
2052
|
+
- Partial success allowed — individual failures don't block other edits
|
|
2053
|
+
- Each edit can include: \`updates\` (properties), \`text\`, \`runtimeUpdates\`
|
|
2054
|
+
|
|
2055
|
+
**Returns:** Per-edit results with success/failure status.`,
|
|
2056
|
+
schema: z.object({
|
|
2057
|
+
edits: z
|
|
2058
|
+
.array(z.object({
|
|
2059
|
+
blockId: z.string().describe('The ID of the block to edit'),
|
|
2060
|
+
updates: z
|
|
2061
|
+
.record(z.any(), z.any())
|
|
2062
|
+
.optional()
|
|
2063
|
+
.describe('Property updates as key-value pairs'),
|
|
2064
|
+
text: z
|
|
2065
|
+
.string()
|
|
2066
|
+
.optional()
|
|
2067
|
+
.describe('New text content for the block'),
|
|
2068
|
+
runtimeUpdates: z
|
|
2069
|
+
.record(z.any(), z.any())
|
|
2070
|
+
.optional()
|
|
2071
|
+
.describe('Runtime state updates to merge'),
|
|
2072
|
+
}))
|
|
2073
|
+
.describe('Array of block edits to apply'),
|
|
2074
|
+
}),
|
|
2075
|
+
});
|
|
2076
|
+
// ============================================================================
|
|
2077
|
+
// Return tools based on mode
|
|
2078
|
+
// ============================================================================
|
|
2079
|
+
if (readOnly) {
|
|
2080
|
+
return {
|
|
2081
|
+
listBlocksTool,
|
|
2082
|
+
readBlockByIdTool,
|
|
2083
|
+
searchBlocksTool,
|
|
2084
|
+
readFlowContextTool,
|
|
2085
|
+
readFlowStatusTool,
|
|
2086
|
+
readBlockHistoryTool,
|
|
2087
|
+
readPermissionsTool,
|
|
2088
|
+
readSurveyTool,
|
|
2089
|
+
validateSurveyAnswersTool,
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
return {
|
|
2093
|
+
listBlocksTool,
|
|
2094
|
+
editBlockTool,
|
|
2095
|
+
createBlockTool,
|
|
2096
|
+
deleteBlockTool,
|
|
2097
|
+
readBlockByIdTool,
|
|
2098
|
+
searchBlocksTool,
|
|
2099
|
+
readFlowContextTool,
|
|
2100
|
+
readFlowStatusTool,
|
|
2101
|
+
readBlockHistoryTool,
|
|
2102
|
+
readPermissionsTool,
|
|
2103
|
+
readSurveyTool,
|
|
2104
|
+
fillSurveyAnswersTool,
|
|
2105
|
+
validateSurveyAnswersTool,
|
|
2106
|
+
executeActionTool,
|
|
2107
|
+
findAndReplaceTool,
|
|
2108
|
+
moveBlockTool,
|
|
2109
|
+
bulkEditBlocksTool,
|
|
2110
|
+
};
|
|
2111
|
+
};
|
|
2112
|
+
const checkIfInRoomAndJoinPublicRoom = async (matrixClient, roomId) => {
|
|
2113
|
+
const joinRuleEvent = await matrixClient.getStateEvent(roomId, 'm.room.join_rules', '');
|
|
2114
|
+
const joinRule = joinRuleEvent.join_rule;
|
|
2115
|
+
const isPublicRoom = joinRule === 'public';
|
|
2116
|
+
const isInRoom = matrixClient.getRoom(roomId)?.getMember(matrixClient.getUserId() ?? '')
|
|
2117
|
+
?.membership === 'join';
|
|
2118
|
+
if (!isPublicRoom && !isInRoom) {
|
|
2119
|
+
await matrixClient.joinRoom(roomId);
|
|
2120
|
+
Logger.log(`Joined room ${roomId}`);
|
|
2121
|
+
}
|
|
2122
|
+
return isInRoom;
|
|
2123
|
+
};
|