@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,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown by the runtime's UCAN adapter when an invocation cannot be minted for
|
|
3
|
+
* an EXPECTED reason — the oracle has no signing key loaded, or the user has
|
|
4
|
+
* no cached delegation. It is deliberately distinct from a plain `Error`
|
|
5
|
+
* (which signals an UNEXPECTED mint/sign failure) so plugin auth helpers can
|
|
6
|
+
* classify the two without matching error-message substrings: an
|
|
7
|
+
* `instanceof UcanMintUnavailableError` check demotes the expected case to a
|
|
8
|
+
* non-error log and degrades silently, while any other throw is a real
|
|
9
|
+
* failure.
|
|
10
|
+
*/
|
|
11
|
+
export class UcanMintUnavailableError extends Error {
|
|
12
|
+
constructor(message: string) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'UcanMintUnavailableError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The AG-UI sub-agent — the Workers port of the Node runtime's
|
|
3
|
+
* `createAguiSubAgent`. One tool per client-declared AG-UI action; every
|
|
4
|
+
* action call is forwarded into the main chat so the client renders the
|
|
5
|
+
* matching component events.
|
|
6
|
+
*/
|
|
7
|
+
import type { PluginSubAgent, PluginTool } from '../../plugin-api/types';
|
|
8
|
+
|
|
9
|
+
const formatToolDocs = (tools: PluginTool[]): string => {
|
|
10
|
+
if (!tools.length) return '- No AG-UI tools configured.';
|
|
11
|
+
return tools
|
|
12
|
+
.map((t) => {
|
|
13
|
+
const description = t.description?.trim() ?? 'No description provided.';
|
|
14
|
+
return `- \`${t.name}\`: ${description}`;
|
|
15
|
+
})
|
|
16
|
+
.join('\n');
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const buildAguiPrompt = (toolsDoc: string): string =>
|
|
20
|
+
`
|
|
21
|
+
You are the AG-UI Agent — a specialized sub-agent that generates interactive UI
|
|
22
|
+
components in the user's browser by calling AG-UI (Agent Generated UI) tools.
|
|
23
|
+
|
|
24
|
+
## 🚨 THE ONLY RULE THAT MATTERS
|
|
25
|
+
|
|
26
|
+
Your response counts as a failure if you say things like "I've created the table" or "The visualization is ready" without actually invoking an AG-UI tool. You MUST call a tool. Words alone do nothing — the UI only changes when a tool is invoked.
|
|
27
|
+
|
|
28
|
+
## What are AG-UI Tools?
|
|
29
|
+
|
|
30
|
+
AG-UI tools dynamically generate interactive components (tables, charts, forms,
|
|
31
|
+
etc.) that render directly in the client's browser. They execute instantly
|
|
32
|
+
without backend processing.
|
|
33
|
+
|
|
34
|
+
## Available AG-UI Tools
|
|
35
|
+
|
|
36
|
+
${toolsDoc}
|
|
37
|
+
|
|
38
|
+
## Worked Example — fruits table
|
|
39
|
+
|
|
40
|
+
\`\`\`
|
|
41
|
+
Task: "Create a data table with these 5 fruits: [{name: 'Apple', color: 'Red', price: 1.5}, ...]"
|
|
42
|
+
|
|
43
|
+
Your action: call create_data_table with
|
|
44
|
+
{
|
|
45
|
+
id: "fruits_table",
|
|
46
|
+
title: "Fruits",
|
|
47
|
+
data: [{name: "Apple", color: "Red", price: 1.5}, ...],
|
|
48
|
+
columns: [
|
|
49
|
+
{key: "name", label: "Fruit"},
|
|
50
|
+
{key: "color", label: "Color"},
|
|
51
|
+
{key: "price", label: "Avg Price"}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
Your message: "Here's the fruits table."
|
|
56
|
+
\`\`\`
|
|
57
|
+
|
|
58
|
+
## Message Output Rules (after the tool call)
|
|
59
|
+
|
|
60
|
+
Your message to the main agent should ONLY be a short natural-language
|
|
61
|
+
confirmation — NEVER the data, JSON, or a text rendition of the UI.
|
|
62
|
+
|
|
63
|
+
DO:
|
|
64
|
+
- Call the AG-UI tool FIRST.
|
|
65
|
+
- Then add one short sentence like "Here's the fruits table" or "Rendered the revenue chart."
|
|
66
|
+
|
|
67
|
+
DON'T:
|
|
68
|
+
- Output data as markdown tables in your message.
|
|
69
|
+
- Display JSON or raw rows in your message.
|
|
70
|
+
- Recreate the table/chart/list as text — the canvas already shows it.
|
|
71
|
+
- Reply "I've created …" without actually having called the tool.
|
|
72
|
+
|
|
73
|
+
## Schema Compliance
|
|
74
|
+
|
|
75
|
+
- STRICTLY follow each tool's schema.
|
|
76
|
+
- All required fields must be present and correctly typed.
|
|
77
|
+
- Extract parameters verbatim from the task — do not substitute, guess, or reformat values.
|
|
78
|
+
- Validation errors cause the tool to fail silently — double-check before calling.
|
|
79
|
+
|
|
80
|
+
## Task Discipline
|
|
81
|
+
|
|
82
|
+
- You are a one-shot sub-agent invoked by the main agent. Your single task
|
|
83
|
+
message is ALL the context you have. Do not assume prior conversation state.
|
|
84
|
+
- If the task is unclear or missing critical details, STOP immediately and
|
|
85
|
+
return a clear message explaining what's missing. Do NOT guess.
|
|
86
|
+
- Never loop or retry the same failing approach. If the first attempt fails,
|
|
87
|
+
stop and return a clear error — the main agent will re-invoke you if needed.
|
|
88
|
+
|
|
89
|
+
## Workflow
|
|
90
|
+
|
|
91
|
+
1. Parse the task. Identify which tool to use and which parameters to pass.
|
|
92
|
+
2. Extract parameters verbatim from the task.
|
|
93
|
+
3. **CALL THE TOOL.** This is not optional.
|
|
94
|
+
4. Add one short confirmation sentence.
|
|
95
|
+
`.trim();
|
|
96
|
+
|
|
97
|
+
const buildAguiDescription = (tools: PluginTool[]): string => {
|
|
98
|
+
const names = tools.map((t) => t.name).join(', ') || 'no configured tools';
|
|
99
|
+
return `Specialized AG-UI Agent that generates interactive UI components (tables, charts, forms) in the user's browser. Available tools: (${names}). Give it ONE self-contained task including every value the component needs.`;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export function createAguiSubAgent(tools: PluginTool[]): PluginSubAgent {
|
|
103
|
+
return {
|
|
104
|
+
name: 'AG-UI Agent',
|
|
105
|
+
description: buildAguiDescription(tools),
|
|
106
|
+
systemPrompt: buildAguiPrompt(formatToolDocs(tools)),
|
|
107
|
+
tools,
|
|
108
|
+
model: 'subagent',
|
|
109
|
+
middlewares: [],
|
|
110
|
+
// AG-UI action invocations must surface in the main chat so the UI can
|
|
111
|
+
// render the corresponding component events. Forward every action this
|
|
112
|
+
// sub-agent exposes — the tool list IS the action list.
|
|
113
|
+
forwardTools: true,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AG-UI plugin — the Workers port of the Node runtime's `AGUIPlugin`.
|
|
3
|
+
*
|
|
4
|
+
* The client declares its AG-UI actions in the turn body (`agActions[]` →
|
|
5
|
+
* `state.agActions`, each `{ name, description, schema, hasRender? }`); the
|
|
6
|
+
* plugin builds one tool per action and exposes them through the AG-UI
|
|
7
|
+
* sub-agent (`call_ag-ui_agent`). Calling an action emits `action_call` over
|
|
8
|
+
* the realtime channel (`ctx.frontend.callAgAction`) and waits for the
|
|
9
|
+
* client's `action_call_result` — the SSE stream shows the same call as an
|
|
10
|
+
* `action_call` event from the sub-agent's forwarded tool call.
|
|
11
|
+
*/
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
import type { AgAction } from '../../core/state';
|
|
14
|
+
import { clientSchemaToZod } from '../../core/utils';
|
|
15
|
+
import { OraclePlugin } from '../../plugin-api/oracle-plugin';
|
|
16
|
+
import { tool } from '../../plugin-api/tool-helper';
|
|
17
|
+
import type {
|
|
18
|
+
PluginManifest,
|
|
19
|
+
PluginSubAgent,
|
|
20
|
+
PluginTool,
|
|
21
|
+
RuntimeContext,
|
|
22
|
+
} from '../../plugin-api/types';
|
|
23
|
+
import { logActionToMatrix } from '../portal/action-log';
|
|
24
|
+
import { createAguiSubAgent } from './agui-agent';
|
|
25
|
+
|
|
26
|
+
const manifest: PluginManifest = {
|
|
27
|
+
title: 'AG-UI',
|
|
28
|
+
summary:
|
|
29
|
+
"Renders interactive UI components (tables, charts, forms) in the user's browser via AG-UI actions.",
|
|
30
|
+
whenToUse: [
|
|
31
|
+
'User asks for an interactive table, chart, or form to be rendered.',
|
|
32
|
+
'A response is best shown as a structured UI component rather than plain text.',
|
|
33
|
+
],
|
|
34
|
+
whenNotToUse: [
|
|
35
|
+
'No AG-UI actions are declared on this request (sub-agent is not built).',
|
|
36
|
+
"A plain text answer is sufficient — don't render UI just because you can.",
|
|
37
|
+
],
|
|
38
|
+
examples: [
|
|
39
|
+
{
|
|
40
|
+
user: 'Show me the results as a table.',
|
|
41
|
+
thought:
|
|
42
|
+
'Structured display — delegate to call_ag-ui_agent with the rows.',
|
|
43
|
+
tool: 'call_ag-ui_agent',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
tags: ['agui', 'ui', 'portal', 'copilot'],
|
|
47
|
+
category: 'ui',
|
|
48
|
+
visibility: 'on-demand',
|
|
49
|
+
stability: 'stable',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const AG_ACTION_TIMEOUT_MS = 15_000;
|
|
53
|
+
|
|
54
|
+
const AG_ACTION_SHAPE = z.object({
|
|
55
|
+
name: z.string(),
|
|
56
|
+
description: z.string(),
|
|
57
|
+
schema: z.record(z.string(), z.unknown()),
|
|
58
|
+
hasRender: z.boolean().optional(),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const ARGS_RECORD_SHAPE = z.record(z.string(), z.unknown());
|
|
62
|
+
|
|
63
|
+
export function parseAgActions(value: unknown): AgAction[] {
|
|
64
|
+
if (!Array.isArray(value)) return [];
|
|
65
|
+
const out: AgAction[] = [];
|
|
66
|
+
for (const entry of value) {
|
|
67
|
+
const parsed = AG_ACTION_SHAPE.safeParse(entry);
|
|
68
|
+
if (parsed.success) out.push(parsed.data);
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function parseArgs(input: unknown): Record<string, unknown> {
|
|
74
|
+
const parsed = ARGS_RECORD_SHAPE.safeParse(input ?? {});
|
|
75
|
+
return parsed.success ? parsed.data : {};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function buildActionTool(action: AgAction): PluginTool | null {
|
|
79
|
+
const schema = clientSchemaToZod(action.schema, action.name);
|
|
80
|
+
if (!schema) return null;
|
|
81
|
+
return tool(
|
|
82
|
+
async (input, ctx: RuntimeContext) => {
|
|
83
|
+
const sessionId = ctx.session.id;
|
|
84
|
+
if (!sessionId) {
|
|
85
|
+
throw new Error('sessionId is required for AG-UI actions');
|
|
86
|
+
}
|
|
87
|
+
const frontend = ctx.frontend;
|
|
88
|
+
if (!frontend) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`AG-UI action ${action.name} needs the realtime channel, which this host does not provide.`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (!frontend.hasClient(sessionId)) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`No browser is connected to session ${sessionId}, so AG-UI action ${action.name} cannot run — the client must open the realtime (socket.io) channel for this session first.`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
const args = parseArgs(input);
|
|
99
|
+
const requestId = ctx.session.requestId;
|
|
100
|
+
const toolCallId = `ag_${requestId || 'noreq'}_${crypto
|
|
101
|
+
.randomUUID()
|
|
102
|
+
.slice(0, 8)}`;
|
|
103
|
+
const result = await frontend.callAgAction({
|
|
104
|
+
sessionId,
|
|
105
|
+
toolCallId,
|
|
106
|
+
toolName: action.name,
|
|
107
|
+
args,
|
|
108
|
+
timeoutMs: AG_ACTION_TIMEOUT_MS,
|
|
109
|
+
});
|
|
110
|
+
logActionToMatrix(ctx, {
|
|
111
|
+
name: action.name,
|
|
112
|
+
args,
|
|
113
|
+
result,
|
|
114
|
+
success: true,
|
|
115
|
+
});
|
|
116
|
+
return JSON.stringify(result);
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: action.name,
|
|
120
|
+
description: action.description,
|
|
121
|
+
schema,
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function readAgActions(rtCtx: RuntimeContext): AgAction[] {
|
|
127
|
+
return parseAgActions(rtCtx.history.state.agActions);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export class AGUIPlugin extends OraclePlugin {
|
|
131
|
+
readonly name = 'agui';
|
|
132
|
+
|
|
133
|
+
readonly version = '1.0.0';
|
|
134
|
+
|
|
135
|
+
readonly manifest = manifest;
|
|
136
|
+
|
|
137
|
+
override async getRequestSubAgents(
|
|
138
|
+
rtCtx: RuntimeContext,
|
|
139
|
+
): Promise<PluginSubAgent[]> {
|
|
140
|
+
const actions = readAgActions(rtCtx);
|
|
141
|
+
if (actions.length === 0) return [];
|
|
142
|
+
const tools = actions
|
|
143
|
+
.map(buildActionTool)
|
|
144
|
+
.filter((t): t is PluginTool => t !== null);
|
|
145
|
+
if (tools.length === 0) return [];
|
|
146
|
+
return [createAguiSubAgent(tools)];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attachments plugin — the agent-facing half of attachment payload
|
|
3
|
+
* retention (`src/attachments/retention.ts`), Workers-only: the Node runtime
|
|
4
|
+
* keeps every attachment inline for the life of a session.
|
|
5
|
+
*
|
|
6
|
+
* Contributes one request-time tool, `view_attachment`, and only on turns
|
|
7
|
+
* whose session already has an offloaded payload (the host surface says so),
|
|
8
|
+
* so sessions without attachments never see the tool.
|
|
9
|
+
*/
|
|
10
|
+
import { OraclePlugin } from '../../plugin-api/oracle-plugin';
|
|
11
|
+
import type {
|
|
12
|
+
PluginManifest,
|
|
13
|
+
PluginTool,
|
|
14
|
+
RuntimeContext,
|
|
15
|
+
} from '../../plugin-api/types';
|
|
16
|
+
import { createViewAttachmentTool } from './view-attachment-tool';
|
|
17
|
+
|
|
18
|
+
const manifest: PluginManifest = {
|
|
19
|
+
title: 'Attachments',
|
|
20
|
+
summary:
|
|
21
|
+
'Fetch a file the user attached earlier in this conversation again once its bytes are no longer inline.',
|
|
22
|
+
whenToUse: [
|
|
23
|
+
'The user refers to an image or file from earlier and the message that carried it now shows an "[attachment offloaded]" note instead of the file.',
|
|
24
|
+
'You need to read, describe or compare an earlier attachment precisely rather than from memory.',
|
|
25
|
+
],
|
|
26
|
+
whenNotToUse: [
|
|
27
|
+
'The attachment is still inline in a recent message — read it directly.',
|
|
28
|
+
'The user wants a NEW file processed — it arrives with their message, no tool call needed.',
|
|
29
|
+
],
|
|
30
|
+
examples: [
|
|
31
|
+
{
|
|
32
|
+
user: 'What colour was the logo in the image I sent you earlier?',
|
|
33
|
+
thought:
|
|
34
|
+
'The earlier message only lists the image with a ref — fetch it again before answering.',
|
|
35
|
+
tool: 'view_attachment',
|
|
36
|
+
args: { ref: '$abc123:example.org' },
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
visibility: 'always',
|
|
40
|
+
stability: 'stable',
|
|
41
|
+
category: 'core',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export class AttachmentsPlugin extends OraclePlugin {
|
|
45
|
+
readonly name = 'attachments';
|
|
46
|
+
|
|
47
|
+
readonly version = '1.0.0';
|
|
48
|
+
|
|
49
|
+
readonly manifest = manifest;
|
|
50
|
+
|
|
51
|
+
override getRequestTools(rtCtx: RuntimeContext): PluginTool[] {
|
|
52
|
+
return rtCtx.attachments?.offloaded ? [createViewAttachmentTool()] : [];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `view_attachment` — fetch an attachment of this session again after its
|
|
3
|
+
* inline payload was offloaded (see `src/attachments/retention.ts`).
|
|
4
|
+
*
|
|
5
|
+
* Two result shapes, decided by the host surface with the same routing as
|
|
6
|
+
* the turn pipeline:
|
|
7
|
+
* - text (helper-model description / transcription / document text, or a
|
|
8
|
+
* decoded plain-text file): returned as the tool output;
|
|
9
|
+
* - native (the current model reads the modality): the bytes go back to the
|
|
10
|
+
* model as a content block. A tool message cannot carry one on the
|
|
11
|
+
* OpenAI-compatible wire, so the tool returns a LangGraph `Command` that
|
|
12
|
+
* appends its tool message AND a human message holding the block — the
|
|
13
|
+
* same proven shape a fresh attachment turn uses. That message is tagged
|
|
14
|
+
* `lc_source: attachment_view`: transcripts hide it, retention treats it
|
|
15
|
+
* as part of the current user turn and offloads it again later.
|
|
16
|
+
*/
|
|
17
|
+
import { HumanMessage, ToolMessage } from '@langchain/core/messages';
|
|
18
|
+
import { Command } from '@langchain/langgraph';
|
|
19
|
+
import { z } from 'zod';
|
|
20
|
+
import {
|
|
21
|
+
ATTACHMENT_VIEW_SOURCE,
|
|
22
|
+
buildUserMessageContent,
|
|
23
|
+
VIEW_ATTACHMENT_TOOL_NAME,
|
|
24
|
+
} from '../../attachments';
|
|
25
|
+
import { tool } from '../../plugin-api/tool-helper';
|
|
26
|
+
import type { PluginTool, RuntimeContext } from '../../plugin-api/types';
|
|
27
|
+
|
|
28
|
+
export const viewAttachmentSchema = z.object({
|
|
29
|
+
ref: z
|
|
30
|
+
.string()
|
|
31
|
+
.min(1)
|
|
32
|
+
.describe(
|
|
33
|
+
'The attachment reference exactly as listed in the "[attachment offloaded]" note of the message it belongs to (a Matrix event id such as "$abc…" or an mxc:// URI).',
|
|
34
|
+
),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export const VIEW_ATTACHMENT_DESCRIPTION =
|
|
38
|
+
'Look at an attachment from earlier in this conversation again. Older messages keep only a note listing their ' +
|
|
39
|
+
'attachments ("[attachment offloaded] … ref: …") instead of the file itself; pass that ref to get the file back. ' +
|
|
40
|
+
'Images and documents the current model can read are re-attached as-is; anything else comes back as text ' +
|
|
41
|
+
'(a description, transcription or the document text). Use it whenever the user refers to a file that is no ' +
|
|
42
|
+
'longer inline — do not guess its contents from memory.';
|
|
43
|
+
|
|
44
|
+
export function createViewAttachmentTool(): PluginTool {
|
|
45
|
+
return tool(
|
|
46
|
+
async (rawArgs: unknown, ctx: RuntimeContext) => {
|
|
47
|
+
const { ref } = viewAttachmentSchema.parse(rawArgs);
|
|
48
|
+
if (!ctx.attachments) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
'attachment access is not available on this host — the file cannot be fetched again',
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
const { meta, view } = await ctx.attachments.view(ref);
|
|
54
|
+
if (view.kind === 'text') return view.text;
|
|
55
|
+
if (!ctx.toolCallId) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`"${meta.filename}" was fetched but cannot be re-attached outside a tool call`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const label = `Re-attached "${meta.filename}" (${meta.mimetype}) from earlier in this conversation.`;
|
|
61
|
+
const timestamp = new Date().toISOString();
|
|
62
|
+
const msgFromMatrixRoom = ctx.session.client === 'matrix';
|
|
63
|
+
return new Command({
|
|
64
|
+
update: {
|
|
65
|
+
messages: [
|
|
66
|
+
new ToolMessage({
|
|
67
|
+
content: `${label} It follows in the next message.`,
|
|
68
|
+
tool_call_id: ctx.toolCallId,
|
|
69
|
+
}),
|
|
70
|
+
new HumanMessage({
|
|
71
|
+
content: buildUserMessageContent(label, [view.native]),
|
|
72
|
+
additional_kwargs: {
|
|
73
|
+
lc_source: ATTACHMENT_VIEW_SOURCE,
|
|
74
|
+
timestamp,
|
|
75
|
+
msgFromMatrixRoom,
|
|
76
|
+
attachment: meta,
|
|
77
|
+
attachments: [meta],
|
|
78
|
+
},
|
|
79
|
+
}),
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: VIEW_ATTACHMENT_TOOL_NAME,
|
|
86
|
+
description: VIEW_ATTACHMENT_DESCRIPTION,
|
|
87
|
+
schema: viewAttachmentSchema,
|
|
88
|
+
visibility: 'always',
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base64 helpers over the Web platform primitives (`btoa`/`atob`) — the
|
|
3
|
+
* Workers runtime has no `Buffer`. Chunked so multi-megabyte payloads never
|
|
4
|
+
* hit the argument-count limit of `String.fromCharCode(...bytes)`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const CHUNK = 0x8000;
|
|
8
|
+
|
|
9
|
+
/** Encode raw bytes as standard base64. */
|
|
10
|
+
export function bytesToBase64(bytes: Uint8Array): string {
|
|
11
|
+
let binary = '';
|
|
12
|
+
for (let i = 0; i < bytes.length; i += CHUNK) {
|
|
13
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
|
|
14
|
+
}
|
|
15
|
+
return btoa(binary);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Decode standard base64 to raw bytes. Throws on malformed input. */
|
|
19
|
+
export function base64ToBytes(b64: string): Uint8Array {
|
|
20
|
+
const binary = atob(b64);
|
|
21
|
+
const bytes = new Uint8Array(binary.length);
|
|
22
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
23
|
+
bytes[i] = binary.charCodeAt(i);
|
|
24
|
+
}
|
|
25
|
+
return bytes;
|
|
26
|
+
}
|