@ixo/oracle-runtime-workers 0.38.0 → 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 +2 -2
- 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 +45 -4
- package/src/core/main-agent.ts +210 -122
- 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/capability-gate.ts +77 -28
- package/src/core/middlewares/context-guard.ts +154 -21
- 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 +13 -1
- package/src/do/transcript.ts +40 -11
- package/src/do/turn-metadata.ts +16 -4
- package/src/do/user-oracle-do.ts +132 -59
- package/src/index.ts +1 -0
- 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/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/serialization.ts +12 -0
- package/src/sqlite/sqlite-saver.ts +17 -0
- package/src/tasks/schedule-input.ts +5 -22
package/src/core/main-agent.ts
CHANGED
|
@@ -17,14 +17,26 @@ 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 {
|
|
@@ -63,7 +75,6 @@ import {
|
|
|
63
75
|
dropShadowingRequestEntries,
|
|
64
76
|
formatByPlugin,
|
|
65
77
|
turnToolSummaries,
|
|
66
|
-
type ManifestRegistry,
|
|
67
78
|
} from './registries';
|
|
68
79
|
import {
|
|
69
80
|
buildPluginContext,
|
|
@@ -90,12 +101,6 @@ import { ixoAddressOf, renderUserIdentity } from './user-identity';
|
|
|
90
101
|
|
|
91
102
|
const PLUGIN_LOGGER_COMPONENT = 'main-agent';
|
|
92
103
|
|
|
93
|
-
const DEFAULT_OPERATIONAL_MODE = [
|
|
94
|
-
'**General conversation mode**',
|
|
95
|
-
'',
|
|
96
|
-
'Default to conversation mode, using available capabilities for recall, search, and task delegation.',
|
|
97
|
-
].join('\n');
|
|
98
|
-
|
|
99
104
|
type CollectedTool = { pluginName: string; tool: PluginTool };
|
|
100
105
|
|
|
101
106
|
/** The validated env values the user-identity section names. */
|
|
@@ -105,31 +110,16 @@ const identityConfigSchema = z.object({
|
|
|
105
110
|
ORACLE_ENTITY_DID: z.string().min(1).optional(),
|
|
106
111
|
});
|
|
107
112
|
|
|
108
|
-
/**
|
|
109
|
-
function visibilityIndex(
|
|
110
|
-
manifests: ManifestRegistry,
|
|
111
|
-
): Map<string, NonNullable<PluginManifest['visibility']>> {
|
|
112
|
-
const out = new Map<string, NonNullable<PluginManifest['visibility']>>();
|
|
113
|
-
for (const { pluginName, manifest } of manifests.collect()) {
|
|
114
|
-
out.set(pluginName, manifest.visibility ?? 'on-demand');
|
|
115
|
-
}
|
|
116
|
-
return out;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Filter collected tools by their effective visibility — per-tool override
|
|
121
|
-
* wins; otherwise the plugin's manifest visibility decides.
|
|
122
|
-
*/
|
|
113
|
+
/** Filter collected tools by their effective visibility. */
|
|
123
114
|
function selectByVisibility(
|
|
124
115
|
tools: CollectedTool[],
|
|
125
|
-
|
|
116
|
+
manifestOf: Map<string, PluginManifest>,
|
|
126
117
|
visibility: NonNullable<PluginManifest['visibility']>,
|
|
127
118
|
): CollectedTool[] {
|
|
128
|
-
return tools.filter(
|
|
129
|
-
|
|
130
|
-
tool
|
|
131
|
-
|
|
132
|
-
});
|
|
119
|
+
return tools.filter(
|
|
120
|
+
({ pluginName, tool }) =>
|
|
121
|
+
effectiveToolVisibility(tool, manifestOf.get(pluginName)) === visibility,
|
|
122
|
+
);
|
|
133
123
|
}
|
|
134
124
|
|
|
135
125
|
/**
|
|
@@ -173,7 +163,7 @@ export async function createMainAgent(
|
|
|
173
163
|
ambient,
|
|
174
164
|
state,
|
|
175
165
|
availablePlugins,
|
|
176
|
-
|
|
166
|
+
routeCapabilities,
|
|
177
167
|
checkpointer,
|
|
178
168
|
abortSignal,
|
|
179
169
|
byoProvider,
|
|
@@ -249,6 +239,7 @@ export async function createMainAgent(
|
|
|
249
239
|
toolSurfaces: new Map(),
|
|
250
240
|
subAgentToolNames: new Set(),
|
|
251
241
|
context: { user: requestCtx.user, session: requestCtx.session },
|
|
242
|
+
stateUpdate: {},
|
|
252
243
|
};
|
|
253
244
|
}
|
|
254
245
|
|
|
@@ -267,14 +258,12 @@ export async function createMainAgent(
|
|
|
267
258
|
state.loadedPlugins,
|
|
268
259
|
);
|
|
269
260
|
|
|
270
|
-
// What the
|
|
271
|
-
//
|
|
272
|
-
//
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
...(preloadedPlugins ?? []),
|
|
277
|
-
]);
|
|
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);
|
|
278
267
|
// Carry the prior request state (browserTools, agActions, …) into the
|
|
279
268
|
// per-request RuntimeContext and the tool-wrapper closures, but NOT the
|
|
280
269
|
// message history: keeping the full `messages` array here would pin the
|
|
@@ -411,22 +400,6 @@ export async function createMainAgent(
|
|
|
411
400
|
const missing = unmetManifestRequirements(manifest, hasCapability);
|
|
412
401
|
if (missing.length > 0) unmetRequirements.set(pluginName, missing);
|
|
413
402
|
}
|
|
414
|
-
// The router predicts before the tools are collected (see
|
|
415
|
-
// `routableCandidates`): its preload of a plugin left with nothing, or of
|
|
416
|
-
// one whose requirements the delegation does not meet, is void, for the
|
|
417
|
-
// gate and for what handlers see as loaded.
|
|
418
|
-
const turnPreloads = preloadedPlugins
|
|
419
|
-
? new Set(
|
|
420
|
-
[...preloadedPlugins].filter(
|
|
421
|
-
(pluginName) =>
|
|
422
|
-
!toolAccess.hiddenPlugins.has(pluginName) &&
|
|
423
|
-
!unmetRequirements.has(pluginName),
|
|
424
|
-
),
|
|
425
|
-
)
|
|
426
|
-
: undefined;
|
|
427
|
-
for (const pluginName of preloadedPlugins ?? [])
|
|
428
|
-
if (!turnPreloads?.has(pluginName) && !threadLoaded.has(pluginName))
|
|
429
|
-
loadedSet.delete(pluginName);
|
|
430
403
|
|
|
431
404
|
// The per-turn tool-surface line. Request tools are named in full (there
|
|
432
405
|
// are only a handful and they are the ones that vary turn to turn); the
|
|
@@ -440,7 +413,9 @@ export async function createMainAgent(
|
|
|
440
413
|
ambient.logger.debug?.(
|
|
441
414
|
`[main-agent] full tool surface: ${formatByPlugin(allTools) || '∅'}`,
|
|
442
415
|
);
|
|
443
|
-
const
|
|
416
|
+
const manifestOf = new Map(
|
|
417
|
+
manifestEntries.map(({ pluginName, manifest }) => [pluginName, manifest]),
|
|
418
|
+
);
|
|
444
419
|
if (unmetRequirements.size > 0)
|
|
445
420
|
ambient.logger.log(
|
|
446
421
|
`[main-agent] not usable by ${requestCtx.user.did} (authorization lacks what they require): ${[...unmetRequirements.keys()].join(', ')}`,
|
|
@@ -452,27 +427,152 @@ export async function createMainAgent(
|
|
|
452
427
|
]),
|
|
453
428
|
);
|
|
454
429
|
|
|
455
|
-
|
|
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');
|
|
456
562
|
// Bind ALL on-demand tools at compile time — gating happens per model call
|
|
457
563
|
// in `CapabilityGateMiddleware` based on the live `loadedPlugins` state.
|
|
458
564
|
// This lets `load_capability` take effect on the very next LLM call inside
|
|
459
565
|
// the same run, instead of waiting for the next request to rebuild.
|
|
460
|
-
const onDemandTools = selectByVisibility(allTools,
|
|
461
|
-
const silentTools = selectByVisibility(allTools,
|
|
566
|
+
const onDemandTools = selectByVisibility(allTools, manifestOf, 'on-demand');
|
|
567
|
+
const silentTools = selectByVisibility(allTools, manifestOf, 'silent');
|
|
462
568
|
|
|
463
569
|
// ── 4. Wrap tools (meta + plugin) so handlers receive a RuntimeContext ──
|
|
464
570
|
const metaTools = [
|
|
465
571
|
...buildMetaTools({
|
|
466
572
|
manifestRegistry: registries.manifests,
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
toolAccess: {
|
|
471
|
-
...toolAccess,
|
|
472
|
-
preloadedOnly: new Set(
|
|
473
|
-
[...(turnPreloads ?? [])].filter((name) => !threadLoaded.has(name)),
|
|
474
|
-
),
|
|
475
|
-
},
|
|
573
|
+
toolRegistry: turnSummaries,
|
|
574
|
+
toolAccess,
|
|
575
|
+
loader: capabilityLoader,
|
|
476
576
|
}),
|
|
477
577
|
// Pages through tool results the result cap saved whole (result-cap.ts);
|
|
478
578
|
// its chunks stay well under the cap so a page is never capped itself.
|
|
@@ -480,12 +580,8 @@ export async function createMainAgent(
|
|
|
480
580
|
? [
|
|
481
581
|
buildReadResultTool({
|
|
482
582
|
read: hooks.readResult,
|
|
483
|
-
maxChars:
|
|
484
|
-
|
|
485
|
-
Math.max(
|
|
486
|
-
512,
|
|
487
|
-
Math.floor((contextBudget?.resultCapChars ?? 32_000) / 2),
|
|
488
|
-
),
|
|
583
|
+
maxChars: readResultMaxChars(
|
|
584
|
+
contextBudget?.resultCapChars ?? 32_000,
|
|
489
585
|
),
|
|
490
586
|
}),
|
|
491
587
|
]
|
|
@@ -648,29 +744,6 @@ export async function createMainAgent(
|
|
|
648
744
|
...domainTools,
|
|
649
745
|
];
|
|
650
746
|
|
|
651
|
-
// Lookups used by `CapabilityGateMiddleware` to gate on-demand plugins
|
|
652
|
-
// and sub-agents per model call. Meta-tools omitted from the map are
|
|
653
|
-
// pass-through.
|
|
654
|
-
const pluginByToolName = new Map<string, string>();
|
|
655
|
-
const visibilityByToolName = new Map<
|
|
656
|
-
string,
|
|
657
|
-
NonNullable<PluginManifest['visibility']>
|
|
658
|
-
>();
|
|
659
|
-
for (const { pluginName, tool } of allTools) {
|
|
660
|
-
pluginByToolName.set(tool.name, pluginName);
|
|
661
|
-
const effective =
|
|
662
|
-
tool.visibility ?? manifestViz.get(pluginName) ?? 'on-demand';
|
|
663
|
-
visibilityByToolName.set(tool.name, effective);
|
|
664
|
-
}
|
|
665
|
-
for (const { pluginName, subAgent } of subAgentEntries) {
|
|
666
|
-
const toolName = computeSubAgentToolName(subAgent.name);
|
|
667
|
-
pluginByToolName.set(toolName, pluginName);
|
|
668
|
-
visibilityByToolName.set(
|
|
669
|
-
toolName,
|
|
670
|
-
manifestViz.get(pluginName) ?? 'on-demand',
|
|
671
|
-
);
|
|
672
|
-
}
|
|
673
|
-
|
|
674
747
|
// ── 6. Middleware stack — always-on + plugin contributions ──────────────
|
|
675
748
|
const pluginMiddlewares = [
|
|
676
749
|
...registries.middlewares.collect(buildCtx),
|
|
@@ -757,9 +830,9 @@ export async function createMainAgent(
|
|
|
757
830
|
createCapabilityGateMiddleware({
|
|
758
831
|
pluginByToolName,
|
|
759
832
|
visibilityByToolName,
|
|
760
|
-
preloadedPlugins: turnPreloads,
|
|
761
833
|
unmetRequirements,
|
|
762
834
|
withheldToolNames: toolAccess.withheldToolNames,
|
|
835
|
+
describeLoad: capabilityLoader.describeForGate,
|
|
763
836
|
logger: ambient.logger,
|
|
764
837
|
}),
|
|
765
838
|
userContextStateMiddleware(),
|
|
@@ -842,6 +915,13 @@ export async function createMainAgent(
|
|
|
842
915
|
? { onOverflow: hooks.onContextOverflow }
|
|
843
916
|
: {}),
|
|
844
917
|
...(hooks?.onContextEvent ? { onEvent: hooks.onContextEvent } : {}),
|
|
918
|
+
...(hooks?.readResult
|
|
919
|
+
? {
|
|
920
|
+
readResultChars: readResultMaxChars(
|
|
921
|
+
contextBudget.resultCapChars,
|
|
922
|
+
),
|
|
923
|
+
}
|
|
924
|
+
: {}),
|
|
845
925
|
logger: ambient.logger,
|
|
846
926
|
}),
|
|
847
927
|
]
|
|
@@ -882,7 +962,7 @@ export async function createMainAgent(
|
|
|
882
962
|
pluginName,
|
|
883
963
|
manifest: withoutWithheldExamples(manifest, toolAccess.withheldToolNames),
|
|
884
964
|
}));
|
|
885
|
-
const tier1 = renderTier1({ manifests: eagerEntries });
|
|
965
|
+
const tier1 = renderTier1({ manifests: eagerEntries, catalogue });
|
|
886
966
|
for (const warning of tier1.warnings) ambient.logger.warn(warning);
|
|
887
967
|
|
|
888
968
|
const customInstructions = identity.prompt?.customInstructions?.trim() ?? '';
|
|
@@ -899,35 +979,15 @@ export async function createMainAgent(
|
|
|
899
979
|
origin === 'request' && pluginName === PORTAL_CAPABILITY,
|
|
900
980
|
)
|
|
901
981
|
.map(({ tool }) => tool.name),
|
|
902
|
-
bound:
|
|
903
|
-
manifestViz.get(PORTAL_CAPABILITY) === 'always' ||
|
|
904
|
-
loadedSet.has(PORTAL_CAPABILITY),
|
|
982
|
+
bound: manifestOf.get(PORTAL_CAPABILITY)?.visibility === 'always',
|
|
905
983
|
};
|
|
906
984
|
|
|
907
|
-
// Operating guides of the plugins in use when the turn starts — loaded on
|
|
908
|
-
// the thread or `always` visible — and usable by this delegation. A router
|
|
909
|
-
// preload lasts one turn: its guide would enter the prompt and leave it
|
|
910
|
-
// again on the next turn, so it comes with `load_capability` instead, as
|
|
911
|
-
// does the guide of a plugin loaded mid-turn (here from the next turn on).
|
|
912
|
-
const operatingGuides = registries.manifests
|
|
913
|
-
.operatingGuides()
|
|
914
|
-
.filter(({ pluginName }) => {
|
|
915
|
-
const visibility = manifestViz.get(pluginName) ?? 'on-demand';
|
|
916
|
-
return (
|
|
917
|
-
visibility !== 'silent' &&
|
|
918
|
-
(visibility === 'always' || threadLoaded.has(pluginName)) &&
|
|
919
|
-
!unmetRequirements.has(pluginName) &&
|
|
920
|
-
!toolAccess.hiddenPlugins.has(pluginName)
|
|
921
|
-
);
|
|
922
|
-
})
|
|
923
|
-
.map(({ guide }) => guide);
|
|
924
|
-
|
|
925
985
|
const composedPrompt = await composePrompt({
|
|
926
986
|
identity,
|
|
927
987
|
capabilityBlock: tier1.block,
|
|
928
988
|
browserTools,
|
|
929
989
|
customInstructions,
|
|
930
|
-
operationalMode: hooks?.operationalMode ??
|
|
990
|
+
operationalMode: hooks?.operationalMode ?? '',
|
|
931
991
|
userPreferencesContext: formatUserPreferences(state.userPreferences),
|
|
932
992
|
userContext: state.userContext,
|
|
933
993
|
// The day only: the prompt must stay byte-identical across the turns
|
|
@@ -948,7 +1008,7 @@ export async function createMainAgent(
|
|
|
948
1008
|
tool.name === SEND_FILE_TOOL && !unmetRequirements.has(pluginName),
|
|
949
1009
|
),
|
|
950
1010
|
),
|
|
951
|
-
operatingGuides,
|
|
1011
|
+
operatingGuides: capabilityPlan.inPrompt.map(({ guide }) => guide),
|
|
952
1012
|
});
|
|
953
1013
|
// The domain block goes last: it frames its documents as retrieved data
|
|
954
1014
|
// that never outranks the instructions above it.
|
|
@@ -992,6 +1052,34 @@ export async function createMainAgent(
|
|
|
992
1052
|
toolSurfaces,
|
|
993
1053
|
subAgentToolNames: new Set(subAgentTools.map((t) => t.name)),
|
|
994
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
|
+
: {}),
|
|
995
1083
|
...(domain
|
|
996
1084
|
? {
|
|
997
1085
|
domainContext: {
|