@ixo/oracle-runtime-workers 0.2.0
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/README.md +110 -0
- package/package.json +77 -0
- package/src/attachments/classify.ts +114 -0
- package/src/attachments/content-blocks.ts +51 -0
- package/src/attachments/download.ts +235 -0
- package/src/attachments/extract.ts +241 -0
- package/src/attachments/index.ts +84 -0
- package/src/attachments/magic.ts +133 -0
- package/src/attachments/pipeline.ts +409 -0
- package/src/attachments/retention.ts +205 -0
- package/src/attachments/route.ts +41 -0
- package/src/attachments/sandbox-archive.ts +90 -0
- package/src/attachments/sanitize.ts +35 -0
- package/src/attachments/types.ts +86 -0
- package/src/attachments/view.ts +107 -0
- package/src/core/env.ts +200 -0
- package/src/core/index.ts +513 -0
- package/src/core/llm.ts +842 -0
- package/src/core/main-agent-types.ts +123 -0
- package/src/core/main-agent.ts +399 -0
- package/src/core/manifest.ts +365 -0
- package/src/core/meta-tools.ts +232 -0
- package/src/core/middlewares/byo-history-sanitizer.ts +165 -0
- package/src/core/middlewares/capability-gate.ts +76 -0
- package/src/core/middlewares/index.ts +18 -0
- package/src/core/middlewares/page-context.ts +98 -0
- package/src/core/middlewares/safety-guardrail.ts +117 -0
- package/src/core/middlewares/summarization.ts +99 -0
- package/src/core/middlewares/tool-repetition-guard.ts +125 -0
- package/src/core/middlewares/tool-validation.ts +83 -0
- package/src/core/middlewares/work-status.ts +57 -0
- package/src/core/openrouter-pricing.ts +124 -0
- package/src/core/plugin-loader.ts +192 -0
- package/src/core/plugins/index.ts +6 -0
- package/src/core/plugins/skills/index.ts +8 -0
- package/src/core/plugins/skills/skills-tools.ts +356 -0
- package/src/core/plugins/skills/skills.plugin.ts +113 -0
- package/src/core/plugins/weather/index.ts +8 -0
- package/src/core/plugins/weather/weather-client.ts +153 -0
- package/src/core/plugins/weather/weather-middleware.ts +27 -0
- package/src/core/plugins/weather/weather-sub-agent.ts +65 -0
- package/src/core/plugins/weather/weather-tools.ts +103 -0
- package/src/core/plugins/weather/weather.plugin.ts +227 -0
- package/src/core/prompt-composer.ts +469 -0
- package/src/core/registries.ts +642 -0
- package/src/core/runtime-context.ts +619 -0
- package/src/core/state.ts +108 -0
- package/src/core/sub-agent-fallback.ts +187 -0
- package/src/core/subagent-as-tool.ts +322 -0
- package/src/core/template.ts +245 -0
- package/src/core/utils.ts +246 -0
- package/src/core/wrap-plugin-tool.ts +100 -0
- package/src/do/ambient.ts +266 -0
- package/src/do/contracts.ts +562 -0
- package/src/do/debug-timers.ts +120 -0
- package/src/do/fresh-stub.ts +32 -0
- package/src/do/gateway-retry.ts +73 -0
- package/src/do/secrets-adapter.ts +32 -0
- package/src/do/size-alerts.ts +76 -0
- package/src/do/sse-stream.ts +472 -0
- package/src/do/transcript.ts +192 -0
- package/src/do/turn-body.ts +84 -0
- package/src/do/turn-metadata.ts +146 -0
- package/src/do/ucan-service.ts +339 -0
- package/src/do/user-oracle-do.ts +3035 -0
- package/src/gateway-worker.ts +82 -0
- package/src/index.ts +237 -0
- package/src/llm/byo-adapter.ts +59 -0
- package/src/llm/byo-catalog.ts +520 -0
- package/src/llm/byo-client.ts +280 -0
- package/src/llm/byo-routes.ts +255 -0
- package/src/llm/byo-service.ts +836 -0
- package/src/llm/chatgpt-oauth.ts +430 -0
- package/src/llm/provider-error.ts +283 -0
- package/src/matrix/gateway-do.ts +918 -0
- package/src/matrix/ingest.ts +209 -0
- package/src/matrix/replay-format.ts +34 -0
- package/src/matrix/reply-chain.ts +109 -0
- package/src/matrix/room-state-codec.ts +115 -0
- package/src/matrix/user-homeserver.ts +106 -0
- package/src/matrix/work-status.ts +294 -0
- package/src/memory/session-history-indexer.ts +328 -0
- package/src/owner-store/ixo-vfs-store.ts +570 -0
- package/src/owner-store/matrix-media-store.ts +136 -0
- package/src/owner-store/migrating-store.ts +185 -0
- package/src/owner-store/owner-copy-errors.ts +175 -0
- package/src/owner-store/retry.ts +45 -0
- package/src/owner-store/tus-upload.ts +333 -0
- package/src/owner-store/types.ts +0 -0
- package/src/plugin-api/define-plugin.ts +45 -0
- package/src/plugin-api/index.ts +56 -0
- package/src/plugin-api/oracle-plugin.ts +145 -0
- package/src/plugin-api/tool-helper.ts +82 -0
- package/src/plugin-api/types.ts +816 -0
- package/src/plugin-api/ucan-errors.ts +16 -0
- package/src/plugins/agui/agui-agent.ts +115 -0
- package/src/plugins/agui/agui.plugin.ts +148 -0
- package/src/plugins/agui/index.ts +2 -0
- package/src/plugins/attachments/attachments.plugin.ts +54 -0
- package/src/plugins/attachments/index.ts +6 -0
- package/src/plugins/attachments/view-attachment-tool.ts +91 -0
- package/src/plugins/base64.ts +26 -0
- package/src/plugins/composio/composio-tools.ts +365 -0
- package/src/plugins/composio/composio-ucan.ts +43 -0
- package/src/plugins/composio/composio.plugin.ts +214 -0
- package/src/plugins/composio/index.ts +15 -0
- package/src/plugins/domain-indexer/domain-indexer-agent.ts +57 -0
- package/src/plugins/domain-indexer/domain-indexer-tools.ts +253 -0
- package/src/plugins/domain-indexer/domain-indexer.plugin.ts +100 -0
- package/src/plugins/domain-indexer/index.ts +3 -0
- package/src/plugins/editor/blocknote-bridge.ts +120 -0
- package/src/plugins/editor/content-session.ts +331 -0
- package/src/plugins/editor/content-tools.ts +686 -0
- package/src/plugins/editor/document-model.ts +0 -0
- package/src/plugins/editor/editor-access-denied-tool.ts +58 -0
- package/src/plugins/editor/editor-agent.ts +34 -0
- package/src/plugins/editor/editor-config.ts +64 -0
- package/src/plugins/editor/editor-mx.ts +128 -0
- package/src/plugins/editor/editor.plugin.ts +143 -0
- package/src/plugins/editor/failures.ts +154 -0
- package/src/plugins/editor/index.ts +31 -0
- package/src/plugins/editor/jsdom-shim.ts +55 -0
- package/src/plugins/editor/prompts.ts +59 -0
- package/src/plugins/editor/prop-policy.ts +147 -0
- package/src/plugins/editor/provider.ts +386 -0
- package/src/plugins/editor/room-membership.ts +95 -0
- package/src/plugins/editor/standalone-editor-tool.ts +200 -0
- package/src/plugins/firecrawl/firecrawl-agent.ts +93 -0
- package/src/plugins/firecrawl/firecrawl-tools.ts +220 -0
- package/src/plugins/firecrawl/firecrawl.plugin.ts +105 -0
- package/src/plugins/firecrawl/index.ts +15 -0
- package/src/plugins/flows/action-metadata.ts +3505 -0
- package/src/plugins/flows/actions.ts +190 -0
- package/src/plugins/flows/edit.ts +314 -0
- package/src/plugins/flows/errors.ts +38 -0
- package/src/plugins/flows/explain.ts +43 -0
- package/src/plugins/flows/flow-doc.ts +111 -0
- package/src/plugins/flows/flows.plugin.ts +129 -0
- package/src/plugins/flows/forms.ts +330 -0
- package/src/plugins/flows/index.ts +2 -0
- package/src/plugins/flows/linkage.ts +147 -0
- package/src/plugins/flows/port-types.ts +53 -0
- package/src/plugins/flows/prompts.ts +65 -0
- package/src/plugins/flows/read.ts +392 -0
- package/src/plugins/flows/references.ts +66 -0
- package/src/plugins/flows/test-support.ts +165 -0
- package/src/plugins/flows/tools/authoring.ts +516 -0
- package/src/plugins/flows/tools/discovery.ts +119 -0
- package/src/plugins/flows/tools/forms.ts +142 -0
- package/src/plugins/flows/tools/inspect.ts +165 -0
- package/src/plugins/flows/tools/linkage.ts +117 -0
- package/src/plugins/flows/tools/settings.ts +184 -0
- package/src/plugins/flows/translator.ts +328 -0
- package/src/plugins/flows/types.ts +210 -0
- package/src/plugins/flows/ydoc-helpers.ts +543 -0
- package/src/plugins/index.ts +70 -0
- package/src/plugins/mcp-call-timeout.ts +38 -0
- package/src/plugins/mcp-tool-adapter.ts +87 -0
- package/src/plugins/memory/index.ts +17 -0
- package/src/plugins/memory/memory-tools.ts +463 -0
- package/src/plugins/memory/memory-ucan.ts +51 -0
- package/src/plugins/memory/memory.plugin.ts +122 -0
- package/src/plugins/memory/types.ts +147 -0
- package/src/plugins/portal/action-log.ts +34 -0
- package/src/plugins/portal/index.ts +2 -0
- package/src/plugins/portal/portal.plugin.ts +203 -0
- package/src/plugins/sandbox/index.ts +34 -0
- package/src/plugins/sandbox/sandbox-bridge.ts +295 -0
- package/src/plugins/sandbox/sandbox-mcp.ts +127 -0
- package/src/plugins/sandbox/sandbox-write-blob.ts +103 -0
- package/src/plugins/sandbox/sandbox.plugin.ts +599 -0
- package/src/plugins/tasks/index.ts +17 -0
- package/src/plugins/tasks/manifest.ts +81 -0
- package/src/plugins/tasks/middleware.ts +219 -0
- package/src/plugins/tasks/tasks-tools.ts +414 -0
- package/src/plugins/tasks/tasks.plugin.ts +74 -0
- package/src/plugins/ucan-failure.ts +133 -0
- package/src/plugins/user-preferences/index.ts +22 -0
- package/src/plugins/user-preferences/schema.ts +31 -0
- package/src/plugins/user-preferences/user-preferences-store.ts +172 -0
- package/src/plugins/user-preferences/user-preferences-tool.ts +131 -0
- package/src/plugins/user-preferences/user-preferences.plugin.ts +137 -0
- package/src/plugins/vfs/index.ts +40 -0
- package/src/plugins/vfs/vfs-auth.ts +56 -0
- package/src/plugins/vfs/vfs-client.ts +655 -0
- package/src/plugins/vfs/vfs-content.ts +181 -0
- package/src/plugins/vfs/vfs-errors.ts +163 -0
- package/src/plugins/vfs/vfs-sandbox-tools.ts +272 -0
- package/src/plugins/vfs/vfs-tools.ts +635 -0
- package/src/plugins/vfs/vfs.plugin.ts +201 -0
- package/src/realtime/frontend-call-registry.ts +163 -0
- package/src/realtime/realtime-endpoint.ts +619 -0
- package/src/realtime/session-socket-hub.ts +249 -0
- package/src/realtime/socket-io-codec.ts +187 -0
- package/src/secrets/jwe.ts +74 -0
- package/src/secrets/pin-cipher.ts +43 -0
- package/src/secrets/secrets-service.ts +319 -0
- package/src/shell/app.ts +564 -0
- package/src/shell/auth.ts +290 -0
- package/src/sqlite/blob-codec.ts +100 -0
- package/src/sqlite/blob-compactor.ts +146 -0
- package/src/sqlite/cache-config.ts +45 -0
- package/src/sqlite/database.ts +643 -0
- package/src/sqlite/do-vfs.ts +1485 -0
- package/src/sqlite/index.ts +61 -0
- package/src/sqlite/serialization.ts +370 -0
- package/src/sqlite/sessions-store.ts +311 -0
- package/src/sqlite/sqlite-saver.ts +919 -0
- package/src/sqlite/vacuum-policy.ts +77 -0
- package/src/sqlite/wa-sqlite-loader.ts +123 -0
- package/src/sqlite/wasm.d.ts +10 -0
- package/src/tasks/schedule.ts +174 -0
- package/src/tasks/scheduler.ts +753 -0
- package/src/tasks/spec.ts +125 -0
- package/src/tasks/store.ts +351 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { tool } from '../../plugin-api/tool-helper';
|
|
3
|
+
import type { PluginTool } from '../../plugin-api/types';
|
|
4
|
+
|
|
5
|
+
const searchSchema = z.object({
|
|
6
|
+
query: z
|
|
7
|
+
.string()
|
|
8
|
+
.min(
|
|
9
|
+
1,
|
|
10
|
+
'❌ ERROR: Query is required and cannot be empty - this tool works like Google search, you MUST provide a search query',
|
|
11
|
+
)
|
|
12
|
+
.describe(
|
|
13
|
+
'🚨 MANDATORY PARAMETER - ALWAYS REQUIRED\n' +
|
|
14
|
+
'This tool works like Google search - you MUST provide a search query to find entities.\n' +
|
|
15
|
+
'The search query string. Cannot be empty or omitted. This is like typing into Google search.\n' +
|
|
16
|
+
'Examples:\n' +
|
|
17
|
+
'- User asks "What is IXO?" → Use query: "IXO"\n' +
|
|
18
|
+
'- User asks "Tell me about blockchain projects" → Use query: "blockchain projects"\n' +
|
|
19
|
+
'- User asks "What are carbon credits?" → Use query: "carbon credits"\n' +
|
|
20
|
+
"Extract the main search term from the user's question and use it as the query.\n" +
|
|
21
|
+
'This parameter is mandatory - you cannot search without a query, just like you cannot Google search without typing something.',
|
|
22
|
+
),
|
|
23
|
+
limit: z
|
|
24
|
+
.number()
|
|
25
|
+
.min(1)
|
|
26
|
+
.max(10)
|
|
27
|
+
.optional()
|
|
28
|
+
.describe(
|
|
29
|
+
'Optional: Maximum number of results to return (1-10, default: 10)',
|
|
30
|
+
),
|
|
31
|
+
scopes: z
|
|
32
|
+
.string()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe(
|
|
35
|
+
'Optional: Comma-separated list of scopes to search: domain_cards (default), agents, compositions, events. Use "domain_cards" to find organizations/projects/DAOs with summaries and FAQs.',
|
|
36
|
+
),
|
|
37
|
+
filters: z.record(z.string(), z.string()).optional()
|
|
38
|
+
.describe(`Optional filters as key-value pairs. Examples:
|
|
39
|
+
- {"dc.entity_type": "dao/pod"} - Filter by exact chain entity type (e.g. dao/pod, dao/protocol, dao — compound types are preserved, not split)
|
|
40
|
+
- {"dc.entity_verified": "true"} - Only verified entities (chain-sourced: true/false/omit for all)
|
|
41
|
+
- {"dc.categories": "dao,project"} - Filter by categories
|
|
42
|
+
- {"dc.has_url": "true"} - Only entities with URLs
|
|
43
|
+
- {"dc.keywords": "blockchain,web3"} - Filter by keywords
|
|
44
|
+
- {"agent.domain_card_id": "did:ixo:entity:ixoworld"} - Agents for specific entity
|
|
45
|
+
For multiple values, use comma-separated strings. For booleans, use "true" or "false". For dates, use ISO format.`),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const cardSchema = z.object({
|
|
49
|
+
did: z
|
|
50
|
+
.string()
|
|
51
|
+
.min(1, '❌ ERROR: DID is required and cannot be empty')
|
|
52
|
+
.describe(
|
|
53
|
+
'REQUIRED: Domain card DID (Decentralized Identifier). Must be a non-empty string. Get this from search results (record.id) or user input. Example: "did:ixo:entity:ixoworld". This parameter is mandatory and cannot be omitted.',
|
|
54
|
+
),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const SEARCH_DESCRIPTION = `🔍 This is like Google Search for entities and domains in the IXO ecosystem. You search using a query and you will find relevant entities.
|
|
58
|
+
|
|
59
|
+
Think of this tool exactly like Google Search:
|
|
60
|
+
- Google Search: You type a query → You get search results
|
|
61
|
+
- Domain Indexer: You provide a query → You get entity results
|
|
62
|
+
|
|
63
|
+
🚨 CRITICAL REQUIREMENT: You MUST provide the 'query' parameter. This tool works like Google search - you CANNOT search without a query. NEVER call this tool with empty arguments {}.
|
|
64
|
+
|
|
65
|
+
✅ CORRECT EXAMPLES (like Google search):
|
|
66
|
+
{"query": "IXO"}
|
|
67
|
+
{"query": "blockchain impact"}
|
|
68
|
+
{"query": "carbon credits", "limit": 5}
|
|
69
|
+
{"query": "What is IXO?", "scopes": "domain_cards"}
|
|
70
|
+
|
|
71
|
+
❌ WRONG (WILL FAIL - like trying to Google search with no query):
|
|
72
|
+
{}
|
|
73
|
+
{"limit": 5}
|
|
74
|
+
{"scopes": "domain_cards"}
|
|
75
|
+
|
|
76
|
+
The 'query' parameter is MANDATORY in every call - just like you can't Google search without typing something.
|
|
77
|
+
|
|
78
|
+
WHAT IT DOES:
|
|
79
|
+
- Searches the IXO domain indexer to find entities (organizations, projects, DAOs, agents, compositions, events)
|
|
80
|
+
- Returns summaries, overviews, and FAQs for matching entities
|
|
81
|
+
- Works like Google search: you provide a search query and get relevant results
|
|
82
|
+
|
|
83
|
+
PRIMARY USE CASES:
|
|
84
|
+
- When users ask "What is X?" or "Tell me about X" → Use query: "X" to find the entity
|
|
85
|
+
- When users ask "What are the FAQs for X?" → Use query: "X" to find the entity and access its FAQ section
|
|
86
|
+
- When users need information about an organization/project → Use query with the entity name or topic
|
|
87
|
+
- When users ask about entities in the IXO ecosystem → Use query with relevant keywords
|
|
88
|
+
|
|
89
|
+
WHAT IT RETURNS:
|
|
90
|
+
- Entity records containing: name, description, summary, overview, FAQs, keywords, URLs, logos, contact info, location, and metadata
|
|
91
|
+
- Search results include domain cards (organizations/projects/DAOs), agents, compositions (documents/resources), and events
|
|
92
|
+
- Each result has a 'record' field with the entity data - look for 'summary', 'overview', and 'faq' fields in the record
|
|
93
|
+
- Results are ranked by relevance using hybrid search (text + semantic similarity)
|
|
94
|
+
|
|
95
|
+
WORKFLOW:
|
|
96
|
+
1. User asks about something → Extract the search query (entity name, topic, or keywords)
|
|
97
|
+
2. Call this tool with the query: {"query": "extracted search term"}
|
|
98
|
+
3. Review the search results - each item has a 'record' field with entity data
|
|
99
|
+
4. Check the 'record' for 'summary', 'overview', and 'faq' fields
|
|
100
|
+
5. If you need more complete information, use get_domain_card with the entity's DID (found in record.id)
|
|
101
|
+
|
|
102
|
+
FILTERS (optional):
|
|
103
|
+
- Domain cards:
|
|
104
|
+
- dc.entity_type: exact chain entity type — "dao/pod" for pods, "dao/protocol" for protocol DAOs, "dao" for plain DAOs. Compound types are never split; "dao/pod" and "dao" are distinct values.
|
|
105
|
+
- dc.entity_verified: "true" or "false" — chain-verified status. Omit to include both.
|
|
106
|
+
- dc.categories: comma-separated category values
|
|
107
|
+
- dc.keywords: comma-separated keywords
|
|
108
|
+
- dc.issuer: DID of the issuer
|
|
109
|
+
- dc.has_url: "true"/"false" — whether entity has a URL
|
|
110
|
+
- dc.has_logo: "true"/"false" — whether entity has a logo
|
|
111
|
+
- dc.valid_from_gte / dc.valid_from_lte: ISO datetime bounds
|
|
112
|
+
- dc.bbox: minLng,minLat,maxLng,maxLat (geo bounding box)
|
|
113
|
+
- dc.near + dc.radius: lng,lat and radius in km (geo proximity)
|
|
114
|
+
- Agents: agent.domain_card_id, agent.has_url (true/false)
|
|
115
|
+
- Compositions: comp.domain_card_id, comp.creator, comp.has_url (true/false)
|
|
116
|
+
- Events: event.domain_card_id, event.from (ISO date), event.to (ISO date), event.location
|
|
117
|
+
|
|
118
|
+
ENTITY TYPE EXAMPLES:
|
|
119
|
+
- Find all pods: {"dc.entity_type": "dao/pod"}
|
|
120
|
+
- Find protocol DAOs: {"dc.entity_type": "dao/protocol"}
|
|
121
|
+
- Find verified entities of type protocol/dao: {"dc.entity_type": "dao/protocol", "dc.entity_verified": "true"}
|
|
122
|
+
- "verified entities of type protocol/dao relevant to mining" → query: "mining", filters: {"dc.entity_type": "dao/protocol", "dc.entity_verified": "true"}
|
|
123
|
+
|
|
124
|
+
IMPORTANT: Always check the 'record' field in results for summary, overview, and faq data. These fields contain the curated information about the entity.`;
|
|
125
|
+
|
|
126
|
+
const CARD_DESCRIPTION = `Get essential domain card details by DID (Decentralized Identifier) including summary, overview, FAQs, and key information. Returns only essential fields to optimize context usage.
|
|
127
|
+
|
|
128
|
+
🚨 CRITICAL REQUIREMENT: You MUST provide the 'did' parameter. NEVER call this tool with empty arguments {}.
|
|
129
|
+
|
|
130
|
+
✅ CORRECT EXAMPLES:
|
|
131
|
+
{"did": "did:ixo:entity:ixoworld"}
|
|
132
|
+
|
|
133
|
+
❌ WRONG (WILL FAIL):
|
|
134
|
+
{}
|
|
135
|
+
{"query": "IXO"}
|
|
136
|
+
|
|
137
|
+
⚠️ REQUIRED PARAMETERS:
|
|
138
|
+
- did (REQUIRED): The domain card DID (Decentralized Identifier) - MUST always be provided. Cannot be empty.
|
|
139
|
+
|
|
140
|
+
PRIMARY USE CASES:
|
|
141
|
+
- After using domain_indexer_search, use this to get the essential entity information including summary, overview, and FAQs
|
|
142
|
+
- When you have a specific entity DID and need its summary, overview, or FAQs
|
|
143
|
+
- When search results show partial information and you need the curated summary/overview/FAQ fields
|
|
144
|
+
- When users specifically ask for FAQs, summaries, or overviews of a known entity
|
|
145
|
+
|
|
146
|
+
WHAT IT RETURNS (filtered to essential fields only):
|
|
147
|
+
- id: Entity DID
|
|
148
|
+
- name: Entity name
|
|
149
|
+
- description: Full description
|
|
150
|
+
- summary: Brief summary of the entity (if available)
|
|
151
|
+
- overview: Detailed overview/description (if available)
|
|
152
|
+
- faq: Array of FAQ objects with question and answer fields
|
|
153
|
+
- url: Entity website URL
|
|
154
|
+
- keywords: Array of keywords
|
|
155
|
+
- entity_type: Array containing the chain entity type (e.g. ["dao/pod"], ["dao/protocol"], ["dao"])
|
|
156
|
+
- entity_verified: boolean or null — whether the entity is verified on-chain (null means not yet determined)
|
|
157
|
+
|
|
158
|
+
WORKFLOW:
|
|
159
|
+
1. First use domain_indexer_search to find entities
|
|
160
|
+
2. Extract the DID from search results (record.id field)
|
|
161
|
+
3. Use this tool with the DID to get essential information including summary, overview, and FAQs
|
|
162
|
+
4. The response directly contains summary, overview, and faq fields at the top level
|
|
163
|
+
|
|
164
|
+
IMPORTANT: This tool returns a filtered response with only essential fields (summary, overview, FAQ, and basic info) to avoid context overload. All fields are at the top level - no need to navigate nested objects.`;
|
|
165
|
+
|
|
166
|
+
/** Domain-card payload trimmed to the fields the agent needs. */
|
|
167
|
+
interface DomainCardSummary {
|
|
168
|
+
id: unknown;
|
|
169
|
+
name: unknown;
|
|
170
|
+
description: unknown;
|
|
171
|
+
summary: unknown;
|
|
172
|
+
overview: unknown;
|
|
173
|
+
faq: unknown;
|
|
174
|
+
url: unknown;
|
|
175
|
+
keywords: unknown;
|
|
176
|
+
entity_type: unknown;
|
|
177
|
+
entity_verified: unknown;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
181
|
+
return typeof value === 'object' && value !== null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function projectDomainCard(card: unknown): DomainCardSummary {
|
|
185
|
+
const c = isRecord(card) ? card : {};
|
|
186
|
+
return {
|
|
187
|
+
id: c.id,
|
|
188
|
+
name: c.name,
|
|
189
|
+
description: c.description,
|
|
190
|
+
summary: c.summary,
|
|
191
|
+
overview: c.overview,
|
|
192
|
+
faq: c.faq ?? [],
|
|
193
|
+
url: c.url,
|
|
194
|
+
keywords: c.keywords ?? [],
|
|
195
|
+
entity_type: c.entity_type ?? [],
|
|
196
|
+
entity_verified: c.entity_verified ?? null,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Construct the two domain-indexer tools, closing over the configured base
|
|
202
|
+
* URL. Tools are pure HTTP wrappers — they don't need a `RuntimeContext`,
|
|
203
|
+
* so the handler ignores the second argument.
|
|
204
|
+
*/
|
|
205
|
+
export function createDomainIndexerTools(baseUrl: string): PluginTool[] {
|
|
206
|
+
const search = tool(
|
|
207
|
+
async (rawArgs) => {
|
|
208
|
+
const params = searchSchema.parse(rawArgs);
|
|
209
|
+
const url = new URL('/search', baseUrl);
|
|
210
|
+
url.searchParams.set('q', params.query);
|
|
211
|
+
if (params.limit !== undefined) {
|
|
212
|
+
url.searchParams.set('limit', String(params.limit));
|
|
213
|
+
}
|
|
214
|
+
if (params.scopes) url.searchParams.set('scopes', params.scopes);
|
|
215
|
+
for (const [k, v] of Object.entries(params.filters ?? {})) {
|
|
216
|
+
url.searchParams.set(k, v);
|
|
217
|
+
}
|
|
218
|
+
const response = await fetch(url.toString());
|
|
219
|
+
if (!response.ok) {
|
|
220
|
+
throw new Error(`Search failed: ${response.statusText}`);
|
|
221
|
+
}
|
|
222
|
+
return response.json();
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: 'domain_indexer_search',
|
|
226
|
+
description: SEARCH_DESCRIPTION,
|
|
227
|
+
schema: searchSchema,
|
|
228
|
+
},
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
const card = tool(
|
|
232
|
+
async (rawArgs) => {
|
|
233
|
+
const { did } = cardSchema.parse(rawArgs);
|
|
234
|
+
const url = new URL(`/domain-cards/${did}`, baseUrl);
|
|
235
|
+
const response = await fetch(url.toString());
|
|
236
|
+
if (!response.ok) {
|
|
237
|
+
if (response.status === 404) {
|
|
238
|
+
return { error: 'Domain card not found' };
|
|
239
|
+
}
|
|
240
|
+
throw new Error(`Failed to fetch domain card: ${response.statusText}`);
|
|
241
|
+
}
|
|
242
|
+
const fullCard: unknown = await response.json();
|
|
243
|
+
return projectDomainCard(fullCard);
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: 'get_domain_card',
|
|
247
|
+
description: CARD_DESCRIPTION,
|
|
248
|
+
schema: cardSchema,
|
|
249
|
+
},
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
return [search, card];
|
|
253
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { OraclePlugin } from '../../plugin-api/oracle-plugin';
|
|
3
|
+
import type {
|
|
4
|
+
MergedConfig,
|
|
5
|
+
PluginContext,
|
|
6
|
+
PluginManifest,
|
|
7
|
+
PluginSubAgent,
|
|
8
|
+
} from '../../plugin-api/types';
|
|
9
|
+
import { createDomainIndexerSubAgent } from './domain-indexer-agent';
|
|
10
|
+
import { createDomainIndexerTools } from './domain-indexer-tools';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Default Domain Indexer endpoints per IXO network. Selected at boot from
|
|
14
|
+
* the core `NETWORK` env var. Operators can override with the optional
|
|
15
|
+
* `DOMAIN_INDEXER_URL` env var (useful for local/staging deployments).
|
|
16
|
+
*/
|
|
17
|
+
const DOMAIN_INDEXER_URLS = {
|
|
18
|
+
mainnet: 'https://domain-indexer.ixo.earth',
|
|
19
|
+
testnet: 'https://domain-indexer.testnet.ixo.earth',
|
|
20
|
+
devnet: 'https://domain-indexer.devnet.ixo.earth',
|
|
21
|
+
} as const;
|
|
22
|
+
|
|
23
|
+
const configSchema = z.object({
|
|
24
|
+
DOMAIN_INDEXER_URL: z.string().url().optional(),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const networkSchema = z.enum(['mainnet', 'testnet', 'devnet']);
|
|
28
|
+
|
|
29
|
+
function resolveDomainIndexerUrl(config: MergedConfig): string {
|
|
30
|
+
const override = configSchema.safeParse(config);
|
|
31
|
+
if (override.success && override.data.DOMAIN_INDEXER_URL) {
|
|
32
|
+
return override.data.DOMAIN_INDEXER_URL;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const network = networkSchema.safeParse(config.NETWORK);
|
|
36
|
+
if (network.success) {
|
|
37
|
+
return DOMAIN_INDEXER_URLS[network.data];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
throw new Error(
|
|
41
|
+
'domain-indexer: could not resolve base URL. Set DOMAIN_INDEXER_URL or ensure NETWORK is one of mainnet|testnet|devnet.',
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const manifest: PluginManifest = {
|
|
46
|
+
title: 'Domain Indexer',
|
|
47
|
+
summary:
|
|
48
|
+
'Domain analysis and entity lookup across the IXO ecosystem — organizations, projects, DAOs, DIDs.',
|
|
49
|
+
whenToUse: [
|
|
50
|
+
'User asks "what is X?" or "tell me about X" for an organization, project, DAO, or DID.',
|
|
51
|
+
'User needs the summary, overview, or FAQ of an IXO entity.',
|
|
52
|
+
'Looking up a domain card by its DID.',
|
|
53
|
+
'Discovering entities by topic, category, or keyword.',
|
|
54
|
+
],
|
|
55
|
+
whenNotToUse: [
|
|
56
|
+
'General web search unrelated to IXO entities (use Firecrawl).',
|
|
57
|
+
'Personal memory or past-conversation recall (use Memory).',
|
|
58
|
+
'Page editing or workspace pages (use Editor) — pages are NOT entities.',
|
|
59
|
+
],
|
|
60
|
+
examples: [
|
|
61
|
+
{
|
|
62
|
+
user: 'Tell me about did:ixo:entity:abc123.',
|
|
63
|
+
thought: 'IXO DID lookup — delegate to call_domain_indexer_agent.',
|
|
64
|
+
tool: 'call_domain_indexer_agent',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
user: 'Find DAOs working on climate.',
|
|
68
|
+
thought: 'Discovery by topic across IXO entities.',
|
|
69
|
+
tool: 'call_domain_indexer_agent',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
tags: ['ixo', 'entities', 'search', 'dids'],
|
|
73
|
+
category: 'data',
|
|
74
|
+
// On-demand: IXO entity lookups are a niche flow — the agent loads this
|
|
75
|
+
// via `load_capability` when the conversation actually needs it, keeping
|
|
76
|
+
// the sub-agent schema out of every unrelated chat turn.
|
|
77
|
+
visibility: 'on-demand',
|
|
78
|
+
stability: 'stable',
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Domain Indexer plugin. Exposes a sub-agent (`call_domain_indexer_agent`)
|
|
83
|
+
* that searches the IXO Domain Indexer and resolves entities by DID.
|
|
84
|
+
*
|
|
85
|
+
* The base URL is derived from `NETWORK` by default and can be overridden
|
|
86
|
+
* with `DOMAIN_INDEXER_URL` (e.g. for a local mirror or staging build).
|
|
87
|
+
* Always on — no `autoDetect`.
|
|
88
|
+
*/
|
|
89
|
+
export class DomainIndexerPlugin extends OraclePlugin {
|
|
90
|
+
readonly name = 'domain-indexer';
|
|
91
|
+
readonly version = '1.0.0';
|
|
92
|
+
readonly manifest = manifest;
|
|
93
|
+
override readonly configSchema = configSchema;
|
|
94
|
+
|
|
95
|
+
override getSubAgents(ctx: PluginContext): PluginSubAgent[] {
|
|
96
|
+
const baseUrl = resolveDomainIndexerUrl(ctx.config);
|
|
97
|
+
const tools = createDomainIndexerTools(baseUrl);
|
|
98
|
+
return [createDomainIndexerSubAgent(tools)];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The only place new BlockNote structure is created.
|
|
3
|
+
*
|
|
4
|
+
* Markdown is parsed and materialised by `@blocknote/server-util` — the
|
|
5
|
+
* upstream BlockNote ↔ Y.js bridge — into a throwaway `Y.Doc`, then the
|
|
6
|
+
* resulting nodes are cloned out as detached values the caller inserts into the
|
|
7
|
+
* live document. Nothing here hand-builds `blockContainer` / `blockContent`
|
|
8
|
+
* XML, and nothing here round-trips an existing document: `yXmlFragmentToBlocks`
|
|
9
|
+
* silently drops any block type outside the default schema, so feeding a real
|
|
10
|
+
* document (which carries custom IXO blocks) through it would delete them.
|
|
11
|
+
*
|
|
12
|
+
* `@blocknote/server-util` resolves the same yjs instance as this package, so
|
|
13
|
+
* the nodes it produces can be inserted into our docs — unlike `@ixo/editor`'s
|
|
14
|
+
* fragment helpers, which carry their own yjs copy.
|
|
15
|
+
*
|
|
16
|
+
* On workerd, `@blocknote/server-util`'s internal JSDOM is replaced by the
|
|
17
|
+
* linkedom-backed shim in `jsdom-shim.ts` (the `jsdom` specifier is aliased to
|
|
18
|
+
* it — see that file for the alias wiring). Everything else in the pipeline
|
|
19
|
+
* (remark, prosemirror-model, y-prosemirror) is pure JS.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { ServerBlockNoteEditor } from '@blocknote/server-util';
|
|
23
|
+
import * as Y from 'yjs';
|
|
24
|
+
import {
|
|
25
|
+
DOCUMENT_FRAGMENT_NAME,
|
|
26
|
+
getContentElement,
|
|
27
|
+
getRootGroup,
|
|
28
|
+
} from './document-model';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Building a `ServerBlockNoteEditor` spins up a DOM shim, so it is created
|
|
32
|
+
* once per isolate and shared. It is stateless across calls.
|
|
33
|
+
*/
|
|
34
|
+
let cachedEditor: ServerBlockNoteEditor | undefined;
|
|
35
|
+
|
|
36
|
+
function serverEditor(): ServerBlockNoteEditor {
|
|
37
|
+
cachedEditor ??= ServerBlockNoteEditor.create();
|
|
38
|
+
return cachedEditor;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The block types BlockNote itself provides (`paragraph`, `heading`,
|
|
43
|
+
* `bulletListItem`, …). Read off the server editor's schema so the set always
|
|
44
|
+
* matches the installed BlockNote version instead of a copied literal.
|
|
45
|
+
*/
|
|
46
|
+
export function proseBlockTypes(): ReadonlySet<string> {
|
|
47
|
+
return new Set(Object.keys(serverEditor().editor.schema.blockSchema));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Parse markdown into detached `blockContainer` elements ready to be inserted
|
|
52
|
+
* into a live document.
|
|
53
|
+
*
|
|
54
|
+
* Returns `[]` for markdown that yields no blocks.
|
|
55
|
+
*/
|
|
56
|
+
export async function markdownToBlockContainers(
|
|
57
|
+
markdown: string,
|
|
58
|
+
): Promise<Y.XmlElement[]> {
|
|
59
|
+
const editor = serverEditor();
|
|
60
|
+
const blocks = await editor.tryParseMarkdownToBlocks(markdown);
|
|
61
|
+
if (blocks.length === 0) return [];
|
|
62
|
+
|
|
63
|
+
const scratch = new Y.Doc();
|
|
64
|
+
try {
|
|
65
|
+
const fragment = scratch.getXmlFragment(DOCUMENT_FRAGMENT_NAME);
|
|
66
|
+
editor.blocksToYXmlFragment(blocks, fragment);
|
|
67
|
+
const group = getRootGroup(fragment);
|
|
68
|
+
if (!group) return [];
|
|
69
|
+
|
|
70
|
+
const containers: Y.XmlElement[] = [];
|
|
71
|
+
for (const node of group.toArray()) {
|
|
72
|
+
if (node instanceof Y.XmlElement && node.nodeName === 'blockContainer') {
|
|
73
|
+
containers.push(node.clone());
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return containers;
|
|
77
|
+
} finally {
|
|
78
|
+
scratch.destroy();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Parse markdown into detached inline nodes for a single block's content —
|
|
84
|
+
* `edit_block`'s text path. Multi-block markdown is flattened into the inline
|
|
85
|
+
* content of every parsed block, so a caller replacing one paragraph's text
|
|
86
|
+
* gets one paragraph's worth of inline nodes.
|
|
87
|
+
*/
|
|
88
|
+
export async function markdownToInlineContent(
|
|
89
|
+
markdown: string,
|
|
90
|
+
): Promise<Array<Y.XmlText | Y.XmlElement>> {
|
|
91
|
+
if (markdown.length === 0) return [];
|
|
92
|
+
|
|
93
|
+
const editor = serverEditor();
|
|
94
|
+
const blocks = await editor.tryParseMarkdownToBlocks(markdown);
|
|
95
|
+
if (blocks.length === 0) return [];
|
|
96
|
+
|
|
97
|
+
const scratch = new Y.Doc();
|
|
98
|
+
try {
|
|
99
|
+
const fragment = scratch.getXmlFragment(DOCUMENT_FRAGMENT_NAME);
|
|
100
|
+
editor.blocksToYXmlFragment(blocks, fragment);
|
|
101
|
+
const group = getRootGroup(fragment);
|
|
102
|
+
if (!group) return [];
|
|
103
|
+
|
|
104
|
+
const nodes: Array<Y.XmlText | Y.XmlElement> = [];
|
|
105
|
+
for (const node of group.toArray()) {
|
|
106
|
+
if (!(node instanceof Y.XmlElement)) continue;
|
|
107
|
+
if (node.nodeName !== 'blockContainer') continue;
|
|
108
|
+
const content = getContentElement(node);
|
|
109
|
+
if (!content) continue;
|
|
110
|
+
for (const inline of content.toArray()) {
|
|
111
|
+
if (inline instanceof Y.XmlText || inline instanceof Y.XmlElement) {
|
|
112
|
+
nodes.push(inline.clone());
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return nodes;
|
|
117
|
+
} finally {
|
|
118
|
+
scratch.destroy();
|
|
119
|
+
}
|
|
120
|
+
}
|