@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,123 @@
|
|
|
1
|
+
import type { AgentMiddleware } from 'langchain';
|
|
2
|
+
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
3
|
+
import type { BaseCheckpointSaver } from '@langchain/langgraph';
|
|
4
|
+
import type { ReactAgent } from 'langchain';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import type {
|
|
7
|
+
ChatOpenAIFields,
|
|
8
|
+
MergedConfig,
|
|
9
|
+
ModelRole,
|
|
10
|
+
OracleIdentity,
|
|
11
|
+
} from '../plugin-api/types';
|
|
12
|
+
import type { Registries } from './registries';
|
|
13
|
+
import type { AmbientServices, RunConfigContext } from './runtime-context';
|
|
14
|
+
import type { TMainAgentGraphState } from './state';
|
|
15
|
+
|
|
16
|
+
/** The 6 internal registries the runtime composes. */
|
|
17
|
+
export type MainAgentRegistries = Registries;
|
|
18
|
+
|
|
19
|
+
export const mainAgentRequestContextSchema = z.object({
|
|
20
|
+
user: z.object({
|
|
21
|
+
did: z.string(),
|
|
22
|
+
matrixUserId: z.string(),
|
|
23
|
+
ucanDelegation: z.any(),
|
|
24
|
+
timezone: z.string().optional(),
|
|
25
|
+
currentTime: z.string().optional(),
|
|
26
|
+
}),
|
|
27
|
+
session: z.object({
|
|
28
|
+
id: z.string(),
|
|
29
|
+
client: z.enum(['portal', 'matrix', 'slack']),
|
|
30
|
+
requestId: z.string(),
|
|
31
|
+
wsId: z.string().optional(),
|
|
32
|
+
roomId: z.string().optional(),
|
|
33
|
+
}),
|
|
34
|
+
history: z
|
|
35
|
+
.object({
|
|
36
|
+
userContext: z.record(z.string(), z.unknown()).optional(),
|
|
37
|
+
})
|
|
38
|
+
.optional(),
|
|
39
|
+
/**
|
|
40
|
+
* Per-request model override, already validated against the catalog
|
|
41
|
+
* allow-list (`isAllowedModel`). Absent → the agent uses the default
|
|
42
|
+
* `main` model.
|
|
43
|
+
*/
|
|
44
|
+
model: z.string().optional(),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
/** Per-request shape — exposes only what the main-agent build needs. */
|
|
48
|
+
export type MainAgentRequestContext = z.infer<
|
|
49
|
+
typeof mainAgentRequestContextSchema
|
|
50
|
+
>;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Optional hooks the consuming host provides to keep host-specific behaviour
|
|
54
|
+
* (model selection, prompt section snippets) out of the runtime.
|
|
55
|
+
*/
|
|
56
|
+
export interface MainAgentHooks {
|
|
57
|
+
/** Optional model resolver. Default: `ambient.llm.get`. */
|
|
58
|
+
resolveModel?: (
|
|
59
|
+
role: ModelRole,
|
|
60
|
+
params?: ChatOpenAIFields,
|
|
61
|
+
) => ReturnType<AmbientServices['llm']['get']>;
|
|
62
|
+
/** Look up a human-readable page title for the page-context middleware. */
|
|
63
|
+
getRoomTitle?: (roomId: string) => Promise<string | undefined>;
|
|
64
|
+
/** Cheap classification model used by the safety-guardrail middleware. */
|
|
65
|
+
safetyModel?: BaseChatModel;
|
|
66
|
+
/** Tool names whose `ToolMessage` outputs should be stripped between turns. */
|
|
67
|
+
validationSkipToolNames?: string[];
|
|
68
|
+
/** Operational-mode block — overridden when a richer mode applies. */
|
|
69
|
+
operationalMode?: string;
|
|
70
|
+
/** Degraded-services notice appended to the system prompt body. */
|
|
71
|
+
degradedServicesBlock?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Host middlewares appended after the always-on stack and the plugins'
|
|
74
|
+
* (e.g. the Matrix `work_status` card driver, which needs a per-object
|
|
75
|
+
* producer the core cannot construct).
|
|
76
|
+
*/
|
|
77
|
+
middlewares?: AgentMiddleware[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface MainAgentArgs {
|
|
81
|
+
registries: MainAgentRegistries;
|
|
82
|
+
identity: OracleIdentity;
|
|
83
|
+
config: MergedConfig;
|
|
84
|
+
requestCtx: MainAgentRequestContext;
|
|
85
|
+
ambient: AmbientServices;
|
|
86
|
+
state: Partial<TMainAgentGraphState>;
|
|
87
|
+
availablePlugins: ReadonlySet<string>;
|
|
88
|
+
/**
|
|
89
|
+
* Checkpointer for the user's thread state. On Workers the Durable Object
|
|
90
|
+
* owns it (SQLite over DO storage) and passes it in per turn; omit for a
|
|
91
|
+
* stateless build (tests).
|
|
92
|
+
*/
|
|
93
|
+
checkpointer?: BaseCheckpointSaver;
|
|
94
|
+
/**
|
|
95
|
+
* The turn's abort signal. Forwarded to the request-time plugin hooks
|
|
96
|
+
* (`getRequestTools` / `getRequestSubAgents`) through their `RuntimeContext`
|
|
97
|
+
* so a cancelled turn also cancels the network calls those hooks make. The
|
|
98
|
+
* caller still passes the same signal to `agent.invoke` / `agent.stream`.
|
|
99
|
+
*/
|
|
100
|
+
abortSignal?: AbortSignal;
|
|
101
|
+
/** Provider of a BYO turn (`runtime.context.byo`); absent on platform turns. */
|
|
102
|
+
byoProvider?: string;
|
|
103
|
+
hooks?: MainAgentHooks;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
107
|
+
export type CompiledMainAgent = ReactAgent<any>;
|
|
108
|
+
|
|
109
|
+
/** What `createMainAgent` resolves to. */
|
|
110
|
+
export interface MainAgentBuildResult {
|
|
111
|
+
/** The compiled LangChain agent — call `invoke` / `stream` on it. */
|
|
112
|
+
agent: CompiledMainAgent;
|
|
113
|
+
/** The composed system prompt the agent was built with (diagnostics). */
|
|
114
|
+
systemPrompt: string;
|
|
115
|
+
/** Names of every tool bound to the agent, in binding order. */
|
|
116
|
+
boundToolNames: string[];
|
|
117
|
+
/**
|
|
118
|
+
* The request's `{ user, session }` channel. Tools already fall back to it,
|
|
119
|
+
* but pass it as `context` in the `invoke` / `stream` config so LangChain
|
|
120
|
+
* middlewares and sub-agents see the same channel the Node runtime provides.
|
|
121
|
+
*/
|
|
122
|
+
context: RunConfigContext;
|
|
123
|
+
}
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createAgent,
|
|
3
|
+
toolRetryMiddleware,
|
|
4
|
+
type StructuredTool,
|
|
5
|
+
} from 'langchain';
|
|
6
|
+
import type {
|
|
7
|
+
PluginContext,
|
|
8
|
+
PluginManifest,
|
|
9
|
+
PluginTool,
|
|
10
|
+
RuntimeContext,
|
|
11
|
+
SharedAccessors,
|
|
12
|
+
} from '../plugin-api/types';
|
|
13
|
+
import type { MainAgentArgs, MainAgentBuildResult } from './main-agent-types';
|
|
14
|
+
import { renderTier1, type Tier1Entry } from './manifest';
|
|
15
|
+
import { buildMetaTools } from './meta-tools';
|
|
16
|
+
import {
|
|
17
|
+
createByoHistorySanitizerMiddleware,
|
|
18
|
+
createCapabilityGateMiddleware,
|
|
19
|
+
createPageContextMiddleware,
|
|
20
|
+
createSafetyGuardrailMiddleware,
|
|
21
|
+
createSummarizationMiddleware,
|
|
22
|
+
createToolRepetitionGuardMiddleware,
|
|
23
|
+
createToolValidationMiddleware,
|
|
24
|
+
} from './middlewares';
|
|
25
|
+
import {
|
|
26
|
+
composePrompt,
|
|
27
|
+
formatTimeContext,
|
|
28
|
+
formatUserPreferences,
|
|
29
|
+
} from './prompt-composer';
|
|
30
|
+
import { formatByPlugin, type ManifestRegistry } from './registries';
|
|
31
|
+
import {
|
|
32
|
+
buildPluginContext,
|
|
33
|
+
buildRuntimeContext,
|
|
34
|
+
type RunConfig,
|
|
35
|
+
type RuntimeStateInput,
|
|
36
|
+
} from './runtime-context';
|
|
37
|
+
import { MainAgentGraphState } from './state';
|
|
38
|
+
import { collectSubAgentsWithFallback } from './sub-agent-fallback';
|
|
39
|
+
import { computeSubAgentToolName } from './subagent-as-tool';
|
|
40
|
+
import { wrapPluginTool } from './wrap-plugin-tool';
|
|
41
|
+
|
|
42
|
+
const PLUGIN_LOGGER_COMPONENT = 'main-agent';
|
|
43
|
+
|
|
44
|
+
const DEFAULT_OPERATIONAL_MODE = [
|
|
45
|
+
'**General conversation mode**',
|
|
46
|
+
'',
|
|
47
|
+
'Default to conversation mode, using available capabilities for recall, search, and task delegation.',
|
|
48
|
+
].join('\n');
|
|
49
|
+
|
|
50
|
+
type CollectedTool = { pluginName: string; tool: PluginTool };
|
|
51
|
+
|
|
52
|
+
/** Map plugin name → effective `manifest.visibility` (default `on-demand`). */
|
|
53
|
+
function visibilityIndex(
|
|
54
|
+
manifests: ManifestRegistry,
|
|
55
|
+
): Map<string, NonNullable<PluginManifest['visibility']>> {
|
|
56
|
+
const out = new Map<string, NonNullable<PluginManifest['visibility']>>();
|
|
57
|
+
for (const { pluginName, manifest } of manifests.collect()) {
|
|
58
|
+
out.set(pluginName, manifest.visibility ?? 'on-demand');
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Filter collected tools by their effective visibility — per-tool override
|
|
65
|
+
* wins; otherwise the plugin's manifest visibility decides.
|
|
66
|
+
*/
|
|
67
|
+
function selectByVisibility(
|
|
68
|
+
tools: CollectedTool[],
|
|
69
|
+
manifestViz: Map<string, NonNullable<PluginManifest['visibility']>>,
|
|
70
|
+
visibility: NonNullable<PluginManifest['visibility']>,
|
|
71
|
+
): CollectedTool[] {
|
|
72
|
+
return tools.filter(({ pluginName, tool }) => {
|
|
73
|
+
const effective =
|
|
74
|
+
tool.visibility ?? manifestViz.get(pluginName) ?? 'on-demand';
|
|
75
|
+
return effective === visibility;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Build a compiled main agent from the runtime's registries plus per-request
|
|
81
|
+
* context. Same pipeline as the Node runtime — tools from the registries
|
|
82
|
+
* (boot cache + request hooks, collected concurrently with per-plugin failure
|
|
83
|
+
* isolation), visibility partition, meta-tools, sub-agents as tools, the
|
|
84
|
+
* fixed middleware order, prompt composition — minus the branches that do not
|
|
85
|
+
* exist on Workers (the commerce lane, Slack, Matrix group chats, and the
|
|
86
|
+
* ChatGPT-subscription history sanitizer). The Matrix `work_status` card
|
|
87
|
+
* driver arrives as a host middleware (`hooks.middlewares`); page context and
|
|
88
|
+
* the safety guardrail install when the host supplies `hooks.getRoomTitle` /
|
|
89
|
+
* `hooks.safetyModel`, exactly as on Node.
|
|
90
|
+
*/
|
|
91
|
+
export async function createMainAgent(
|
|
92
|
+
args: MainAgentArgs,
|
|
93
|
+
): Promise<MainAgentBuildResult> {
|
|
94
|
+
const {
|
|
95
|
+
registries,
|
|
96
|
+
identity,
|
|
97
|
+
config,
|
|
98
|
+
requestCtx,
|
|
99
|
+
ambient,
|
|
100
|
+
state,
|
|
101
|
+
availablePlugins,
|
|
102
|
+
checkpointer,
|
|
103
|
+
abortSignal,
|
|
104
|
+
byoProvider,
|
|
105
|
+
hooks,
|
|
106
|
+
} = args;
|
|
107
|
+
|
|
108
|
+
// ── 1. Plugin context (boot-time, no per-request fields) ────────────────
|
|
109
|
+
const buildCtx: PluginContext = buildPluginContext({
|
|
110
|
+
config,
|
|
111
|
+
identity,
|
|
112
|
+
availablePlugins,
|
|
113
|
+
logger: ambient.logger,
|
|
114
|
+
pluginName: PLUGIN_LOGGER_COMPONENT,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// ── 2. Request-time runtime context (drives getRequestTools/...SubAgents) ─
|
|
118
|
+
ambient.logger.debug?.(
|
|
119
|
+
`[MainAgent] state.loadedPlugins:`,
|
|
120
|
+
state.loadedPlugins,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const loadedSet = new Set<string>(state.loadedPlugins ?? []);
|
|
124
|
+
// Carry the prior request state (browserTools, agActions, …) into the
|
|
125
|
+
// per-request RuntimeContext and the tool-wrapper closures, but NOT the
|
|
126
|
+
// message history: keeping the full `messages` array here would pin the
|
|
127
|
+
// entire history inside every tool closure for the whole run, and it would
|
|
128
|
+
// be the WRONG history anyway — this snapshot is taken before the turn
|
|
129
|
+
// runs. Tools that must describe the conversation read
|
|
130
|
+
// `ctx.history.messages`, which `buildRuntimeContext` fills from
|
|
131
|
+
// LangGraph's live `ToolRuntime.state` at call time.
|
|
132
|
+
//
|
|
133
|
+
// Explicit fields come last so they win over the spread (notably
|
|
134
|
+
// `loadedPlugins`, which must be the de-duped Set, not the raw state array).
|
|
135
|
+
const wrapState: RuntimeStateInput = {
|
|
136
|
+
...state,
|
|
137
|
+
messages: [],
|
|
138
|
+
userContext: state.userContext,
|
|
139
|
+
loadedPlugins: loadedSet,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// `ctx.shared` — read accessors other plugins registered via
|
|
143
|
+
// `getSharedState()`, evaluated lazily against the live context.
|
|
144
|
+
const sharedFactory = (ctx: RuntimeContext): SharedAccessors =>
|
|
145
|
+
registries.sharedState.build(ctx.history.state, ctx);
|
|
146
|
+
|
|
147
|
+
const runConfig: RunConfig = {
|
|
148
|
+
context: {
|
|
149
|
+
user: {
|
|
150
|
+
did: requestCtx.user.did,
|
|
151
|
+
matrixUserId: requestCtx.user.matrixUserId,
|
|
152
|
+
ucanDelegation: requestCtx.user.ucanDelegation,
|
|
153
|
+
timezone: requestCtx.user.timezone,
|
|
154
|
+
currentTime: requestCtx.user.currentTime,
|
|
155
|
+
},
|
|
156
|
+
session: {
|
|
157
|
+
id: requestCtx.session.id,
|
|
158
|
+
client: requestCtx.session.client,
|
|
159
|
+
requestId: requestCtx.session.requestId,
|
|
160
|
+
wsId: requestCtx.session.wsId,
|
|
161
|
+
roomId: requestCtx.session.roomId,
|
|
162
|
+
},
|
|
163
|
+
...(byoProvider ? { byo: { provider: byoProvider, active: true } } : {}),
|
|
164
|
+
},
|
|
165
|
+
...(abortSignal ? { signal: abortSignal } : {}),
|
|
166
|
+
};
|
|
167
|
+
const rtCtx = buildRuntimeContext(
|
|
168
|
+
runConfig,
|
|
169
|
+
ambient,
|
|
170
|
+
wrapState,
|
|
171
|
+
sharedFactory,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// ── 3. Resolve registries (boot-time + request-time contributions) ──────
|
|
175
|
+
// Tool and sub-agent collection are independent request-time fan-outs
|
|
176
|
+
// (each may open network connections); run them concurrently so the
|
|
177
|
+
// slower of the two — not their sum — gates the build.
|
|
178
|
+
const [allTools, subAgentEntries] = await Promise.all([
|
|
179
|
+
registries.tools.collect(buildCtx, rtCtx),
|
|
180
|
+
registries.subAgents.collect(buildCtx, rtCtx),
|
|
181
|
+
]);
|
|
182
|
+
|
|
183
|
+
// The per-turn tool-surface line. Request tools are named in full (there
|
|
184
|
+
// are only a handful and they are the ones that vary turn to turn); the
|
|
185
|
+
// full bound list, boot tools included, stays at debug.
|
|
186
|
+
const requestTools = allTools.filter(({ origin }) => origin === 'request');
|
|
187
|
+
ambient.logger.log(
|
|
188
|
+
`[main-agent] tool surface: ${allTools.length} tools ` +
|
|
189
|
+
`(boot ${allTools.length - requestTools.length}, request ${requestTools.length}) ` +
|
|
190
|
+
`— request tools: ${formatByPlugin(requestTools) || '∅'}`,
|
|
191
|
+
);
|
|
192
|
+
ambient.logger.debug?.(
|
|
193
|
+
`[main-agent] full tool surface: ${formatByPlugin(allTools) || '∅'}`,
|
|
194
|
+
);
|
|
195
|
+
const manifestEntries = registries.manifests.collect();
|
|
196
|
+
const manifestViz = visibilityIndex(registries.manifests);
|
|
197
|
+
const titleByPlugin = new Map(
|
|
198
|
+
manifestEntries.map(({ pluginName, manifest }) => [
|
|
199
|
+
pluginName,
|
|
200
|
+
manifest.title,
|
|
201
|
+
]),
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const eagerTools = selectByVisibility(allTools, manifestViz, 'always');
|
|
205
|
+
// Bind ALL on-demand tools at compile time — gating happens per model call
|
|
206
|
+
// in `CapabilityGateMiddleware` based on the live `loadedPlugins` state.
|
|
207
|
+
// This lets `load_capability` take effect on the very next LLM call inside
|
|
208
|
+
// the same run, instead of waiting for the next request to rebuild.
|
|
209
|
+
const onDemandTools = selectByVisibility(allTools, manifestViz, 'on-demand');
|
|
210
|
+
const silentTools = selectByVisibility(allTools, manifestViz, 'silent');
|
|
211
|
+
|
|
212
|
+
// ── 4. Wrap tools (meta + plugin) so handlers receive a RuntimeContext ──
|
|
213
|
+
const metaTools = buildMetaTools({
|
|
214
|
+
manifestRegistry: registries.manifests,
|
|
215
|
+
toolRegistry: registries.tools,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const fallbackContext = runConfig.context;
|
|
219
|
+
const wrap = (entry: CollectedTool) =>
|
|
220
|
+
wrapPluginTool(entry.tool, {
|
|
221
|
+
ambient,
|
|
222
|
+
state: wrapState,
|
|
223
|
+
pluginTitle: titleByPlugin.get(entry.pluginName),
|
|
224
|
+
sharedFactory,
|
|
225
|
+
fallbackContext,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// ── 5. Sub-agents — bind all at compile time; gating happens at runtime ─
|
|
229
|
+
// Sub-agents share the tool namespace with plugin tools, so they go through
|
|
230
|
+
// the same `CapabilityGateMiddleware` filter as plugin tools.
|
|
231
|
+
const subAgentTools = await collectSubAgentsWithFallback({
|
|
232
|
+
registry: registries.subAgents,
|
|
233
|
+
buildCtx,
|
|
234
|
+
ambient,
|
|
235
|
+
state: wrapState,
|
|
236
|
+
userDid: requestCtx.user.did,
|
|
237
|
+
sessionId: requestCtx.session.id,
|
|
238
|
+
rtCtx,
|
|
239
|
+
sharedFactory,
|
|
240
|
+
fallbackContext,
|
|
241
|
+
subAgents: subAgentEntries,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
ambient.logger.debug?.(
|
|
245
|
+
`[main-agent] binding summary (all bound; gated at runtime): ` +
|
|
246
|
+
`eager=${eagerTools.length} onDemand=${onDemandTools.length} silent=${silentTools.length} ` +
|
|
247
|
+
`subAgents=${subAgentEntries.length} loadedPlugins=[${Array.from(loadedSet).join(', ')}]`,
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
const tools: StructuredTool[] = [
|
|
251
|
+
...metaTools.map((t) =>
|
|
252
|
+
wrapPluginTool(t, {
|
|
253
|
+
ambient,
|
|
254
|
+
state: wrapState,
|
|
255
|
+
sharedFactory,
|
|
256
|
+
fallbackContext,
|
|
257
|
+
}),
|
|
258
|
+
),
|
|
259
|
+
...eagerTools.map(wrap),
|
|
260
|
+
...onDemandTools.map(wrap),
|
|
261
|
+
...silentTools.map(wrap),
|
|
262
|
+
...subAgentTools,
|
|
263
|
+
];
|
|
264
|
+
|
|
265
|
+
// Lookups used by `CapabilityGateMiddleware` to gate on-demand plugins
|
|
266
|
+
// and sub-agents per model call. Meta-tools omitted from the map are
|
|
267
|
+
// pass-through.
|
|
268
|
+
const pluginByToolName = new Map<string, string>();
|
|
269
|
+
const visibilityByToolName = new Map<
|
|
270
|
+
string,
|
|
271
|
+
NonNullable<PluginManifest['visibility']>
|
|
272
|
+
>();
|
|
273
|
+
for (const { pluginName, tool } of allTools) {
|
|
274
|
+
pluginByToolName.set(tool.name, pluginName);
|
|
275
|
+
const effective =
|
|
276
|
+
tool.visibility ?? manifestViz.get(pluginName) ?? 'on-demand';
|
|
277
|
+
visibilityByToolName.set(tool.name, effective);
|
|
278
|
+
}
|
|
279
|
+
for (const { pluginName, subAgent } of subAgentEntries) {
|
|
280
|
+
const toolName = computeSubAgentToolName(subAgent.name);
|
|
281
|
+
pluginByToolName.set(toolName, pluginName);
|
|
282
|
+
visibilityByToolName.set(
|
|
283
|
+
toolName,
|
|
284
|
+
manifestViz.get(pluginName) ?? 'on-demand',
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ── 6. Middleware stack — always-on + plugin contributions ──────────────
|
|
289
|
+
const pluginMiddlewares = registries.middlewares
|
|
290
|
+
.collect(buildCtx)
|
|
291
|
+
.map(({ middleware }) => middleware);
|
|
292
|
+
|
|
293
|
+
// The summarization middleware needs the same resolver so a host's
|
|
294
|
+
// `hooks.resolveModel` override covers the summary model too.
|
|
295
|
+
const resolveModel = hooks?.resolveModel ?? ambient.llm.get.bind(ambient.llm);
|
|
296
|
+
|
|
297
|
+
const middleware = [
|
|
298
|
+
// Outermost, as on Node: rewrite cross-provider reasoning residue before
|
|
299
|
+
// the summarizer condenses (a well-formed) history.
|
|
300
|
+
createByoHistorySanitizerMiddleware({ logger: ambient.logger }),
|
|
301
|
+
// Condense long threads before everything tool-related.
|
|
302
|
+
// Without this, thread state — reloaded, re-serialized, and re-uploaded to
|
|
303
|
+
// the owner store on every turn — grows without bound.
|
|
304
|
+
createSummarizationMiddleware({ model: resolveModel('routing') }),
|
|
305
|
+
createCapabilityGateMiddleware({
|
|
306
|
+
pluginByToolName,
|
|
307
|
+
visibilityByToolName,
|
|
308
|
+
logger: ambient.logger,
|
|
309
|
+
}),
|
|
310
|
+
createToolValidationMiddleware({
|
|
311
|
+
skipToolNames: hooks?.validationSkipToolNames,
|
|
312
|
+
logger: ambient.logger,
|
|
313
|
+
}),
|
|
314
|
+
createToolRepetitionGuardMiddleware({ logger: ambient.logger }),
|
|
315
|
+
toolRetryMiddleware({ onFailure: (error) => error.message }),
|
|
316
|
+
// Same host-gated pair as the Node runtime: the page-context block needs a
|
|
317
|
+
// title lookup, the safety guardrail a classification model.
|
|
318
|
+
...(hooks?.getRoomTitle
|
|
319
|
+
? [
|
|
320
|
+
createPageContextMiddleware({
|
|
321
|
+
getRoomTitle: hooks.getRoomTitle,
|
|
322
|
+
logger: ambient.logger,
|
|
323
|
+
}),
|
|
324
|
+
]
|
|
325
|
+
: []),
|
|
326
|
+
...(hooks?.safetyModel
|
|
327
|
+
? [
|
|
328
|
+
createSafetyGuardrailMiddleware({
|
|
329
|
+
safetyModel: hooks.safetyModel,
|
|
330
|
+
logger: ambient.logger,
|
|
331
|
+
}),
|
|
332
|
+
]
|
|
333
|
+
: []),
|
|
334
|
+
...pluginMiddlewares,
|
|
335
|
+
...(hooks?.middlewares ?? []),
|
|
336
|
+
];
|
|
337
|
+
|
|
338
|
+
// ── 7. Prompt composition ───────────────────────────────────────────────
|
|
339
|
+
const eagerEntries: Tier1Entry[] = manifestEntries.filter(
|
|
340
|
+
({ manifest }) => manifest.visibility === 'always',
|
|
341
|
+
);
|
|
342
|
+
const tier1 = renderTier1({ manifests: eagerEntries });
|
|
343
|
+
for (const warning of tier1.warnings) ambient.logger.warn(warning);
|
|
344
|
+
|
|
345
|
+
const customInstructions = identity.prompt?.customInstructions?.trim() ?? '';
|
|
346
|
+
|
|
347
|
+
const systemPrompt = await composePrompt({
|
|
348
|
+
identity,
|
|
349
|
+
capabilityBlock: tier1.block,
|
|
350
|
+
customInstructions,
|
|
351
|
+
operationalMode: hooks?.operationalMode ?? DEFAULT_OPERATIONAL_MODE,
|
|
352
|
+
userPreferencesContext: formatUserPreferences(state.userPreferences),
|
|
353
|
+
userContext: state.userContext,
|
|
354
|
+
timeContext: formatTimeContext(
|
|
355
|
+
requestCtx.user.timezone,
|
|
356
|
+
requestCtx.user.currentTime,
|
|
357
|
+
),
|
|
358
|
+
currentEntityDid: state.currentEntityDid ?? '',
|
|
359
|
+
oracleNameOverride: state.userPreferences?.agentName,
|
|
360
|
+
degradedServicesBlock: hooks?.degradedServicesBlock,
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// ── 8. Model ────────────────────────────────────────────────────────────
|
|
364
|
+
// A per-request model (already allow-list-validated by the caller) wins
|
|
365
|
+
// over the role default; the adapter honours `params.model`.
|
|
366
|
+
const model = resolveModel(
|
|
367
|
+
'main',
|
|
368
|
+
requestCtx.model ? { model: requestCtx.model } : undefined,
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
// ── 9. Compile ──────────────────────────────────────────────────────────
|
|
372
|
+
const agent = createAgent({
|
|
373
|
+
model,
|
|
374
|
+
tools,
|
|
375
|
+
middleware,
|
|
376
|
+
stateSchema: MainAgentGraphState,
|
|
377
|
+
systemPrompt,
|
|
378
|
+
...(checkpointer ? { checkpointer } : {}),
|
|
379
|
+
name: identity.name,
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
agent,
|
|
384
|
+
systemPrompt,
|
|
385
|
+
boundToolNames: tools.map((t) => t.name),
|
|
386
|
+
context: runConfig.context,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export { MainAgentGraphState };
|
|
391
|
+
export type {
|
|
392
|
+
CompiledMainAgent,
|
|
393
|
+
MainAgentArgs,
|
|
394
|
+
MainAgentBuildResult,
|
|
395
|
+
MainAgentHooks,
|
|
396
|
+
MainAgentRegistries,
|
|
397
|
+
MainAgentRequestContext,
|
|
398
|
+
} from './main-agent-types';
|
|
399
|
+
export type { TMainAgentGraphState } from './state';
|