@ixo/oracle-runtime-workers 0.13.0 → 0.16.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 +32 -10
- package/package.json +13 -8
- package/src/artifacts/config.ts +158 -0
- package/src/artifacts/crypto.ts +94 -0
- package/src/artifacts/routes.ts +73 -0
- package/src/artifacts/store.ts +238 -0
- package/src/artifacts/sweep.ts +200 -0
- package/src/artifacts/tool.ts +103 -0
- package/src/artifacts/viewer-page.ts +614 -0
- package/src/attachments/download.ts +168 -45
- package/src/attachments/index.ts +9 -0
- package/src/attachments/magic.ts +33 -0
- package/src/attachments/pipeline.ts +105 -49
- package/src/attachments/sandbox-archive.ts +9 -8
- package/src/attachments/view-cache.ts +142 -0
- package/src/attachments/view.ts +53 -12
- package/src/channels/auth.ts +310 -0
- package/src/channels/contract.ts +246 -0
- package/src/channels/turns.ts +280 -0
- package/src/core/budgeted-llm.ts +223 -0
- package/src/core/capability-router.ts +273 -0
- package/src/core/context-budget.ts +97 -10
- package/src/core/env.ts +111 -0
- package/src/core/execution-profile.ts +17 -0
- package/src/core/index.ts +267 -9
- package/src/core/llm.ts +30 -9
- package/src/core/main-agent-types.ts +41 -1
- package/src/core/main-agent.ts +367 -65
- package/src/core/manifest.ts +34 -0
- package/src/core/meta-tools.ts +128 -18
- package/src/core/middlewares/capability-gate.ts +130 -12
- package/src/core/middlewares/context-guard.ts +55 -24
- package/src/core/middlewares/index.ts +20 -2
- package/src/core/middlewares/page-context.ts +46 -2
- package/src/core/middlewares/return-direct-first.ts +47 -0
- package/src/core/middlewares/summarization.ts +152 -10
- package/src/core/middlewares/tool-execution.ts +323 -0
- package/src/core/middlewares/tool-repetition-guard.ts +202 -35
- package/src/core/middlewares/turn-boundary.ts +162 -0
- package/src/core/openrouter-pricing.ts +155 -22
- package/src/core/prompt-composer.ts +112 -7
- package/src/core/registries.ts +253 -48
- package/src/core/request-admission.ts +159 -0
- package/src/core/runtime-context.ts +107 -8
- package/src/core/state.ts +1 -1
- package/src/core/sub-agent-fallback.ts +21 -2
- package/src/core/subagent-as-tool.ts +78 -43
- package/src/core/tool-access.ts +189 -0
- package/src/core/tool-scheduler.ts +125 -0
- package/src/core/turn-budget.ts +205 -0
- package/src/core/turn-time-note.ts +43 -0
- package/src/core/user-kv.ts +446 -0
- package/src/core/wrap-plugin-tool.ts +20 -0
- package/src/delivery/plan.ts +278 -0
- package/src/delivery/profile.ts +161 -0
- package/src/delivery/prompt.ts +27 -0
- package/src/delivery/schema.ts +61 -0
- package/src/delivery/shaper.ts +389 -0
- package/src/delivery/types.ts +84 -0
- package/src/do/ambient.ts +51 -15
- package/src/do/contracts.ts +170 -6
- package/src/do/idle-eviction.ts +38 -0
- package/src/do/matrix-turn-ledger.ts +24 -5
- package/src/do/result-store-test-do.ts +137 -3
- package/src/do/result-store.ts +239 -58
- package/src/do/room-mirror.ts +29 -0
- package/src/do/run-buffer.ts +64 -9
- package/src/do/run-coordinator.ts +451 -76
- package/src/do/run-request.ts +2 -0
- package/src/do/run-store-test-do.ts +301 -1
- package/src/do/run-store.ts +385 -43
- package/src/do/secrets-adapter.ts +22 -3
- package/src/do/sse-stream.ts +60 -9
- package/src/do/transcript.ts +48 -5
- package/src/do/turn-body.ts +105 -5
- package/src/do/ucan-service.ts +204 -34
- package/src/do/user-oracle-do.ts +2003 -274
- package/src/feedback/config.ts +127 -0
- package/src/feedback/contract.ts +148 -0
- package/src/feedback/linear-sink.ts +285 -0
- package/src/feedback/privacy.ts +150 -0
- package/src/feedback/reservation.ts +298 -0
- package/src/feedback/submit.ts +247 -0
- package/src/index.ts +138 -6
- package/src/llm/byo-adapter.ts +27 -6
- package/src/llm/byo-catalog.ts +13 -0
- package/src/llm/byo-model-fallback.ts +248 -0
- package/src/llm/byo-service.ts +45 -8
- package/src/llm/provider-error.ts +139 -2
- package/src/matrix/gateway-do.ts +504 -108
- package/src/matrix/group-chat.ts +66 -23
- package/src/matrix/inbox-store.ts +16 -1
- package/src/matrix/ingest.ts +79 -14
- package/src/matrix/media-deadline.ts +130 -0
- package/src/matrix/replay-format.ts +5 -3
- package/src/matrix/reply-parts.ts +92 -0
- package/src/matrix/room-alias.ts +96 -0
- package/src/matrix/user-homeserver.ts +166 -7
- package/src/owner-store/ixo-vfs-store.ts +170 -43
- package/src/owner-store/migrating-store.ts +31 -15
- package/src/owner-store/types.ts +18 -3
- package/src/plugin-api/index.ts +43 -2
- package/src/plugin-api/oracle-plugin.ts +35 -0
- package/src/plugin-api/request-admission.ts +41 -0
- package/src/plugin-api/tool-helper.ts +25 -1
- package/src/plugin-api/tool-plane.ts +92 -0
- package/src/plugin-api/types.ts +194 -6
- package/src/plugins/agui/agui.plugin.ts +31 -16
- package/src/plugins/composio/composio-tools.ts +57 -6
- package/src/plugins/composio/composio-ucan.ts +3 -2
- package/src/plugins/composio/composio.plugin.ts +2 -0
- package/src/plugins/delegated-capabilities.ts +25 -0
- package/src/plugins/editor/content-session.ts +173 -83
- package/src/plugins/editor/content-tools.ts +76 -17
- package/src/plugins/editor/editor-mx.ts +28 -9
- package/src/plugins/editor/editor.plugin.ts +3 -1
- package/src/plugins/editor/failures.ts +28 -7
- package/src/plugins/editor/prompts.ts +2 -1
- package/src/plugins/editor/provider.ts +553 -40
- package/src/plugins/editor/standalone-editor-tool.ts +61 -18
- package/src/plugins/firecrawl/firecrawl-tools.ts +13 -5
- package/src/plugins/flows/action-metadata.ts +111 -11
- package/src/plugins/flows/actions.ts +18 -0
- package/src/plugins/flows/decision-context.ts +31 -0
- package/src/plugins/flows/edit.ts +223 -52
- package/src/plugins/flows/errors.ts +22 -1
- package/src/plugins/flows/flow-doc.ts +92 -9
- package/src/plugins/flows/flows.plugin.ts +11 -5
- package/src/plugins/flows/forms.ts +34 -0
- package/src/plugins/flows/index.ts +2 -0
- package/src/plugins/flows/input-policy.ts +140 -0
- package/src/plugins/flows/prompts.ts +14 -11
- package/src/plugins/flows/read.ts +49 -9
- package/src/plugins/flows/ref-syntax.ts +129 -0
- package/src/plugins/flows/test-homeserver.ts +245 -0
- package/src/plugins/flows/tools/authoring.ts +216 -112
- package/src/plugins/flows/tools/discovery.ts +4 -1
- package/src/plugins/flows/tools/forms.ts +21 -2
- package/src/plugins/flows/tools/settings.ts +34 -13
- package/src/plugins/flows/translator.ts +13 -10
- package/src/plugins/flows/types.ts +167 -42
- package/src/plugins/index.ts +11 -1
- package/src/plugins/ixo-transaction/chain-ids.ts +43 -0
- package/src/plugins/ixo-transaction/index.ts +2 -0
- package/src/plugins/ixo-transaction/ixo-transaction.plugin.ts +119 -0
- package/src/plugins/ixo-transaction/receipts.ts +99 -0
- package/src/plugins/ixo-transaction/tools.ts +329 -0
- package/src/plugins/ixo-transaction/wallet-signing.ts +202 -0
- package/src/plugins/mcp-tool-adapter.ts +23 -2
- package/src/plugins/memory/memory-tools.ts +7 -2
- package/src/plugins/memory/memory-ucan.ts +3 -2
- package/src/plugins/memory/memory.plugin.ts +2 -0
- package/src/plugins/pod-creator/blueprint-store.ts +163 -0
- package/src/plugins/pod-creator/blueprint-types.ts +61 -0
- package/src/plugins/pod-creator/bounded-map.ts +87 -0
- package/src/plugins/pod-creator/capsule-content-client.ts +285 -0
- package/src/plugins/pod-creator/chain-gateway.ts +110 -0
- package/src/plugins/pod-creator/config.ts +85 -0
- package/src/plugins/pod-creator/create-session-store.ts +180 -0
- package/src/plugins/pod-creator/create-tools.ts +538 -0
- package/src/plugins/pod-creator/design-pod-roles.ts +111 -0
- package/src/plugins/pod-creator/index.ts +65 -0
- package/src/plugins/pod-creator/orchestration-tools.ts +172 -0
- package/src/plugins/pod-creator/pod-creator.plugin.ts +190 -0
- package/src/plugins/pod-creator/stage.ts +201 -0
- package/src/plugins/pod-creator/sub-agents.ts +189 -0
- package/src/plugins/portal/action-log.ts +21 -1
- package/src/plugins/portal/declared-tools.ts +90 -0
- package/src/plugins/portal/portal.plugin.ts +42 -16
- package/src/plugins/sandbox/sandbox-bridge.ts +79 -10
- package/src/plugins/sandbox/sandbox-mcp.ts +3 -2
- package/src/plugins/sandbox/sandbox-write-blob.ts +11 -1
- package/src/plugins/sandbox/sandbox.plugin.ts +8 -16
- package/src/plugins/tasks/manifest.ts +1 -1
- package/src/plugins/tasks/middleware.ts +20 -7
- package/src/plugins/tasks/tasks-tools.ts +30 -3
- package/src/plugins/tasks/tasks.plugin.ts +64 -11
- package/src/plugins/user-preferences/schema.ts +12 -1
- package/src/plugins/user-preferences/user-preferences-store.ts +38 -13
- package/src/plugins/user-preferences/user-preferences-tool.ts +3 -2
- package/src/plugins/vfs/vfs-client.ts +250 -71
- package/src/plugins/vfs/vfs-content.ts +30 -11
- package/src/plugins/vfs/vfs-errors.ts +22 -0
- package/src/plugins/vfs/vfs-sandbox-tools.ts +12 -5
- package/src/plugins/vfs/vfs-tools.ts +60 -20
- package/src/realtime/frontend-call-registry.ts +228 -81
- package/src/realtime/realtime-endpoint.ts +215 -44
- package/src/realtime/session-socket-hub.ts +47 -2
- package/src/secrets/secrets-service.ts +9 -3
- package/src/shell/app.ts +562 -82
- package/src/shell/auth.ts +131 -37
- package/src/shell/turn-body-cap.ts +11 -13
- package/src/sqlite/blob-compactor.ts +61 -28
- package/src/sqlite/database.ts +156 -90
- package/src/sqlite/do-vfs.ts +416 -129
- package/src/sqlite/page-tier.ts +133 -40
- package/src/sqlite/serialization.ts +40 -0
- package/src/sqlite/sessions-store.ts +10 -2
- package/src/sqlite/sqlite-saver.ts +139 -19
- package/src/sqlite/user-kv-store.ts +317 -0
- package/src/tasks/schedule.ts +201 -12
- package/src/tasks/scheduler.ts +586 -98
- package/src/tasks/spec.ts +67 -3
- package/src/tasks/store.ts +292 -27
- package/src/tasks/topic-deliverables.ts +70 -0
package/src/core/manifest.ts
CHANGED
|
@@ -49,8 +49,35 @@ export const pluginManifestSchema: z.ZodType<PluginManifest> = z.object({
|
|
|
49
49
|
category: manifestCategorySchema.optional(),
|
|
50
50
|
visibility: manifestVisibilitySchema.optional(),
|
|
51
51
|
stability: manifestStabilitySchema.optional(),
|
|
52
|
+
requires: z
|
|
53
|
+
.array(z.object({ resource: z.string().min(1), action: z.string().min(1) }))
|
|
54
|
+
.optional(),
|
|
52
55
|
});
|
|
53
56
|
|
|
57
|
+
/** A UCAN capability a plugin's manifest requires of the user's delegation. */
|
|
58
|
+
export type CapabilityRequirement = NonNullable<
|
|
59
|
+
PluginManifest['requires']
|
|
60
|
+
>[number];
|
|
61
|
+
|
|
62
|
+
/** The manifest's requirements the user's delegation does not meet (empty when usable). */
|
|
63
|
+
export function unmetRequirements(
|
|
64
|
+
manifest: Pick<PluginManifest, 'requires'>,
|
|
65
|
+
hasCapability: (resource: string, action: string) => boolean,
|
|
66
|
+
): CapabilityRequirement[] {
|
|
67
|
+
return (manifest.requires ?? []).filter(
|
|
68
|
+
(r) => !hasCapability(r.resource, r.action),
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** `can` on `with` pairs for a message: "`*` on `ixo:filesystem`, …". */
|
|
73
|
+
export function describeRequirements(
|
|
74
|
+
requirements: readonly CapabilityRequirement[],
|
|
75
|
+
): string {
|
|
76
|
+
return requirements
|
|
77
|
+
.map((r) => `\`${r.action}\` on \`${r.resource}\``)
|
|
78
|
+
.join(', ');
|
|
79
|
+
}
|
|
80
|
+
|
|
54
81
|
// ── Validator ───────────────────────────────────────────────────────────────
|
|
55
82
|
|
|
56
83
|
/** Result of validating a single plugin manifest. */
|
|
@@ -289,6 +316,13 @@ export function estimateTokensApprox(text: string): number {
|
|
|
289
316
|
return Math.ceil(text.length / 4);
|
|
290
317
|
}
|
|
291
318
|
|
|
319
|
+
/**
|
|
320
|
+
* Size above which a plugin's `operatingGuide` is reported at boot (chars/4
|
|
321
|
+
* tokens). The guide rides in the system prompt of every turn that has the
|
|
322
|
+
* plugin loaded; the bundled flows guide is about 2,000.
|
|
323
|
+
*/
|
|
324
|
+
export const OPERATING_GUIDE_WARN_TOKENS = 3_000;
|
|
325
|
+
|
|
292
326
|
function formatExample(ex: ManifestExample): string {
|
|
293
327
|
const argSummary =
|
|
294
328
|
ex.args && Object.keys(ex.args).length > 0
|
package/src/core/meta-tools.ts
CHANGED
|
@@ -3,9 +3,39 @@ import { Command } from '@langchain/langgraph';
|
|
|
3
3
|
import { ToolMessage } from '@langchain/core/messages';
|
|
4
4
|
import { tool } from '../plugin-api/tool-helper';
|
|
5
5
|
import type { PluginManifest, PluginTool } from '../plugin-api/types';
|
|
6
|
-
import
|
|
6
|
+
import {
|
|
7
|
+
describeRequirements,
|
|
8
|
+
unmetRequirements,
|
|
9
|
+
type CapabilityRequirement,
|
|
10
|
+
} from './manifest';
|
|
11
|
+
import type { ManifestRegistry, ToolSummarySource } from './registries';
|
|
12
|
+
import { withoutWithheldExamples, type TurnToolAccess } from './tool-access';
|
|
7
13
|
import { acquireToolLock } from './utils';
|
|
8
14
|
|
|
15
|
+
export const LOAD_CAPABILITY_TOOL_NAME = 'load_capability';
|
|
16
|
+
export const LIST_CAPABILITIES_TOOL_NAME = 'list_capabilities';
|
|
17
|
+
/** Names of the tools `buildMetaTools` returns. */
|
|
18
|
+
export const META_TOOL_NAMES: readonly string[] = [
|
|
19
|
+
LOAD_CAPABILITY_TOOL_NAME,
|
|
20
|
+
LIST_CAPABILITIES_TOOL_NAME,
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The part of the turn's `resolveTurnToolAccess` result the meta-tools read:
|
|
25
|
+
* computed once per build from the same delegation their `ctx` carries.
|
|
26
|
+
*/
|
|
27
|
+
export type MetaToolAccess = Pick<
|
|
28
|
+
TurnToolAccess<never>,
|
|
29
|
+
'withheldToolNames' | 'hiddenPlugins'
|
|
30
|
+
> & {
|
|
31
|
+
/**
|
|
32
|
+
* Plugins available this turn only through the capability router's
|
|
33
|
+
* preload: usable, but their operating guide is not in the system prompt,
|
|
34
|
+
* so `load_capability` still returns it.
|
|
35
|
+
*/
|
|
36
|
+
preloadedOnly?: ReadonlySet<string>;
|
|
37
|
+
};
|
|
38
|
+
|
|
9
39
|
// ── list_capabilities ───────────────────────────────────────────────────────
|
|
10
40
|
|
|
11
41
|
/**
|
|
@@ -27,6 +57,11 @@ interface CapabilityListing {
|
|
|
27
57
|
loaded: boolean;
|
|
28
58
|
category?: PluginManifest['category'];
|
|
29
59
|
tags: string[];
|
|
60
|
+
/**
|
|
61
|
+
* Present only when the user's authorization does not grant what the
|
|
62
|
+
* plugin requires: it cannot be loaded or used until they re-authorize.
|
|
63
|
+
*/
|
|
64
|
+
unavailable?: { missing: CapabilityRequirement[] };
|
|
30
65
|
}
|
|
31
66
|
|
|
32
67
|
/**
|
|
@@ -34,10 +69,13 @@ interface CapabilityListing {
|
|
|
34
69
|
*
|
|
35
70
|
* Returns one entry per loaded plugin, with a `loaded` flag that combines
|
|
36
71
|
* the boot-time always-on plugins (`visibility === 'always'`) with the set
|
|
37
|
-
* of plugins the agent has explicitly loaded for this thread.
|
|
72
|
+
* of plugins the agent has explicitly loaded for this thread. A plugin left
|
|
73
|
+
* with nothing by the turn's tool planes (`access.hiddenPlugins`) is not
|
|
74
|
+
* listed at all; one whose `requires` are unmet is listed as `unavailable`.
|
|
38
75
|
*/
|
|
39
76
|
export function buildListCapabilitiesTool(
|
|
40
77
|
manifestRegistry: ManifestRegistry,
|
|
78
|
+
access: MetaToolAccess,
|
|
41
79
|
): PluginTool {
|
|
42
80
|
return tool(
|
|
43
81
|
async (args, ctx) => {
|
|
@@ -54,13 +92,19 @@ export function buildListCapabilitiesTool(
|
|
|
54
92
|
if (visibility === 'silent' && !includeSilent) continue;
|
|
55
93
|
if (visibility === 'on-demand' && !includeOnDemand) continue;
|
|
56
94
|
|
|
95
|
+
if (access.hiddenPlugins.has(pluginName)) continue;
|
|
96
|
+
|
|
97
|
+
const missing = unmetRequirements(manifest, ctx.ucan.hasCapability);
|
|
57
98
|
out.push({
|
|
58
99
|
name: pluginName,
|
|
59
100
|
summary: manifest.summary,
|
|
60
101
|
visibility,
|
|
61
|
-
loaded:
|
|
102
|
+
loaded:
|
|
103
|
+
missing.length === 0 &&
|
|
104
|
+
(visibility === 'always' || loadedSet.has(pluginName)),
|
|
62
105
|
category: manifest.category,
|
|
63
106
|
tags: manifest.tags ?? [],
|
|
107
|
+
...(missing.length > 0 ? { unavailable: { missing } } : {}),
|
|
64
108
|
});
|
|
65
109
|
}
|
|
66
110
|
ctx.logger.debug?.(
|
|
@@ -72,7 +116,7 @@ export function buildListCapabilitiesTool(
|
|
|
72
116
|
return JSON.stringify(out);
|
|
73
117
|
},
|
|
74
118
|
{
|
|
75
|
-
name:
|
|
119
|
+
name: LIST_CAPABILITIES_TOOL_NAME,
|
|
76
120
|
description: 'List all available capabilities and their summaries.',
|
|
77
121
|
schema: listCapabilitiesSchema,
|
|
78
122
|
},
|
|
@@ -103,6 +147,18 @@ interface LoadCapabilityResult extends PluginManifest {
|
|
|
103
147
|
alreadyAvailable: boolean;
|
|
104
148
|
/** One entry per tool the plugin contributes. */
|
|
105
149
|
tools: ToolDetail[];
|
|
150
|
+
/**
|
|
151
|
+
* The plugin's working rules (`OraclePlugin.operatingGuide`), present when
|
|
152
|
+
* this call loaded it or the router only preloaded it, once per turn. A
|
|
153
|
+
* plugin loaded on the thread (or `always` visible) has it in the system
|
|
154
|
+
* prompt, so it is not repeated.
|
|
155
|
+
*/
|
|
156
|
+
operatingGuide?: string;
|
|
157
|
+
/**
|
|
158
|
+
* Present when the plugin was NOT loaded because the user's authorization
|
|
159
|
+
* does not grant what it requires (`manifest.requires`).
|
|
160
|
+
*/
|
|
161
|
+
refused?: { missing: CapabilityRequirement[]; reason: string };
|
|
106
162
|
}
|
|
107
163
|
|
|
108
164
|
/**
|
|
@@ -113,13 +169,22 @@ interface LoadCapabilityResult extends PluginManifest {
|
|
|
113
169
|
* cannot be invoked in parallel — concurrent calls throw immediately.
|
|
114
170
|
*
|
|
115
171
|
* Behavior per name:
|
|
116
|
-
* - Unknown plugin
|
|
117
|
-
* `
|
|
172
|
+
* - Unknown plugin, or one the turn's tool planes left with nothing
|
|
173
|
+
* (`access.hiddenPlugins`, answered exactly like an unknown one) →
|
|
174
|
+
* throws, instructing the agent to call `list_capabilities` first.
|
|
118
175
|
* - `silent` plugin → throws (silent plugins are not agent-loadable).
|
|
176
|
+
* - Plugin whose `requires` the user's delegation does not grant →
|
|
177
|
+
* included in the result with `refused` (what is missing) and not
|
|
178
|
+
* loaded; the rest of the batch is unaffected.
|
|
119
179
|
* - Plugin already loaded, or visibility is `always` → included in result
|
|
120
180
|
* with `alreadyAvailable: true` (no state change for that plugin).
|
|
121
181
|
* - Otherwise → added to the `loadedPlugins` state update.
|
|
122
182
|
*
|
|
183
|
+
* The tool list and `examples` leave out the tools the turn withholds
|
|
184
|
+
* (`access.withheldToolNames`). A plugin this call loads, or one the router
|
|
185
|
+
* only preloaded, comes with its `operatingGuide` when it has one — once per
|
|
186
|
+
* turn; a refused one never does.
|
|
187
|
+
*
|
|
123
188
|
* Return value:
|
|
124
189
|
* - If all requested plugins were already available: returns the result
|
|
125
190
|
* array directly (no state change).
|
|
@@ -129,8 +194,12 @@ interface LoadCapabilityResult extends PluginManifest {
|
|
|
129
194
|
*/
|
|
130
195
|
export function buildLoadCapabilityTool(
|
|
131
196
|
manifestRegistry: ManifestRegistry,
|
|
132
|
-
|
|
197
|
+
toolSource: ToolSummarySource,
|
|
198
|
+
access: MetaToolAccess,
|
|
133
199
|
): PluginTool {
|
|
200
|
+
// Guides this tool already returned: the tool is built per turn, and the
|
|
201
|
+
// context it reads does not see a load made earlier in the same turn.
|
|
202
|
+
const guidesReturned = new Set<string>();
|
|
134
203
|
return tool(
|
|
135
204
|
async (args, ctx) => {
|
|
136
205
|
const { names } = loadCapabilitySchema.parse(args);
|
|
@@ -145,7 +214,7 @@ export function buildLoadCapabilityTool(
|
|
|
145
214
|
.collect()
|
|
146
215
|
.find((m) => m.pluginName === name);
|
|
147
216
|
|
|
148
|
-
if (!entry) {
|
|
217
|
+
if (!entry || access.hiddenPlugins.has(name)) {
|
|
149
218
|
throw new Error(
|
|
150
219
|
`Capability "${name}" does not exist. Call list_capabilities first to discover available plugins.`,
|
|
151
220
|
);
|
|
@@ -157,8 +226,29 @@ export function buildLoadCapabilityTool(
|
|
|
157
226
|
);
|
|
158
227
|
}
|
|
159
228
|
|
|
160
|
-
const
|
|
229
|
+
const missing = unmetRequirements(
|
|
230
|
+
entry.manifest,
|
|
231
|
+
ctx.ucan.hasCapability,
|
|
232
|
+
);
|
|
233
|
+
if (missing.length > 0) {
|
|
234
|
+
results.push({
|
|
235
|
+
...withoutWithheldExamples(
|
|
236
|
+
entry.manifest,
|
|
237
|
+
access.withheldToolNames,
|
|
238
|
+
),
|
|
239
|
+
alreadyAvailable: false,
|
|
240
|
+
tools: [],
|
|
241
|
+
refused: {
|
|
242
|
+
missing,
|
|
243
|
+
reason: `The user's authorization for this oracle does not grant ${describeRequirements(missing)}, which "${name}" requires, so it was not loaded. Tell the user; they can re-authorize the oracle with that permission and ask again.`,
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const tools: ToolDetail[] = toolSource
|
|
161
250
|
.toolSummariesForPlugin(name)
|
|
251
|
+
.filter((t) => !access.withheldToolNames.has(t.name))
|
|
162
252
|
.map((t) => ({
|
|
163
253
|
name: t.name,
|
|
164
254
|
description: t.description,
|
|
@@ -168,15 +258,32 @@ export function buildLoadCapabilityTool(
|
|
|
168
258
|
const alwaysVisible = entry.manifest.visibility === 'always';
|
|
169
259
|
const alreadyAvailable = alreadyLoaded || alwaysVisible;
|
|
170
260
|
|
|
171
|
-
|
|
261
|
+
const guideInPrompt =
|
|
262
|
+
alreadyAvailable && access.preloadedOnly?.has(name) !== true;
|
|
263
|
+
const guide =
|
|
264
|
+
guideInPrompt || guidesReturned.has(name)
|
|
265
|
+
? undefined
|
|
266
|
+
: manifestRegistry.operatingGuide(name);
|
|
267
|
+
if (guide) guidesReturned.add(name);
|
|
268
|
+
results.push({
|
|
269
|
+
...withoutWithheldExamples(
|
|
270
|
+
entry.manifest,
|
|
271
|
+
access.withheldToolNames,
|
|
272
|
+
),
|
|
273
|
+
alreadyAvailable,
|
|
274
|
+
tools,
|
|
275
|
+
...(guide ? { operatingGuide: guide } : {}),
|
|
276
|
+
});
|
|
172
277
|
|
|
173
278
|
if (!alreadyAvailable) {
|
|
174
279
|
newToLoad.push(name);
|
|
175
280
|
}
|
|
176
281
|
}
|
|
177
282
|
|
|
283
|
+
// Text, never the bare array: LangChain would pass an array of
|
|
284
|
+
// objects through as the message's content blocks.
|
|
178
285
|
if (newToLoad.length === 0) {
|
|
179
|
-
return results;
|
|
286
|
+
return JSON.stringify(results);
|
|
180
287
|
}
|
|
181
288
|
|
|
182
289
|
const update: Record<string, unknown> = {
|
|
@@ -196,7 +303,7 @@ export function buildLoadCapabilityTool(
|
|
|
196
303
|
}
|
|
197
304
|
},
|
|
198
305
|
{
|
|
199
|
-
name:
|
|
306
|
+
name: LOAD_CAPABILITY_TOOL_NAME,
|
|
200
307
|
description:
|
|
201
308
|
"Load one or more capabilities for the rest of this conversation. Pass all capabilities you need in a single call — batching is preferred over multiple calls. The response is an array of plugin manifests plus tool lists; after this call, the new capabilities' tools are usable on the next model step.",
|
|
202
309
|
schema: loadCapabilitySchema,
|
|
@@ -207,12 +314,15 @@ export function buildLoadCapabilityTool(
|
|
|
207
314
|
// ── Bundle ──────────────────────────────────────────────────────────────────
|
|
208
315
|
|
|
209
316
|
/**
|
|
210
|
-
* Inputs for `buildMetaTools`. The runtime passes
|
|
211
|
-
*
|
|
317
|
+
* Inputs for `buildMetaTools`. The runtime passes the manifest registry and
|
|
318
|
+
* the turn's own collected tools (`turnToolSummaries`); a `ToolRegistry`
|
|
319
|
+
* also satisfies `toolRegistry`, but lists boot-time tools only.
|
|
212
320
|
*/
|
|
213
321
|
export interface BuildMetaToolsOptions {
|
|
214
322
|
manifestRegistry: ManifestRegistry;
|
|
215
|
-
toolRegistry:
|
|
323
|
+
toolRegistry: ToolSummarySource;
|
|
324
|
+
/** The turn's tool-plane result (`resolveTurnToolAccess`). */
|
|
325
|
+
toolAccess: MetaToolAccess;
|
|
216
326
|
}
|
|
217
327
|
|
|
218
328
|
/**
|
|
@@ -224,9 +334,9 @@ export interface BuildMetaToolsOptions {
|
|
|
224
334
|
* - `list_capabilities` — list every visible plugin with status flags.
|
|
225
335
|
*/
|
|
226
336
|
export function buildMetaTools(opts: BuildMetaToolsOptions): PluginTool[] {
|
|
227
|
-
const { manifestRegistry, toolRegistry } = opts;
|
|
337
|
+
const { manifestRegistry, toolRegistry, toolAccess } = opts;
|
|
228
338
|
return [
|
|
229
|
-
buildLoadCapabilityTool(manifestRegistry, toolRegistry),
|
|
230
|
-
buildListCapabilitiesTool(manifestRegistry),
|
|
339
|
+
buildLoadCapabilityTool(manifestRegistry, toolRegistry, toolAccess),
|
|
340
|
+
buildListCapabilitiesTool(manifestRegistry, toolAccess),
|
|
231
341
|
];
|
|
232
342
|
}
|
|
@@ -1,10 +1,24 @@
|
|
|
1
|
+
import { ToolMessage } from '@langchain/core/messages';
|
|
1
2
|
import { type AgentMiddleware, createMiddleware } from 'langchain';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
import type { Logger, PluginManifest } from '../../plugin-api/types';
|
|
5
|
+
import { describeRequirements, type CapabilityRequirement } from '../manifest';
|
|
4
6
|
import { NOOP_LOGGER } from '../utils';
|
|
5
7
|
|
|
6
8
|
type Visibility = NonNullable<PluginManifest['visibility']>;
|
|
7
9
|
|
|
10
|
+
/** `response_metadata` key the gate sets on its refusals (never sent to a provider). */
|
|
11
|
+
const REFUSED = 'capabilityGateRefused';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A ToolMessage the gate produced instead of running the tool. The tool never
|
|
15
|
+
* ran, so it is not a failure: the repetition guard must let the same call
|
|
16
|
+
* through once the capability is loaded.
|
|
17
|
+
*/
|
|
18
|
+
export function isCapabilityGateRefusal(message: ToolMessage): boolean {
|
|
19
|
+
return message.response_metadata?.[REFUSED] === true;
|
|
20
|
+
}
|
|
21
|
+
|
|
8
22
|
export interface CapabilityGateMiddlewareOptions {
|
|
9
23
|
/**
|
|
10
24
|
* Tool name → contributing plugin name. Tools absent from this map
|
|
@@ -18,29 +32,81 @@ export interface CapabilityGateMiddlewareOptions {
|
|
|
18
32
|
* middleware does not need access to the registries at runtime.
|
|
19
33
|
*/
|
|
20
34
|
visibilityByToolName: Map<string, Visibility>;
|
|
35
|
+
/**
|
|
36
|
+
* Plugins the capability router preloaded for this turn. Their on-demand
|
|
37
|
+
* tools pass the gate as if loaded, without touching `state.loadedPlugins`.
|
|
38
|
+
*/
|
|
39
|
+
preloadedPlugins?: ReadonlySet<string>;
|
|
40
|
+
/**
|
|
41
|
+
* Plugin name → the `manifest.requires` capabilities the user's delegation
|
|
42
|
+
* does not grant. Such a plugin's tools are hidden and refused whatever
|
|
43
|
+
* their visibility, loaded or preloaded.
|
|
44
|
+
*/
|
|
45
|
+
unmetRequirements?: ReadonlyMap<string, readonly CapabilityRequirement[]>;
|
|
46
|
+
/**
|
|
47
|
+
* Tool names the turn withholds (`resolveTurnToolAccess`): admin tools the
|
|
48
|
+
* user's delegation does not grant, never bound. A call naming one is
|
|
49
|
+
* refused without saying what it is, and not as a load refusal: loading
|
|
50
|
+
* cannot make it available.
|
|
51
|
+
*/
|
|
52
|
+
withheldToolNames?: ReadonlySet<string>;
|
|
21
53
|
/** Optional logger; defaults to a no-op. */
|
|
22
54
|
logger?: Logger;
|
|
23
55
|
}
|
|
24
56
|
|
|
25
57
|
/**
|
|
26
|
-
* Gates on-demand plugin tools (and sub-agents-as-tools)
|
|
58
|
+
* Gates on-demand plugin tools (and sub-agents-as-tools) on both sides of
|
|
59
|
+
* the model.
|
|
27
60
|
*
|
|
28
|
-
* All plugin tools are bound to the agent at compile time.
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
61
|
+
* All plugin tools are bound to the agent at compile time. On every model
|
|
62
|
+
* invocation this middleware reads `state.loadedPlugins` and trims the
|
|
63
|
+
* request's `tools` array down to what the agent should see at this point
|
|
64
|
+
* in the conversation. Because they are all bound, the tool node would still
|
|
65
|
+
* run a call to a hidden tool — one named from an earlier thread, guessed,
|
|
66
|
+
* or planted by injected text — so every tool call is checked against the
|
|
67
|
+
* same rule before it runs, and a hidden one is answered with an error
|
|
68
|
+
* instead of executing.
|
|
32
69
|
*
|
|
33
70
|
* Why a middleware: `createAgent({ tools })` freezes the bound list, so
|
|
34
71
|
* `load_capability` updating state mid-run would otherwise have no effect
|
|
35
72
|
* until the next request rebuilt the agent. Filtering inside `wrapModelCall`
|
|
36
73
|
* lets a load decision take effect on the very next LLM call.
|
|
74
|
+
*
|
|
75
|
+
* This is capability discovery, not authorization: any non-silent plugin can
|
|
76
|
+
* be loaded with `load_capability`. What the gate guarantees is that an
|
|
77
|
+
* on-demand tool runs only after that load (or the turn's preload) is
|
|
78
|
+
* visible in the thread.
|
|
37
79
|
*/
|
|
38
80
|
export const createCapabilityGateMiddleware = (
|
|
39
81
|
options: CapabilityGateMiddlewareOptions,
|
|
40
82
|
): AgentMiddleware => {
|
|
41
|
-
const { pluginByToolName, visibilityByToolName } = options;
|
|
83
|
+
const { pluginByToolName, visibilityByToolName, preloadedPlugins } = options;
|
|
42
84
|
const logger = options.logger ?? NOOP_LOGGER;
|
|
43
85
|
|
|
86
|
+
/** What the user's authorization lacks for `toolName`'s plugin, or null. */
|
|
87
|
+
const unmetFor = (
|
|
88
|
+
toolName: string,
|
|
89
|
+
): { plugin: string; missing: readonly CapabilityRequirement[] } | null => {
|
|
90
|
+
const plugin = pluginByToolName.get(toolName);
|
|
91
|
+
if (!plugin) return null;
|
|
92
|
+
const missing = options.unmetRequirements?.get(plugin);
|
|
93
|
+
return missing && missing.length > 0 ? { plugin, missing } : null;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/** The plugin that has to be loaded before `toolName` may be seen or run; null when it is open. */
|
|
97
|
+
const gatingPlugin = (
|
|
98
|
+
toolName: string,
|
|
99
|
+
loaded: ReadonlySet<string>,
|
|
100
|
+
): string | null => {
|
|
101
|
+
const plugin = pluginByToolName.get(toolName);
|
|
102
|
+
if (!plugin) return null;
|
|
103
|
+
const viz = visibilityByToolName.get(toolName) ?? 'on-demand';
|
|
104
|
+
if (viz === 'always' || viz === 'silent') return null;
|
|
105
|
+
if (loaded.has(plugin) || preloadedPlugins?.has(plugin) === true)
|
|
106
|
+
return null;
|
|
107
|
+
return plugin;
|
|
108
|
+
};
|
|
109
|
+
|
|
44
110
|
return createMiddleware({
|
|
45
111
|
name: 'CapabilityGateMiddleware',
|
|
46
112
|
stateSchema: z.object({
|
|
@@ -57,20 +123,72 @@ export const createCapabilityGateMiddleware = (
|
|
|
57
123
|
// `unknown`. Narrow at runtime; unknown-named tools pass through.
|
|
58
124
|
const name = typeof t.name === 'string' ? t.name : undefined;
|
|
59
125
|
if (!name) return true;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
126
|
+
return (
|
|
127
|
+
options.withheldToolNames?.has(name) !== true &&
|
|
128
|
+
unmetFor(name) === null &&
|
|
129
|
+
gatingPlugin(name, loaded) === null
|
|
130
|
+
);
|
|
65
131
|
});
|
|
66
132
|
|
|
67
133
|
if (filtered.length !== request.tools.length) {
|
|
68
134
|
logger.log(
|
|
69
|
-
`[CapabilityGateMiddleware] exposed ${filtered.length}/${request.tools.length} tools; loadedPlugins=${Array.from(loaded).join(',') || '∅'}
|
|
135
|
+
`[CapabilityGateMiddleware] exposed ${filtered.length}/${request.tools.length} tools; loadedPlugins=${Array.from(loaded).join(',') || '∅'}` +
|
|
136
|
+
(preloadedPlugins?.size
|
|
137
|
+
? ` preloadedPlugins=${Array.from(preloadedPlugins).join(',')}`
|
|
138
|
+
: ''),
|
|
70
139
|
);
|
|
71
140
|
}
|
|
72
141
|
|
|
73
142
|
return handler({ ...request, tools: filtered });
|
|
74
143
|
},
|
|
144
|
+
// The state here is the one the tool node runs with, so a load made by
|
|
145
|
+
// an earlier step of this run counts; a `load_capability` issued in the
|
|
146
|
+
// same model response as the gated call does not (both run on the state
|
|
147
|
+
// from before either), and the model is told to call again.
|
|
148
|
+
wrapToolCall: (request, handler) => {
|
|
149
|
+
const name = request.toolCall.name;
|
|
150
|
+
if (options.withheldToolNames?.has(name)) {
|
|
151
|
+
logger.warn(
|
|
152
|
+
`[CapabilityGateMiddleware] refused a call to ${name}: an admin tool the user's delegation does not grant`,
|
|
153
|
+
);
|
|
154
|
+
return new ToolMessage({
|
|
155
|
+
content: `Tool "${name}" is not available in this conversation, so the call was not run.`,
|
|
156
|
+
tool_call_id: request.toolCall.id ?? '',
|
|
157
|
+
name,
|
|
158
|
+
status: 'error',
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
const unmet = unmetFor(name);
|
|
162
|
+
if (unmet) {
|
|
163
|
+
logger.warn(
|
|
164
|
+
`[CapabilityGateMiddleware] refused a call to ${name}: the user's authorization does not grant ${describeRequirements(unmet.missing)} ('${unmet.plugin}' requires it)`,
|
|
165
|
+
);
|
|
166
|
+
return new ToolMessage({
|
|
167
|
+
content:
|
|
168
|
+
`Tool "${name}" was not run: it belongs to the "${unmet.plugin}" capability, which requires ${describeRequirements(unmet.missing)}, and the user's authorization for this oracle does not grant it. ` +
|
|
169
|
+
'Tell the user; they can re-authorize the oracle with that permission and ask again.',
|
|
170
|
+
tool_call_id: request.toolCall.id ?? '',
|
|
171
|
+
name,
|
|
172
|
+
status: 'error',
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
const plugin = gatingPlugin(
|
|
176
|
+
name,
|
|
177
|
+
new Set<string>(request.state.loadedPlugins ?? []),
|
|
178
|
+
);
|
|
179
|
+
if (plugin === null) return handler(request);
|
|
180
|
+
logger.warn(
|
|
181
|
+
`[CapabilityGateMiddleware] refused a call to ${name}: capability '${plugin}' is not loaded in this thread`,
|
|
182
|
+
);
|
|
183
|
+
return new ToolMessage({
|
|
184
|
+
content:
|
|
185
|
+
`Tool "${name}" is not available: it belongs to the "${plugin}" capability, which is not loaded in this conversation, so the call was not run. ` +
|
|
186
|
+
`If the user's request needs it, call load_capability with names ["${plugin}"] first, then call the tool again.`,
|
|
187
|
+
tool_call_id: request.toolCall.id ?? '',
|
|
188
|
+
name,
|
|
189
|
+
status: 'error',
|
|
190
|
+
response_metadata: { [REFUSED]: true },
|
|
191
|
+
});
|
|
192
|
+
},
|
|
75
193
|
});
|
|
76
194
|
};
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
* fail at the provider.
|
|
15
15
|
* 3. **Overflow recovery.** When the provider rejects the request anyway,
|
|
16
16
|
* the limit it names is learned (the window is lowered for good), the
|
|
17
|
-
* request is pruned hard, and it is retried once
|
|
17
|
+
* request is pruned hard, and it is retried once — only when the prune
|
|
18
|
+
* made it strictly smaller; an unchanged request is never sent twice.
|
|
18
19
|
*
|
|
19
20
|
* Token counts are chars/4 estimates; the thresholds leave the slack for it.
|
|
20
21
|
*/
|
|
@@ -26,7 +27,12 @@ import {
|
|
|
26
27
|
import { convertToOpenAITool } from '@langchain/core/utils/function_calling';
|
|
27
28
|
import { type AgentMiddleware, createMiddleware } from 'langchain';
|
|
28
29
|
import type { Logger } from '../../plugin-api/types';
|
|
29
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
estimateContentTokens,
|
|
32
|
+
messageContentTokens,
|
|
33
|
+
messageToolCallTokens,
|
|
34
|
+
type ContextBudget,
|
|
35
|
+
} from '../context-budget';
|
|
30
36
|
import { isContextOverflowError } from '../context-window';
|
|
31
37
|
import { NOOP_LOGGER } from '../utils';
|
|
32
38
|
import { cappedMetaOf } from './result-cap';
|
|
@@ -156,13 +162,52 @@ export function estimateRequestTokens(input: {
|
|
|
156
162
|
}): number {
|
|
157
163
|
let tokens = input.systemTokens + input.schemaTokens;
|
|
158
164
|
for (const m of input.messages) {
|
|
159
|
-
tokens +=
|
|
165
|
+
tokens += messageContentTokens(m) + 4;
|
|
160
166
|
if (AIMessage.isInstance(m) && m.tool_calls?.length)
|
|
161
|
-
tokens +=
|
|
167
|
+
tokens += messageToolCallTokens(m, m.tool_calls);
|
|
162
168
|
}
|
|
163
169
|
return tokens;
|
|
164
170
|
}
|
|
165
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Tool-schema sizes, kept across turns. Every turn binds fresh tool objects,
|
|
174
|
+
* but a plugin's tool keeps its schema object for the life of the isolate,
|
|
175
|
+
* and what a tool sends is fixed by that schema, its name and its
|
|
176
|
+
* description: the size is kept per schema object, then per name and
|
|
177
|
+
* description. A tool without a schema object is sized per tool object.
|
|
178
|
+
*/
|
|
179
|
+
const schemaSizes = new WeakMap<object, Map<string, number>>();
|
|
180
|
+
const entrySizes = new WeakMap<object, number>();
|
|
181
|
+
|
|
182
|
+
function schemaTokensOf(tools: readonly unknown[]): number {
|
|
183
|
+
let total = 0;
|
|
184
|
+
for (const entry of tools) {
|
|
185
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
186
|
+
const schema: unknown =
|
|
187
|
+
'schema' in entry ? Reflect.get(entry, 'schema') : undefined;
|
|
188
|
+
const sizes =
|
|
189
|
+
schema && typeof schema === 'object'
|
|
190
|
+
? (schemaSizes.get(schema) ??
|
|
191
|
+
schemaSizes.set(schema, new Map()).get(schema))
|
|
192
|
+
: undefined;
|
|
193
|
+
const key = `${String(Reflect.get(entry, 'name'))}\n${String(Reflect.get(entry, 'description'))}`;
|
|
194
|
+
let n = sizes ? sizes.get(key) : entrySizes.get(entry);
|
|
195
|
+
if (n === undefined) {
|
|
196
|
+
try {
|
|
197
|
+
n = estimateContentTokens(
|
|
198
|
+
'schema' in entry ? convertToOpenAITool(entry as never) : entry,
|
|
199
|
+
);
|
|
200
|
+
} catch {
|
|
201
|
+
n = 0;
|
|
202
|
+
}
|
|
203
|
+
if (sizes) sizes.set(key, n);
|
|
204
|
+
else entrySizes.set(entry, n);
|
|
205
|
+
}
|
|
206
|
+
total += n;
|
|
207
|
+
}
|
|
208
|
+
return total;
|
|
209
|
+
}
|
|
210
|
+
|
|
166
211
|
export function createContextGuardMiddleware(
|
|
167
212
|
options: ContextGuardOptions,
|
|
168
213
|
): AgentMiddleware {
|
|
@@ -177,26 +222,6 @@ export function createContextGuardMiddleware(
|
|
|
177
222
|
);
|
|
178
223
|
}
|
|
179
224
|
};
|
|
180
|
-
const schemaTokensCache = new WeakMap<object, number>();
|
|
181
|
-
const schemaTokensOf = (tools: unknown[]): number => {
|
|
182
|
-
let total = 0;
|
|
183
|
-
for (const entry of tools) {
|
|
184
|
-
if (!entry || typeof entry !== 'object') continue;
|
|
185
|
-
let n = schemaTokensCache.get(entry);
|
|
186
|
-
if (n === undefined) {
|
|
187
|
-
try {
|
|
188
|
-
n = estimateContentTokens(
|
|
189
|
-
'schema' in entry ? convertToOpenAITool(entry as never) : entry,
|
|
190
|
-
);
|
|
191
|
-
} catch {
|
|
192
|
-
n = 0;
|
|
193
|
-
}
|
|
194
|
-
schemaTokensCache.set(entry, n);
|
|
195
|
-
}
|
|
196
|
-
total += n;
|
|
197
|
-
}
|
|
198
|
-
return total;
|
|
199
|
-
};
|
|
200
225
|
return createMiddleware({
|
|
201
226
|
name: 'ContextGuardMiddleware',
|
|
202
227
|
wrapModelCall: async (request, handler) => {
|
|
@@ -254,7 +279,13 @@ export function createContextGuardMiddleware(
|
|
|
254
279
|
kind: 'overflow-retry',
|
|
255
280
|
...(learned !== undefined ? { learnedWindow: learned } : {}),
|
|
256
281
|
});
|
|
282
|
+
const before = tokens;
|
|
257
283
|
shrink(PRUNE_HARD, 'overflow', 'after the provider overflow');
|
|
284
|
+
if (tokens >= before)
|
|
285
|
+
throw new ContextOverflowError(
|
|
286
|
+
"The conversation no longer fits the model's context window and nothing more could be trimmed. Start a new session.",
|
|
287
|
+
error,
|
|
288
|
+
);
|
|
258
289
|
try {
|
|
259
290
|
return await handler({ ...request, messages });
|
|
260
291
|
} catch (retryError) {
|
|
@@ -2,8 +2,15 @@ export { createCapabilityGateMiddleware } from './capability-gate';
|
|
|
2
2
|
export type { CapabilityGateMiddlewareOptions } from './capability-gate';
|
|
3
3
|
export { createToolValidationMiddleware } from './tool-validation';
|
|
4
4
|
export type { ToolValidationMiddlewareOptions } from './tool-validation';
|
|
5
|
-
export {
|
|
6
|
-
|
|
5
|
+
export {
|
|
6
|
+
createToolRepetitionGuardMiddleware,
|
|
7
|
+
DEFAULT_REPETITION_CAPS,
|
|
8
|
+
repetitionCapsFromEnv,
|
|
9
|
+
} from './tool-repetition-guard';
|
|
10
|
+
export type {
|
|
11
|
+
RepetitionCaps,
|
|
12
|
+
ToolRepetitionGuardMiddlewareOptions,
|
|
13
|
+
} from './tool-repetition-guard';
|
|
7
14
|
export {
|
|
8
15
|
createSummarizationMiddleware,
|
|
9
16
|
isSummarizationMessage,
|
|
@@ -21,3 +28,14 @@ export { createPageContextMiddleware } from './page-context';
|
|
|
21
28
|
export type { PageContextMiddlewareOptions } from './page-context';
|
|
22
29
|
export { createSafetyGuardrailMiddleware } from './safety-guardrail';
|
|
23
30
|
export type { SafetyGuardrailMiddlewareOptions } from './safety-guardrail';
|
|
31
|
+
export {
|
|
32
|
+
createToolExecutionMiddleware,
|
|
33
|
+
isUncertainOutcome,
|
|
34
|
+
operationKey,
|
|
35
|
+
uncertainWriteToolResult,
|
|
36
|
+
} from './tool-execution';
|
|
37
|
+
export type {
|
|
38
|
+
ToolExecutionOptions,
|
|
39
|
+
WriteClaim,
|
|
40
|
+
WriteClaimStore,
|
|
41
|
+
} from './tool-execution';
|