@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/main-agent.ts
CHANGED
|
@@ -17,17 +17,30 @@ import type {
|
|
|
17
17
|
} from '../plugin-api/types';
|
|
18
18
|
import type { MainAgentArgs, MainAgentBuildResult } from './main-agent-types';
|
|
19
19
|
import {
|
|
20
|
+
effectiveToolVisibility,
|
|
20
21
|
renderTier1,
|
|
21
22
|
unmetRequirements as unmetManifestRequirements,
|
|
22
23
|
type CapabilityRequirement,
|
|
24
|
+
type CatalogueEntry,
|
|
23
25
|
type Tier1Entry,
|
|
24
26
|
} from './manifest';
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
+
import { idlePlugins } from './capability-expiry';
|
|
28
|
+
import {
|
|
29
|
+
buildMetaTools,
|
|
30
|
+
createCapabilityLoader,
|
|
31
|
+
META_TOOL_NAMES,
|
|
32
|
+
} from './meta-tools';
|
|
33
|
+
import {
|
|
34
|
+
buildReadResultTool,
|
|
35
|
+
READ_RESULT_TOOL_NAME,
|
|
36
|
+
readResultMaxChars,
|
|
37
|
+
} from './read-result-tool';
|
|
27
38
|
import { describeBudget } from './context-budget';
|
|
39
|
+
import { planCapabilityContext, renderGuideNote } from './operating-guides';
|
|
28
40
|
import { createContextGuardMiddleware } from './middlewares/context-guard';
|
|
29
41
|
import { repetitionCapsFromEnv } from './middlewares/tool-repetition-guard';
|
|
30
42
|
import {
|
|
43
|
+
createAttachmentRecallMiddleware,
|
|
31
44
|
createByoHistorySanitizerMiddleware,
|
|
32
45
|
createCapabilityGateMiddleware,
|
|
33
46
|
createDanglingToolCallRepairMiddleware,
|
|
@@ -62,7 +75,6 @@ import {
|
|
|
62
75
|
dropShadowingRequestEntries,
|
|
63
76
|
formatByPlugin,
|
|
64
77
|
turnToolSummaries,
|
|
65
|
-
type ManifestRegistry,
|
|
66
78
|
} from './registries';
|
|
67
79
|
import {
|
|
68
80
|
buildPluginContext,
|
|
@@ -89,12 +101,6 @@ import { ixoAddressOf, renderUserIdentity } from './user-identity';
|
|
|
89
101
|
|
|
90
102
|
const PLUGIN_LOGGER_COMPONENT = 'main-agent';
|
|
91
103
|
|
|
92
|
-
const DEFAULT_OPERATIONAL_MODE = [
|
|
93
|
-
'**General conversation mode**',
|
|
94
|
-
'',
|
|
95
|
-
'Default to conversation mode, using available capabilities for recall, search, and task delegation.',
|
|
96
|
-
].join('\n');
|
|
97
|
-
|
|
98
104
|
type CollectedTool = { pluginName: string; tool: PluginTool };
|
|
99
105
|
|
|
100
106
|
/** The validated env values the user-identity section names. */
|
|
@@ -104,31 +110,16 @@ const identityConfigSchema = z.object({
|
|
|
104
110
|
ORACLE_ENTITY_DID: z.string().min(1).optional(),
|
|
105
111
|
});
|
|
106
112
|
|
|
107
|
-
/**
|
|
108
|
-
function visibilityIndex(
|
|
109
|
-
manifests: ManifestRegistry,
|
|
110
|
-
): Map<string, NonNullable<PluginManifest['visibility']>> {
|
|
111
|
-
const out = new Map<string, NonNullable<PluginManifest['visibility']>>();
|
|
112
|
-
for (const { pluginName, manifest } of manifests.collect()) {
|
|
113
|
-
out.set(pluginName, manifest.visibility ?? 'on-demand');
|
|
114
|
-
}
|
|
115
|
-
return out;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Filter collected tools by their effective visibility — per-tool override
|
|
120
|
-
* wins; otherwise the plugin's manifest visibility decides.
|
|
121
|
-
*/
|
|
113
|
+
/** Filter collected tools by their effective visibility. */
|
|
122
114
|
function selectByVisibility(
|
|
123
115
|
tools: CollectedTool[],
|
|
124
|
-
|
|
116
|
+
manifestOf: Map<string, PluginManifest>,
|
|
125
117
|
visibility: NonNullable<PluginManifest['visibility']>,
|
|
126
118
|
): CollectedTool[] {
|
|
127
|
-
return tools.filter(
|
|
128
|
-
|
|
129
|
-
tool
|
|
130
|
-
|
|
131
|
-
});
|
|
119
|
+
return tools.filter(
|
|
120
|
+
({ pluginName, tool }) =>
|
|
121
|
+
effectiveToolVisibility(tool, manifestOf.get(pluginName)) === visibility,
|
|
122
|
+
);
|
|
132
123
|
}
|
|
133
124
|
|
|
134
125
|
/**
|
|
@@ -172,7 +163,7 @@ export async function createMainAgent(
|
|
|
172
163
|
ambient,
|
|
173
164
|
state,
|
|
174
165
|
availablePlugins,
|
|
175
|
-
|
|
166
|
+
routeCapabilities,
|
|
176
167
|
checkpointer,
|
|
177
168
|
abortSignal,
|
|
178
169
|
byoProvider,
|
|
@@ -248,6 +239,7 @@ export async function createMainAgent(
|
|
|
248
239
|
toolSurfaces: new Map(),
|
|
249
240
|
subAgentToolNames: new Set(),
|
|
250
241
|
context: { user: requestCtx.user, session: requestCtx.session },
|
|
242
|
+
stateUpdate: {},
|
|
251
243
|
};
|
|
252
244
|
}
|
|
253
245
|
|
|
@@ -266,14 +258,12 @@ export async function createMainAgent(
|
|
|
266
258
|
state.loadedPlugins,
|
|
267
259
|
);
|
|
268
260
|
|
|
269
|
-
// What the
|
|
270
|
-
//
|
|
271
|
-
//
|
|
272
|
-
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
...(preloadedPlugins ?? []),
|
|
276
|
-
]);
|
|
261
|
+
// What the thread loaded when the turn starts. Tools and sub-agents are
|
|
262
|
+
// collected first: what is left to load depends on them. A cold turn then
|
|
263
|
+
// drops plugins from this set and the router adds to it, below; it is one
|
|
264
|
+
// set from there on, also what tool handlers see as loaded.
|
|
265
|
+
const priorLoaded: ReadonlySet<string> = new Set(state.loadedPlugins ?? []);
|
|
266
|
+
const loadedSet = new Set<string>(priorLoaded);
|
|
277
267
|
// Carry the prior request state (browserTools, agActions, …) into the
|
|
278
268
|
// per-request RuntimeContext and the tool-wrapper closures, but NOT the
|
|
279
269
|
// message history: keeping the full `messages` array here would pin the
|
|
@@ -410,22 +400,6 @@ export async function createMainAgent(
|
|
|
410
400
|
const missing = unmetManifestRequirements(manifest, hasCapability);
|
|
411
401
|
if (missing.length > 0) unmetRequirements.set(pluginName, missing);
|
|
412
402
|
}
|
|
413
|
-
// The router predicts before the tools are collected (see
|
|
414
|
-
// `routableCandidates`): its preload of a plugin left with nothing, or of
|
|
415
|
-
// one whose requirements the delegation does not meet, is void, for the
|
|
416
|
-
// gate and for what handlers see as loaded.
|
|
417
|
-
const turnPreloads = preloadedPlugins
|
|
418
|
-
? new Set(
|
|
419
|
-
[...preloadedPlugins].filter(
|
|
420
|
-
(pluginName) =>
|
|
421
|
-
!toolAccess.hiddenPlugins.has(pluginName) &&
|
|
422
|
-
!unmetRequirements.has(pluginName),
|
|
423
|
-
),
|
|
424
|
-
)
|
|
425
|
-
: undefined;
|
|
426
|
-
for (const pluginName of preloadedPlugins ?? [])
|
|
427
|
-
if (!turnPreloads?.has(pluginName) && !threadLoaded.has(pluginName))
|
|
428
|
-
loadedSet.delete(pluginName);
|
|
429
403
|
|
|
430
404
|
// The per-turn tool-surface line. Request tools are named in full (there
|
|
431
405
|
// are only a handful and they are the ones that vary turn to turn); the
|
|
@@ -439,7 +413,9 @@ export async function createMainAgent(
|
|
|
439
413
|
ambient.logger.debug?.(
|
|
440
414
|
`[main-agent] full tool surface: ${formatByPlugin(allTools) || '∅'}`,
|
|
441
415
|
);
|
|
442
|
-
const
|
|
416
|
+
const manifestOf = new Map(
|
|
417
|
+
manifestEntries.map(({ pluginName, manifest }) => [pluginName, manifest]),
|
|
418
|
+
);
|
|
443
419
|
if (unmetRequirements.size > 0)
|
|
444
420
|
ambient.logger.log(
|
|
445
421
|
`[main-agent] not usable by ${requestCtx.user.did} (authorization lacks what they require): ${[...unmetRequirements.keys()].join(', ')}`,
|
|
@@ -451,27 +427,152 @@ export async function createMainAgent(
|
|
|
451
427
|
]),
|
|
452
428
|
);
|
|
453
429
|
|
|
454
|
-
|
|
430
|
+
// Lookups used by `CapabilityGateMiddleware` to gate on-demand plugins
|
|
431
|
+
// and sub-agents per model call. Meta-tools omitted from the map are
|
|
432
|
+
// pass-through.
|
|
433
|
+
const pluginByToolName = new Map<string, string>();
|
|
434
|
+
const visibilityByToolName = new Map<
|
|
435
|
+
string,
|
|
436
|
+
NonNullable<PluginManifest['visibility']>
|
|
437
|
+
>();
|
|
438
|
+
for (const { pluginName, tool } of allTools) {
|
|
439
|
+
pluginByToolName.set(tool.name, pluginName);
|
|
440
|
+
visibilityByToolName.set(
|
|
441
|
+
tool.name,
|
|
442
|
+
effectiveToolVisibility(tool, manifestOf.get(pluginName)),
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
for (const { pluginName, subAgent } of subAgentEntries) {
|
|
446
|
+
const toolName = computeSubAgentToolName(subAgent.name);
|
|
447
|
+
pluginByToolName.set(toolName, pluginName);
|
|
448
|
+
visibilityByToolName.set(
|
|
449
|
+
toolName,
|
|
450
|
+
effectiveToolVisibility({ name: toolName }, manifestOf.get(pluginName)),
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// This turn's own collection (the shared registry holds no request tools),
|
|
455
|
+
// already cut to what the delegation reaches. A sub-agent counts: to the
|
|
456
|
+
// model its `call_*` tool is what a plugin like firecrawl offers.
|
|
457
|
+
const turnSummaries = turnToolSummaries([
|
|
458
|
+
...toolAccess.tools,
|
|
459
|
+
...subAgentEntries.map(({ pluginName, subAgent }) => ({
|
|
460
|
+
pluginName,
|
|
461
|
+
tool: {
|
|
462
|
+
name: computeSubAgentToolName(subAgent.name),
|
|
463
|
+
description: subAgent.description,
|
|
464
|
+
},
|
|
465
|
+
})),
|
|
466
|
+
]);
|
|
467
|
+
// Shared by `load_capability` and the capability gate's load on a hidden
|
|
468
|
+
// call, so one turn reads a plugin's guide once whichever loaded it.
|
|
469
|
+
const capabilityLoader = createCapabilityLoader(
|
|
470
|
+
registries.manifests,
|
|
471
|
+
turnSummaries,
|
|
472
|
+
toolAccess,
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
// A cold turn drops the capabilities the thread has moved on from, from the
|
|
476
|
+
// gate, the prompt and what handlers see as loaded. The caller writes the
|
|
477
|
+
// same change to the graph (capability-expiry.ts).
|
|
478
|
+
const expiredPlugins = args.capabilityExpiry
|
|
479
|
+
? idlePlugins({
|
|
480
|
+
messages: args.capabilityExpiry.messages,
|
|
481
|
+
loaded: args.capabilityExpiry.loaded,
|
|
482
|
+
pluginOf: (toolName) => pluginByToolName.get(toolName),
|
|
483
|
+
maxIdleTurns: args.capabilityExpiry.maxIdleTurns,
|
|
484
|
+
})
|
|
485
|
+
: [];
|
|
486
|
+
for (const pluginName of expiredPlugins) loadedSet.delete(pluginName);
|
|
487
|
+
if (expiredPlugins.length > 0)
|
|
488
|
+
ambient.logger.log(
|
|
489
|
+
`[main-agent] unloaded idle capabilities: ${expiredPlugins.join(', ')}`,
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
// One line per capability with something left to load this turn: named by
|
|
493
|
+
// the turn's own tools, so a plugin with nothing to offer this turn (no
|
|
494
|
+
// browser tools declared, no editor open) stays out; one the user's
|
|
495
|
+
// authorization lacks stays in, and `load_capability` says what is missing.
|
|
496
|
+
const gatedPlugins = new Set(
|
|
497
|
+
[...visibilityByToolName]
|
|
498
|
+
.filter(([, visibility]) => visibility === 'on-demand')
|
|
499
|
+
.flatMap(([toolName]) => pluginByToolName.get(toolName) ?? []),
|
|
500
|
+
);
|
|
501
|
+
const catalogue: CatalogueEntry[] = manifestEntries
|
|
502
|
+
.filter(
|
|
503
|
+
({ pluginName, manifest }) =>
|
|
504
|
+
manifest.visibility !== 'silent' &&
|
|
505
|
+
!toolAccess.hiddenPlugins.has(pluginName) &&
|
|
506
|
+
(gatedPlugins.has(pluginName) || unmetRequirements.has(pluginName)),
|
|
507
|
+
)
|
|
508
|
+
.map((entry) => ({
|
|
509
|
+
...entry,
|
|
510
|
+
unavailable: unmetRequirements.has(entry.pluginName),
|
|
511
|
+
}));
|
|
512
|
+
|
|
513
|
+
// The router chooses from what a `load_capability` could load right now, so
|
|
514
|
+
// what it returns needs no further check. A preload is such a load, made
|
|
515
|
+
// before the first model call: the tool list changes once, then stays.
|
|
516
|
+
const routable = catalogue.filter(
|
|
517
|
+
({ pluginName, manifest, unavailable }) =>
|
|
518
|
+
manifest.routable !== false && !unavailable && !loadedSet.has(pluginName),
|
|
519
|
+
);
|
|
520
|
+
const routed =
|
|
521
|
+
routable.length > 0 ? await routeCapabilities?.(routable) : undefined;
|
|
522
|
+
const preloaded = routable
|
|
523
|
+
.map(({ pluginName }) => pluginName)
|
|
524
|
+
.filter((pluginName) => routed?.has(pluginName));
|
|
525
|
+
for (const pluginName of preloaded) loadedSet.add(pluginName);
|
|
526
|
+
|
|
527
|
+
// Where each plugin's guide and usage go this turn. A plugin the model loads
|
|
528
|
+
// during the turn gets both with the load result, whatever is decided here.
|
|
529
|
+
const usablePlugin = (pluginName: string): boolean =>
|
|
530
|
+
manifestOf.get(pluginName) !== undefined &&
|
|
531
|
+
manifestOf.get(pluginName)?.visibility !== 'silent' &&
|
|
532
|
+
!unmetRequirements.has(pluginName) &&
|
|
533
|
+
!toolAccess.hiddenPlugins.has(pluginName);
|
|
534
|
+
const capabilityPlan = planCapabilityContext({
|
|
535
|
+
guides: registries.manifests
|
|
536
|
+
.operatingGuides()
|
|
537
|
+
.filter(({ pluginName }) => usablePlugin(pluginName)),
|
|
538
|
+
loaded: loadedSet,
|
|
539
|
+
priorLoaded,
|
|
540
|
+
usable: usablePlugin,
|
|
541
|
+
alwaysVisible: (pluginName) =>
|
|
542
|
+
manifestOf.get(pluginName)?.visibility === 'always',
|
|
543
|
+
...(args.promptCache ? { cache: args.promptCache } : {}),
|
|
544
|
+
});
|
|
545
|
+
const guidesInPrompt = new Set(
|
|
546
|
+
capabilityPlan.inPrompt.map(({ pluginName }) => pluginName),
|
|
547
|
+
);
|
|
548
|
+
const notedLoads = capabilityPlan.onMessage.flatMap((pluginName) => {
|
|
549
|
+
const manifest = manifestOf.get(pluginName);
|
|
550
|
+
return manifest
|
|
551
|
+
? [
|
|
552
|
+
capabilityLoader.describe(
|
|
553
|
+
pluginName,
|
|
554
|
+
manifest,
|
|
555
|
+
guidesInPrompt.has(pluginName),
|
|
556
|
+
),
|
|
557
|
+
]
|
|
558
|
+
: [];
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
const eagerTools = selectByVisibility(allTools, manifestOf, 'always');
|
|
455
562
|
// Bind ALL on-demand tools at compile time — gating happens per model call
|
|
456
563
|
// in `CapabilityGateMiddleware` based on the live `loadedPlugins` state.
|
|
457
564
|
// This lets `load_capability` take effect on the very next LLM call inside
|
|
458
565
|
// the same run, instead of waiting for the next request to rebuild.
|
|
459
|
-
const onDemandTools = selectByVisibility(allTools,
|
|
460
|
-
const silentTools = selectByVisibility(allTools,
|
|
566
|
+
const onDemandTools = selectByVisibility(allTools, manifestOf, 'on-demand');
|
|
567
|
+
const silentTools = selectByVisibility(allTools, manifestOf, 'silent');
|
|
461
568
|
|
|
462
569
|
// ── 4. Wrap tools (meta + plugin) so handlers receive a RuntimeContext ──
|
|
463
570
|
const metaTools = [
|
|
464
571
|
...buildMetaTools({
|
|
465
572
|
manifestRegistry: registries.manifests,
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
toolAccess: {
|
|
470
|
-
...toolAccess,
|
|
471
|
-
preloadedOnly: new Set(
|
|
472
|
-
[...(turnPreloads ?? [])].filter((name) => !threadLoaded.has(name)),
|
|
473
|
-
),
|
|
474
|
-
},
|
|
573
|
+
toolRegistry: turnSummaries,
|
|
574
|
+
toolAccess,
|
|
575
|
+
loader: capabilityLoader,
|
|
475
576
|
}),
|
|
476
577
|
// Pages through tool results the result cap saved whole (result-cap.ts);
|
|
477
578
|
// its chunks stay well under the cap so a page is never capped itself.
|
|
@@ -479,12 +580,8 @@ export async function createMainAgent(
|
|
|
479
580
|
? [
|
|
480
581
|
buildReadResultTool({
|
|
481
582
|
read: hooks.readResult,
|
|
482
|
-
maxChars:
|
|
483
|
-
|
|
484
|
-
Math.max(
|
|
485
|
-
512,
|
|
486
|
-
Math.floor((contextBudget?.resultCapChars ?? 32_000) / 2),
|
|
487
|
-
),
|
|
583
|
+
maxChars: readResultMaxChars(
|
|
584
|
+
contextBudget?.resultCapChars ?? 32_000,
|
|
488
585
|
),
|
|
489
586
|
}),
|
|
490
587
|
]
|
|
@@ -647,29 +744,6 @@ export async function createMainAgent(
|
|
|
647
744
|
...domainTools,
|
|
648
745
|
];
|
|
649
746
|
|
|
650
|
-
// Lookups used by `CapabilityGateMiddleware` to gate on-demand plugins
|
|
651
|
-
// and sub-agents per model call. Meta-tools omitted from the map are
|
|
652
|
-
// pass-through.
|
|
653
|
-
const pluginByToolName = new Map<string, string>();
|
|
654
|
-
const visibilityByToolName = new Map<
|
|
655
|
-
string,
|
|
656
|
-
NonNullable<PluginManifest['visibility']>
|
|
657
|
-
>();
|
|
658
|
-
for (const { pluginName, tool } of allTools) {
|
|
659
|
-
pluginByToolName.set(tool.name, pluginName);
|
|
660
|
-
const effective =
|
|
661
|
-
tool.visibility ?? manifestViz.get(pluginName) ?? 'on-demand';
|
|
662
|
-
visibilityByToolName.set(tool.name, effective);
|
|
663
|
-
}
|
|
664
|
-
for (const { pluginName, subAgent } of subAgentEntries) {
|
|
665
|
-
const toolName = computeSubAgentToolName(subAgent.name);
|
|
666
|
-
pluginByToolName.set(toolName, pluginName);
|
|
667
|
-
visibilityByToolName.set(
|
|
668
|
-
toolName,
|
|
669
|
-
manifestViz.get(pluginName) ?? 'on-demand',
|
|
670
|
-
);
|
|
671
|
-
}
|
|
672
|
-
|
|
673
747
|
// ── 6. Middleware stack — always-on + plugin contributions ──────────────
|
|
674
748
|
const pluginMiddlewares = [
|
|
675
749
|
...registries.middlewares.collect(buildCtx),
|
|
@@ -756,9 +830,9 @@ export async function createMainAgent(
|
|
|
756
830
|
createCapabilityGateMiddleware({
|
|
757
831
|
pluginByToolName,
|
|
758
832
|
visibilityByToolName,
|
|
759
|
-
preloadedPlugins: turnPreloads,
|
|
760
833
|
unmetRequirements,
|
|
761
834
|
withheldToolNames: toolAccess.withheldToolNames,
|
|
835
|
+
describeLoad: capabilityLoader.describeForGate,
|
|
762
836
|
logger: ambient.logger,
|
|
763
837
|
}),
|
|
764
838
|
userContextStateMiddleware(),
|
|
@@ -829,9 +903,10 @@ export async function createMainAgent(
|
|
|
829
903
|
...(returnDirectNames.size > 0
|
|
830
904
|
? [createReturnDirectFirstMiddleware(returnDirectNames)]
|
|
831
905
|
: []),
|
|
832
|
-
// Innermost: sees the request
|
|
833
|
-
//
|
|
834
|
-
//
|
|
906
|
+
// Innermost but one: sees the request as it goes to the provider, except
|
|
907
|
+
// the images the recall middleware below adds inside it. Prunes old tool
|
|
908
|
+
// results under pressure, refuses what cannot fit, and learns the window
|
|
909
|
+
// from a provider overflow (context-guard.ts).
|
|
835
910
|
...(contextBudget
|
|
836
911
|
? [
|
|
837
912
|
createContextGuardMiddleware({
|
|
@@ -840,6 +915,26 @@ export async function createMainAgent(
|
|
|
840
915
|
? { onOverflow: hooks.onContextOverflow }
|
|
841
916
|
: {}),
|
|
842
917
|
...(hooks?.onContextEvent ? { onEvent: hooks.onContextEvent } : {}),
|
|
918
|
+
...(hooks?.readResult
|
|
919
|
+
? {
|
|
920
|
+
readResultChars: readResultMaxChars(
|
|
921
|
+
contextBudget.resultCapChars,
|
|
922
|
+
),
|
|
923
|
+
}
|
|
924
|
+
: {}),
|
|
925
|
+
logger: ambient.logger,
|
|
926
|
+
}),
|
|
927
|
+
]
|
|
928
|
+
: []),
|
|
929
|
+
// Inside the guard: recalled images (attachment-recall.ts) reach the
|
|
930
|
+
// provider but are not counted as chars/4 text, which their base64 is
|
|
931
|
+
// not (they are bounded by the recall's own caps instead); graph state
|
|
932
|
+
// never holds them. A provider refusal of a request carrying them is
|
|
933
|
+
// retried here once without them, before the guard sees the error.
|
|
934
|
+
...(hooks?.attachmentRecall
|
|
935
|
+
? [
|
|
936
|
+
createAttachmentRecallMiddleware({
|
|
937
|
+
...hooks.attachmentRecall,
|
|
843
938
|
logger: ambient.logger,
|
|
844
939
|
}),
|
|
845
940
|
]
|
|
@@ -867,7 +962,7 @@ export async function createMainAgent(
|
|
|
867
962
|
pluginName,
|
|
868
963
|
manifest: withoutWithheldExamples(manifest, toolAccess.withheldToolNames),
|
|
869
964
|
}));
|
|
870
|
-
const tier1 = renderTier1({ manifests: eagerEntries });
|
|
965
|
+
const tier1 = renderTier1({ manifests: eagerEntries, catalogue });
|
|
871
966
|
for (const warning of tier1.warnings) ambient.logger.warn(warning);
|
|
872
967
|
|
|
873
968
|
const customInstructions = identity.prompt?.customInstructions?.trim() ?? '';
|
|
@@ -884,35 +979,15 @@ export async function createMainAgent(
|
|
|
884
979
|
origin === 'request' && pluginName === PORTAL_CAPABILITY,
|
|
885
980
|
)
|
|
886
981
|
.map(({ tool }) => tool.name),
|
|
887
|
-
bound:
|
|
888
|
-
manifestViz.get(PORTAL_CAPABILITY) === 'always' ||
|
|
889
|
-
loadedSet.has(PORTAL_CAPABILITY),
|
|
982
|
+
bound: manifestOf.get(PORTAL_CAPABILITY)?.visibility === 'always',
|
|
890
983
|
};
|
|
891
984
|
|
|
892
|
-
// Operating guides of the plugins in use when the turn starts — loaded on
|
|
893
|
-
// the thread or `always` visible — and usable by this delegation. A router
|
|
894
|
-
// preload lasts one turn: its guide would enter the prompt and leave it
|
|
895
|
-
// again on the next turn, so it comes with `load_capability` instead, as
|
|
896
|
-
// does the guide of a plugin loaded mid-turn (here from the next turn on).
|
|
897
|
-
const operatingGuides = registries.manifests
|
|
898
|
-
.operatingGuides()
|
|
899
|
-
.filter(({ pluginName }) => {
|
|
900
|
-
const visibility = manifestViz.get(pluginName) ?? 'on-demand';
|
|
901
|
-
return (
|
|
902
|
-
visibility !== 'silent' &&
|
|
903
|
-
(visibility === 'always' || threadLoaded.has(pluginName)) &&
|
|
904
|
-
!unmetRequirements.has(pluginName) &&
|
|
905
|
-
!toolAccess.hiddenPlugins.has(pluginName)
|
|
906
|
-
);
|
|
907
|
-
})
|
|
908
|
-
.map(({ guide }) => guide);
|
|
909
|
-
|
|
910
985
|
const composedPrompt = await composePrompt({
|
|
911
986
|
identity,
|
|
912
987
|
capabilityBlock: tier1.block,
|
|
913
988
|
browserTools,
|
|
914
989
|
customInstructions,
|
|
915
|
-
operationalMode: hooks?.operationalMode ??
|
|
990
|
+
operationalMode: hooks?.operationalMode ?? '',
|
|
916
991
|
userPreferencesContext: formatUserPreferences(state.userPreferences),
|
|
917
992
|
userContext: state.userContext,
|
|
918
993
|
// The day only: the prompt must stay byte-identical across the turns
|
|
@@ -933,7 +1008,7 @@ export async function createMainAgent(
|
|
|
933
1008
|
tool.name === SEND_FILE_TOOL && !unmetRequirements.has(pluginName),
|
|
934
1009
|
),
|
|
935
1010
|
),
|
|
936
|
-
operatingGuides,
|
|
1011
|
+
operatingGuides: capabilityPlan.inPrompt.map(({ guide }) => guide),
|
|
937
1012
|
});
|
|
938
1013
|
// The domain block goes last: it frames its documents as retrieved data
|
|
939
1014
|
// that never outranks the instructions above it.
|
|
@@ -977,6 +1052,34 @@ export async function createMainAgent(
|
|
|
977
1052
|
toolSurfaces,
|
|
978
1053
|
subAgentToolNames: new Set(subAgentTools.map((t) => t.name)),
|
|
979
1054
|
context: runConfig.context,
|
|
1055
|
+
stateUpdate: {
|
|
1056
|
+
// Every loaded plugin, so a write here also carries what the caller's
|
|
1057
|
+
// own write to the channel carries (the editor context).
|
|
1058
|
+
...(expiredPlugins.length > 0
|
|
1059
|
+
? { loadedPlugins: { replace: [...loadedSet] } }
|
|
1060
|
+
: preloaded.length > 0
|
|
1061
|
+
? { loadedPlugins: [...loadedSet] }
|
|
1062
|
+
: {}),
|
|
1063
|
+
...(capabilityPlan.promptGuides
|
|
1064
|
+
? { promptGuides: capabilityPlan.promptGuides }
|
|
1065
|
+
: {}),
|
|
1066
|
+
// A channel's first value is stored as written, past its reducer.
|
|
1067
|
+
...(capabilityPlan.capabilityUsage
|
|
1068
|
+
? {
|
|
1069
|
+
capabilityUsage: args.promptCache?.capabilityUsage
|
|
1070
|
+
? { replace: capabilityPlan.capabilityUsage }
|
|
1071
|
+
: capabilityPlan.capabilityUsage,
|
|
1072
|
+
}
|
|
1073
|
+
: {}),
|
|
1074
|
+
},
|
|
1075
|
+
...(notedLoads.length > 0
|
|
1076
|
+
? {
|
|
1077
|
+
guideNote: {
|
|
1078
|
+
plugins: notedLoads.map(({ name }) => name),
|
|
1079
|
+
text: renderGuideNote(notedLoads),
|
|
1080
|
+
},
|
|
1081
|
+
}
|
|
1082
|
+
: {}),
|
|
980
1083
|
...(domain
|
|
981
1084
|
? {
|
|
982
1085
|
domainContext: {
|