@ixo/oracle-runtime-workers 0.37.2 → 0.39.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/package.json +4 -4
- package/src/attachments/channel-links.ts +74 -7
- package/src/attachments/channel.ts +15 -4
- package/src/attachments/content-blocks.ts +19 -16
- package/src/attachments/download.ts +173 -50
- package/src/attachments/index.ts +6 -5
- package/src/attachments/pipeline.ts +117 -49
- package/src/attachments/resize.ts +142 -0
- package/src/attachments/retention.ts +26 -70
- package/src/attachments/room-media.ts +643 -0
- package/src/attachments/route.ts +47 -4
- package/src/attachments/sandbox-archive.ts +15 -4
- package/src/attachments/sanitize.ts +24 -13
- package/src/attachments/view.ts +82 -38
- package/src/channels/replies.ts +8 -4
- package/src/core/capability-expiry.ts +62 -0
- package/src/core/capability-router.ts +126 -95
- package/src/core/context-budget.ts +116 -4
- package/src/core/domain-context/document.ts +74 -2
- package/src/core/domain-context/index.ts +1 -0
- package/src/core/domain-context/resolver.ts +9 -6
- package/src/core/domain-context/turn-context.ts +62 -13
- package/src/core/domain-context/types.ts +17 -0
- package/src/core/env.ts +21 -2
- package/src/core/index.ts +4 -0
- package/src/core/llm.ts +29 -0
- package/src/core/main-agent-types.ts +53 -4
- package/src/core/main-agent.ts +228 -125
- package/src/core/manifest.ts +109 -24
- package/src/core/mcp-result.ts +56 -0
- package/src/core/meta-tools.ts +258 -110
- package/src/core/middlewares/attachment-recall.ts +502 -0
- package/src/core/middlewares/capability-gate.ts +77 -28
- package/src/core/middlewares/context-guard.ts +154 -21
- package/src/core/middlewares/index.ts +5 -0
- package/src/core/middlewares/result-cap.ts +1 -1
- package/src/core/middlewares/summarization.ts +9 -0
- package/src/core/middlewares/tool-marks.ts +11 -2
- package/src/core/middlewares/turn-boundary.ts +22 -12
- package/src/core/operating-guides.ts +126 -0
- package/src/core/plugins/skills/skills-tools.ts +6 -31
- package/src/core/plugins/skills/skills.plugin.ts +4 -3
- package/src/core/plugins/weather/weather.plugin.ts +1 -0
- package/src/core/prompt-composer.ts +188 -111
- package/src/core/read-result-tool.ts +48 -14
- package/src/core/registries.ts +13 -5
- package/src/core/state.ts +4 -3
- package/src/core/sub-agent-fallback.ts +1 -0
- package/src/core/subagent-as-tool.ts +4 -1
- package/src/core/tool-access.ts +3 -32
- package/src/core/wrap-plugin-tool.ts +2 -1
- package/src/do/contracts.ts +64 -24
- package/src/do/room-mirror.ts +144 -20
- package/src/do/transcript.ts +41 -12
- package/src/do/turn-attachments.ts +45 -6
- package/src/do/turn-metadata.ts +16 -4
- package/src/do/user-oracle-do.ts +639 -152
- package/src/index.ts +1 -0
- package/src/llm/provider-error.ts +5 -2
- package/src/llm/stream-liveness.ts +3 -2
- package/src/matrix/gateway-do.ts +164 -102
- package/src/matrix/ingest.ts +12 -3
- package/src/matrix/media-deadline.ts +5 -47
- package/src/memory/session-history-indexer.ts +60 -14
- package/src/plugin-api/oracle-plugin.ts +4 -5
- package/src/plugin-api/types.ts +32 -3
- package/src/plugins/action-proposals/action-proposals.plugin.ts +7 -2
- package/src/plugins/agui/agui.plugin.ts +1 -0
- package/src/plugins/attachments/attachments.plugin.ts +3 -3
- package/src/plugins/attachments/view-attachment-tool.ts +6 -4
- package/src/plugins/compact-schema.ts +80 -0
- package/src/plugins/composio/composio.plugin.ts +1 -0
- package/src/plugins/domain-indexer/domain-indexer.plugin.ts +4 -3
- package/src/plugins/editor/editor.plugin.ts +4 -0
- package/src/plugins/firecrawl/firecrawl-agent.ts +2 -0
- package/src/plugins/firecrawl/firecrawl.plugin.ts +1 -0
- package/src/plugins/flows/flows.plugin.ts +37 -6
- package/src/plugins/flows/prompts.ts +1 -1
- package/src/plugins/flows/tools/authoring.ts +5 -7
- package/src/plugins/flows/tools/discovery.ts +1 -4
- package/src/plugins/flows/tools/forms.ts +2 -8
- package/src/plugins/flows/tools/linkage.ts +1 -4
- package/src/plugins/flows/tools/settings.ts +1 -4
- package/src/plugins/flows/translator.ts +16 -5
- package/src/plugins/flows/types.ts +27 -48
- package/src/plugins/hub-actions/hub-actions.plugin.ts +30 -4
- package/src/plugins/index.ts +2 -2
- package/src/plugins/ixo-chain/ixo-chain.plugin.ts +29 -5
- package/src/plugins/ixo-chain/kinds/core.ts +2 -2
- package/src/plugins/ixo-chain/kinds/types.ts +34 -5
- package/src/plugins/ixo-chain/queries.ts +10 -1
- package/src/plugins/ixo-transaction/ixo-transaction.plugin.ts +1 -0
- package/src/plugins/matrix-group-chats/index.ts +1 -0
- package/src/plugins/memory/memory-tools.ts +4 -4
- package/src/plugins/memory/memory.plugin.ts +27 -1
- package/src/plugins/memory/oracle-document-tools.ts +24 -49
- package/src/plugins/pod-creator/pod-creator.plugin.ts +1 -0
- package/src/plugins/portal/portal.plugin.ts +1 -0
- package/src/plugins/sandbox/sandbox-write-blob.ts +3 -15
- package/src/plugins/sandbox/sandbox.plugin.ts +19 -2
- package/src/plugins/tasks/manifest.ts +1 -0
- package/src/plugins/tasks/tasks-tools.ts +14 -19
- package/src/plugins/user-preferences/user-preferences-tool.ts +11 -37
- package/src/plugins/user-preferences/user-preferences.plugin.ts +4 -0
- package/src/plugins/vfs/vfs-tools.ts +6 -6
- package/src/plugins/vfs/vfs.plugin.ts +4 -5
- package/src/plugins/yoma/yoma.plugin.ts +1 -0
- package/src/sqlite/inline-payloads.ts +121 -0
- package/src/sqlite/serialization.ts +12 -0
- package/src/sqlite/sqlite-saver.ts +258 -42
- package/src/tasks/schedule-input.ts +5 -22
package/src/core/meta-tools.ts
CHANGED
|
@@ -5,11 +5,13 @@ import { tool } from '../plugin-api/tool-helper';
|
|
|
5
5
|
import type { PluginManifest, PluginTool } from '../plugin-api/types';
|
|
6
6
|
import {
|
|
7
7
|
describeRequirements,
|
|
8
|
+
effectiveToolVisibility,
|
|
8
9
|
unmetRequirements,
|
|
9
10
|
type CapabilityRequirement,
|
|
10
11
|
} from './manifest';
|
|
11
12
|
import type { ManifestRegistry, ToolSummarySource } from './registries';
|
|
12
13
|
import { withoutWithheldExamples, type TurnToolAccess } from './tool-access';
|
|
14
|
+
import { USAGE_CARRIED_KWARG } from './operating-guides';
|
|
13
15
|
import { acquireToolLock } from './utils';
|
|
14
16
|
|
|
15
17
|
export const LOAD_CAPABILITY_TOOL_NAME = 'load_capability';
|
|
@@ -27,14 +29,43 @@ export const META_TOOL_NAMES: readonly string[] = [
|
|
|
27
29
|
export type MetaToolAccess = Pick<
|
|
28
30
|
TurnToolAccess<never>,
|
|
29
31
|
'withheldToolNames' | 'hiddenPlugins'
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
>;
|
|
33
|
+
|
|
34
|
+
// ── Shared loading rules ────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
/** Whether `manifest`'s plugin still has a tool behind `load_capability`. */
|
|
37
|
+
function hasGatedTools(
|
|
38
|
+
pluginName: string,
|
|
39
|
+
manifest: PluginManifest,
|
|
40
|
+
toolSource: ToolSummarySource,
|
|
41
|
+
access: MetaToolAccess,
|
|
42
|
+
): boolean {
|
|
43
|
+
return toolSource
|
|
44
|
+
.toolSummariesForPlugin(pluginName)
|
|
45
|
+
.some(
|
|
46
|
+
(tool) =>
|
|
47
|
+
!access.withheldToolNames.has(tool.name) &&
|
|
48
|
+
effectiveToolVisibility(tool, manifest) === 'on-demand',
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Whether the plugin has nothing left to load: the thread loaded it, or it is
|
|
54
|
+
* `always` visible with no tool behind `load_capability`.
|
|
55
|
+
*/
|
|
56
|
+
function fullyBound(
|
|
57
|
+
pluginName: string,
|
|
58
|
+
manifest: PluginManifest,
|
|
59
|
+
loaded: ReadonlySet<string> | undefined,
|
|
60
|
+
toolSource: ToolSummarySource,
|
|
61
|
+
access: MetaToolAccess,
|
|
62
|
+
): boolean {
|
|
63
|
+
return (
|
|
64
|
+
loaded?.has(pluginName) === true ||
|
|
65
|
+
(manifest.visibility === 'always' &&
|
|
66
|
+
!hasGatedTools(pluginName, manifest, toolSource, access))
|
|
67
|
+
);
|
|
68
|
+
}
|
|
38
69
|
|
|
39
70
|
// ── list_capabilities ───────────────────────────────────────────────────────
|
|
40
71
|
|
|
@@ -68,13 +99,15 @@ interface CapabilityListing {
|
|
|
68
99
|
* Build the `list_capabilities` meta-tool.
|
|
69
100
|
*
|
|
70
101
|
* Returns one entry per loaded plugin, with a `loaded` flag that combines
|
|
71
|
-
* the boot-time always-on plugins (`visibility === 'always'
|
|
72
|
-
* of plugins the agent has explicitly
|
|
73
|
-
* with nothing by the turn's tool
|
|
74
|
-
* listed at all; one whose `requires`
|
|
102
|
+
* the boot-time always-on plugins (`visibility === 'always'`, with no tool
|
|
103
|
+
* behind `load_capability`) with the set of plugins the agent has explicitly
|
|
104
|
+
* loaded for this thread. A plugin left with nothing by the turn's tool
|
|
105
|
+
* planes (`access.hiddenPlugins`) is not listed at all; one whose `requires`
|
|
106
|
+
* are unmet is listed as `unavailable`.
|
|
75
107
|
*/
|
|
76
108
|
export function buildListCapabilitiesTool(
|
|
77
109
|
manifestRegistry: ManifestRegistry,
|
|
110
|
+
toolSource: ToolSummarySource,
|
|
78
111
|
access: MetaToolAccess,
|
|
79
112
|
): PluginTool {
|
|
80
113
|
return tool(
|
|
@@ -101,7 +134,7 @@ export function buildListCapabilitiesTool(
|
|
|
101
134
|
visibility,
|
|
102
135
|
loaded:
|
|
103
136
|
missing.length === 0 &&
|
|
104
|
-
(
|
|
137
|
+
fullyBound(pluginName, manifest, loadedSet, toolSource, access),
|
|
105
138
|
category: manifest.category,
|
|
106
139
|
tags: manifest.tags ?? [],
|
|
107
140
|
...(missing.length > 0 ? { unavailable: { missing } } : {}),
|
|
@@ -130,37 +163,154 @@ const loadCapabilitySchema = z.object({
|
|
|
130
163
|
.array(z.string())
|
|
131
164
|
.min(1)
|
|
132
165
|
.describe(
|
|
133
|
-
'One or more capability names to load,
|
|
166
|
+
'One or more capability names to load, from the on-demand capability list.',
|
|
134
167
|
),
|
|
135
168
|
});
|
|
136
169
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
170
|
+
/** The note a no-op load carries, in place of the plugin's manifest. */
|
|
171
|
+
const ALREADY_BOUND_NOTE =
|
|
172
|
+
'Nothing to load: these tools are already in your tool list. Call them directly.';
|
|
141
173
|
|
|
142
|
-
interface LoadCapabilityResult extends
|
|
174
|
+
interface LoadCapabilityResult extends Partial<
|
|
175
|
+
Pick<PluginManifest, 'summary' | 'whenToUse' | 'whenNotToUse' | 'examples'>
|
|
176
|
+
> {
|
|
177
|
+
name: string;
|
|
143
178
|
/**
|
|
144
|
-
* `true` when the plugin
|
|
145
|
-
*
|
|
179
|
+
* `true` when the plugin had nothing left to load, `false` when this call
|
|
180
|
+
* moved it into the loaded set (or refused it).
|
|
146
181
|
*/
|
|
147
182
|
alreadyAvailable: boolean;
|
|
148
|
-
/**
|
|
149
|
-
|
|
183
|
+
/**
|
|
184
|
+
* The plugin's tool names. Their definitions reach the model in its tool
|
|
185
|
+
* list on the next step, so repeating the descriptions here would pay for
|
|
186
|
+
* them twice, and for the rest of the conversation.
|
|
187
|
+
*/
|
|
188
|
+
tools: string[];
|
|
189
|
+
/** Present on a no-op load: says there is nothing to do. */
|
|
190
|
+
note?: string;
|
|
150
191
|
/**
|
|
151
192
|
* The plugin's working rules (`OraclePlugin.operatingGuide`), present when
|
|
152
|
-
* this call loaded it
|
|
153
|
-
*
|
|
154
|
-
* prompt, so it is not repeated.
|
|
193
|
+
* this call loaded it, once per turn, unless the system prompt carries them
|
|
194
|
+
* (`operating-guides.ts`).
|
|
155
195
|
*/
|
|
156
196
|
operatingGuide?: string;
|
|
157
197
|
/**
|
|
158
|
-
* Present when the plugin was NOT loaded
|
|
159
|
-
* does not grant what it requires (`manifest.requires
|
|
198
|
+
* Present when the plugin was NOT loaded: it does not exist, or the user's
|
|
199
|
+
* authorization does not grant what it requires (`manifest.requires`, the
|
|
200
|
+
* `missing` list).
|
|
160
201
|
*/
|
|
161
202
|
refused?: { missing: CapabilityRequirement[]; reason: string };
|
|
162
203
|
}
|
|
163
204
|
|
|
205
|
+
/**
|
|
206
|
+
* What a load tells the model, shared by `load_capability` and the capability
|
|
207
|
+
* gate's load on a hidden tool call. Built per turn: it remembers what the
|
|
208
|
+
* turn already loaded, which the tool context cannot show (a state update
|
|
209
|
+
* made earlier in the turn is not in it).
|
|
210
|
+
*/
|
|
211
|
+
export interface CapabilityLoader {
|
|
212
|
+
/** The plugin's tools the turn does not withhold. */
|
|
213
|
+
toolNames(pluginName: string): string[];
|
|
214
|
+
/**
|
|
215
|
+
* What the model needs to use the plugin: how to use it, the tool names,
|
|
216
|
+
* and its operating guide (once per turn) unless the prompt carries it.
|
|
217
|
+
* Records the plugin as loaded this turn.
|
|
218
|
+
*/
|
|
219
|
+
describe(
|
|
220
|
+
pluginName: string,
|
|
221
|
+
manifest: PluginManifest,
|
|
222
|
+
guideInPrompt: boolean,
|
|
223
|
+
): LoadCapabilityResult;
|
|
224
|
+
/** Whether this turn already loaded the plugin. */
|
|
225
|
+
loadedThisTurn(pluginName: string): boolean;
|
|
226
|
+
/** The gate's load: `describe` as a tool result. */
|
|
227
|
+
describeForGate(pluginName: string): GateLoadResult;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** What the capability gate's refusal adds to its tool message. */
|
|
231
|
+
export interface GateLoadResult {
|
|
232
|
+
text: string;
|
|
233
|
+
additional_kwargs: Record<string, unknown>;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** What marks a message as carrying the usage of `plugins`. */
|
|
237
|
+
function usageMark(plugins: readonly string[]): Record<string, unknown> {
|
|
238
|
+
return plugins.length > 0 ? { [USAGE_CARRIED_KWARG]: plugins } : {};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function createCapabilityLoader(
|
|
242
|
+
manifestRegistry: ManifestRegistry,
|
|
243
|
+
toolSource: ToolSummarySource,
|
|
244
|
+
access: MetaToolAccess,
|
|
245
|
+
): CapabilityLoader {
|
|
246
|
+
const loadedThisTurn = new Set<string>();
|
|
247
|
+
const guidesReturned = new Set<string>();
|
|
248
|
+
|
|
249
|
+
const toolNames = (pluginName: string): string[] =>
|
|
250
|
+
toolSource
|
|
251
|
+
.toolSummariesForPlugin(pluginName)
|
|
252
|
+
.filter((tool) => !access.withheldToolNames.has(tool.name))
|
|
253
|
+
.map((tool) => tool.name);
|
|
254
|
+
|
|
255
|
+
const describe: CapabilityLoader['describe'] = (
|
|
256
|
+
pluginName,
|
|
257
|
+
manifest,
|
|
258
|
+
guideInPrompt,
|
|
259
|
+
) => {
|
|
260
|
+
loadedThisTurn.add(pluginName);
|
|
261
|
+
const { summary, whenToUse, whenNotToUse, examples } =
|
|
262
|
+
withoutWithheldExamples(manifest, access.withheldToolNames);
|
|
263
|
+
const guide =
|
|
264
|
+
guideInPrompt ||
|
|
265
|
+
manifest.visibility === 'always' ||
|
|
266
|
+
guidesReturned.has(pluginName)
|
|
267
|
+
? undefined
|
|
268
|
+
: manifestRegistry.operatingGuide(pluginName);
|
|
269
|
+
if (guide) guidesReturned.add(pluginName);
|
|
270
|
+
return {
|
|
271
|
+
name: pluginName,
|
|
272
|
+
alreadyAvailable: false,
|
|
273
|
+
tools: toolNames(pluginName),
|
|
274
|
+
summary,
|
|
275
|
+
whenToUse,
|
|
276
|
+
whenNotToUse,
|
|
277
|
+
examples,
|
|
278
|
+
...(guide ? { operatingGuide: guide } : {}),
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
toolNames,
|
|
284
|
+
describe,
|
|
285
|
+
loadedThisTurn: (pluginName) => loadedThisTurn.has(pluginName),
|
|
286
|
+
describeForGate: (pluginName) => {
|
|
287
|
+
const entry = manifestRegistry
|
|
288
|
+
.collect()
|
|
289
|
+
.find((m) => m.pluginName === pluginName);
|
|
290
|
+
if (!entry) return { text: '', additional_kwargs: {} };
|
|
291
|
+
return {
|
|
292
|
+
text: JSON.stringify(describe(pluginName, entry.manifest, false)),
|
|
293
|
+
additional_kwargs: usageMark([pluginName]),
|
|
294
|
+
};
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** The names the error for an unknown capability offers. */
|
|
300
|
+
function loadableNames(
|
|
301
|
+
manifestRegistry: ManifestRegistry,
|
|
302
|
+
access: MetaToolAccess,
|
|
303
|
+
): string[] {
|
|
304
|
+
return manifestRegistry
|
|
305
|
+
.collect()
|
|
306
|
+
.filter(
|
|
307
|
+
({ pluginName, manifest }) =>
|
|
308
|
+
manifest.visibility !== 'silent' &&
|
|
309
|
+
!access.hiddenPlugins.has(pluginName),
|
|
310
|
+
)
|
|
311
|
+
.map(({ pluginName }) => pluginName);
|
|
312
|
+
}
|
|
313
|
+
|
|
164
314
|
/**
|
|
165
315
|
* Build the `load_capability` meta-tool.
|
|
166
316
|
*
|
|
@@ -168,22 +318,25 @@ interface LoadCapabilityResult extends PluginManifest {
|
|
|
168
318
|
* capabilities into a single call. A per-session lock ensures the tool
|
|
169
319
|
* cannot be invoked in parallel — concurrent calls throw immediately.
|
|
170
320
|
*
|
|
171
|
-
*
|
|
321
|
+
* Every name is judged before any is loaded, and a name that cannot be loaded
|
|
322
|
+
* never stops the rest of the batch. Behavior per name:
|
|
172
323
|
* - Unknown plugin, or one the turn's tool planes left with nothing
|
|
173
324
|
* (`access.hiddenPlugins`, answered exactly like an unknown one) →
|
|
174
|
-
*
|
|
175
|
-
* - `silent` plugin →
|
|
325
|
+
* `refused`, naming the capabilities that can be loaded.
|
|
326
|
+
* - `silent` plugin → `refused` (silent plugins are not agent-loadable).
|
|
176
327
|
* - Plugin whose `requires` the user's delegation does not grant →
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* with `alreadyAvailable: true
|
|
328
|
+
* `refused`, with what is missing.
|
|
329
|
+
* - Plugin with nothing left to load: loaded on the thread or earlier this
|
|
330
|
+
* turn, or `always` visible with no tool behind `load_capability` → a
|
|
331
|
+
* no-op entry with `alreadyAvailable: true`, its tool names and a note
|
|
332
|
+
* saying so (no state change for that plugin). An `always` plugin with
|
|
333
|
+
* `on-demand` tools does load, once.
|
|
181
334
|
* - Otherwise → added to the `loadedPlugins` state update.
|
|
182
335
|
*
|
|
183
|
-
* The tool
|
|
184
|
-
* (`access.withheldToolNames`). A plugin this call loads
|
|
185
|
-
*
|
|
186
|
-
*
|
|
336
|
+
* The tool names and `examples` leave out the tools the turn withholds
|
|
337
|
+
* (`access.withheldToolNames`). A plugin this call loads comes with its usage
|
|
338
|
+
* and its `operatingGuide` when it has one, once per turn, and the result is
|
|
339
|
+
* marked as carrying them; a refused one never does.
|
|
187
340
|
*
|
|
188
341
|
* Return value:
|
|
189
342
|
* - If all requested plugins were already available: returns the result
|
|
@@ -196,89 +349,80 @@ export function buildLoadCapabilityTool(
|
|
|
196
349
|
manifestRegistry: ManifestRegistry,
|
|
197
350
|
toolSource: ToolSummarySource,
|
|
198
351
|
access: MetaToolAccess,
|
|
352
|
+
loader: CapabilityLoader = createCapabilityLoader(
|
|
353
|
+
manifestRegistry,
|
|
354
|
+
toolSource,
|
|
355
|
+
access,
|
|
356
|
+
),
|
|
199
357
|
): 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>();
|
|
203
358
|
return tool(
|
|
204
359
|
async (args, ctx) => {
|
|
205
360
|
const { names } = loadCapabilitySchema.parse(args);
|
|
206
361
|
|
|
207
362
|
const releaseLock = acquireToolLock(`${ctx.session.id}:load_capability`);
|
|
208
363
|
try {
|
|
209
|
-
const
|
|
210
|
-
|
|
364
|
+
const refused = (
|
|
365
|
+
name: string,
|
|
366
|
+
reason: string,
|
|
367
|
+
missing: CapabilityRequirement[] = [],
|
|
368
|
+
): LoadCapabilityResult => ({
|
|
369
|
+
name,
|
|
370
|
+
alreadyAvailable: false,
|
|
371
|
+
tools: [],
|
|
372
|
+
refused: { missing, reason },
|
|
373
|
+
});
|
|
374
|
+
const nothingToLoad = (name: string): LoadCapabilityResult => ({
|
|
375
|
+
name,
|
|
376
|
+
alreadyAvailable: true,
|
|
377
|
+
tools: loader.toolNames(name),
|
|
378
|
+
note: ALREADY_BOUND_NOTE,
|
|
379
|
+
});
|
|
211
380
|
|
|
212
|
-
|
|
381
|
+
// Judged first: `describe` records the load, so it runs only for what
|
|
382
|
+
// this call really loads.
|
|
383
|
+
const judged = names.map((name) => {
|
|
213
384
|
const entry = manifestRegistry
|
|
214
385
|
.collect()
|
|
215
386
|
.find((m) => m.pluginName === name);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
`Capability "${name}" does not exist.
|
|
387
|
+
if (!entry || access.hiddenPlugins.has(name))
|
|
388
|
+
return refused(
|
|
389
|
+
name,
|
|
390
|
+
`Capability "${name}" does not exist. Loadable capabilities: ${loadableNames(manifestRegistry, access).join(', ')}.`,
|
|
220
391
|
);
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
`Capability "${name}" is internal and cannot be loaded by the agent. Call list_capabilities first to discover loadable plugins.`,
|
|
392
|
+
if (entry.manifest.visibility === 'silent')
|
|
393
|
+
return refused(
|
|
394
|
+
name,
|
|
395
|
+
`Capability "${name}" is internal and cannot be loaded by the agent. Loadable capabilities: ${loadableNames(manifestRegistry, access).join(', ')}.`,
|
|
226
396
|
);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
397
|
const missing = unmetRequirements(
|
|
230
398
|
entry.manifest,
|
|
231
399
|
ctx.ucan.hasCapability,
|
|
232
400
|
);
|
|
233
|
-
if (missing.length > 0)
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const alwaysVisible = entry.manifest.visibility === 'always';
|
|
259
|
-
const alreadyAvailable = alreadyLoaded || alwaysVisible;
|
|
260
|
-
|
|
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
|
-
});
|
|
277
|
-
|
|
278
|
-
if (!alreadyAvailable) {
|
|
279
|
-
newToLoad.push(name);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
401
|
+
if (missing.length > 0)
|
|
402
|
+
return refused(
|
|
403
|
+
name,
|
|
404
|
+
`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.`,
|
|
405
|
+
missing,
|
|
406
|
+
);
|
|
407
|
+
return fullyBound(
|
|
408
|
+
name,
|
|
409
|
+
entry.manifest,
|
|
410
|
+
ctx.loadedPlugins,
|
|
411
|
+
toolSource,
|
|
412
|
+
access,
|
|
413
|
+
)
|
|
414
|
+
? nothingToLoad(name)
|
|
415
|
+
: entry.manifest;
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
const newToLoad: string[] = [];
|
|
419
|
+
const results = judged.map((verdict, index) => {
|
|
420
|
+
if ('alreadyAvailable' in verdict) return verdict;
|
|
421
|
+
const name = names[index] ?? '';
|
|
422
|
+
if (loader.loadedThisTurn(name)) return nothingToLoad(name);
|
|
423
|
+
newToLoad.push(name);
|
|
424
|
+
return loader.describe(name, verdict, false);
|
|
425
|
+
});
|
|
282
426
|
|
|
283
427
|
// Text, never the bare array: LangChain would pass an array of
|
|
284
428
|
// objects through as the message's content blocks.
|
|
@@ -290,10 +434,12 @@ export function buildLoadCapabilityTool(
|
|
|
290
434
|
loadedPlugins: newToLoad,
|
|
291
435
|
};
|
|
292
436
|
if (ctx.toolCallId) {
|
|
437
|
+
update.capabilityUsage = newToLoad;
|
|
293
438
|
update.messages = [
|
|
294
439
|
new ToolMessage({
|
|
295
440
|
content: JSON.stringify(results),
|
|
296
441
|
tool_call_id: ctx.toolCallId,
|
|
442
|
+
additional_kwargs: usageMark(newToLoad),
|
|
297
443
|
}),
|
|
298
444
|
];
|
|
299
445
|
}
|
|
@@ -305,7 +451,7 @@ export function buildLoadCapabilityTool(
|
|
|
305
451
|
{
|
|
306
452
|
name: LOAD_CAPABILITY_TOOL_NAME,
|
|
307
453
|
description:
|
|
308
|
-
|
|
454
|
+
'Load capabilities that are not in your tool list yet. Pass all you need in one call. Returns how to use each and its tool names; the tools are usable on your next step. A capability left unused for a while is unloaded; load it again when you need it. Never needed for a tool already in your tool list.',
|
|
309
455
|
schema: loadCapabilitySchema,
|
|
310
456
|
},
|
|
311
457
|
);
|
|
@@ -323,6 +469,8 @@ export interface BuildMetaToolsOptions {
|
|
|
323
469
|
toolRegistry: ToolSummarySource;
|
|
324
470
|
/** The turn's tool-plane result (`resolveTurnToolAccess`). */
|
|
325
471
|
toolAccess: MetaToolAccess;
|
|
472
|
+
/** The turn's loader, when the capability gate shares it. */
|
|
473
|
+
loader?: CapabilityLoader;
|
|
326
474
|
}
|
|
327
475
|
|
|
328
476
|
/**
|
|
@@ -330,13 +478,13 @@ export interface BuildMetaToolsOptions {
|
|
|
330
478
|
* plugins are loaded:
|
|
331
479
|
*
|
|
332
480
|
* - `load_capability` — mark a plugin as loaded for this thread; returns
|
|
333
|
-
*
|
|
481
|
+
* how to use it + its tool names.
|
|
334
482
|
* - `list_capabilities` — list every visible plugin with status flags.
|
|
335
483
|
*/
|
|
336
484
|
export function buildMetaTools(opts: BuildMetaToolsOptions): PluginTool[] {
|
|
337
|
-
const { manifestRegistry, toolRegistry, toolAccess } = opts;
|
|
485
|
+
const { manifestRegistry, toolRegistry, toolAccess, loader } = opts;
|
|
338
486
|
return [
|
|
339
|
-
buildLoadCapabilityTool(manifestRegistry, toolRegistry, toolAccess),
|
|
340
|
-
buildListCapabilitiesTool(manifestRegistry, toolAccess),
|
|
487
|
+
buildLoadCapabilityTool(manifestRegistry, toolRegistry, toolAccess, loader),
|
|
488
|
+
buildListCapabilitiesTool(manifestRegistry, toolRegistry, toolAccess),
|
|
341
489
|
];
|
|
342
490
|
}
|