@matatbread/matbot-core 0.2.7 → 0.3.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/about.ts +34 -0
- package/src/index.ts +2 -0
- package/src/loader.ts +2 -0
- package/src/provider-registry.ts +29 -0
- package/src/registry.ts +42 -12
- package/src/session-runner.ts +18 -1
- package/src/single-turn.ts +4 -8
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"node",
|
|
5
5
|
"browser"
|
|
6
6
|
],
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.3.0",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=24.0.0"
|
|
10
10
|
},
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@matatbread/matbot-plugin-api": "^0.
|
|
38
|
+
"@matatbread/matbot-plugin-api": "^0.3.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"typescript": "^6.0.3"
|
package/src/about.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Tool, ToolExecutor, ToolContract, ToolResultOf, ToolContext } from '@matatbread/matbot-plugin-api';
|
|
2
|
+
|
|
3
|
+
declare module '@matatbread/matbot-plugin-api' {
|
|
4
|
+
interface ToolContracts {
|
|
5
|
+
about_matbot: ToolContract<{ version: string; about: string }, Record<string, never>>;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const ABOUT = 'Matbot composable LLM harness';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Reports what the model is running: the harness `version` and a one-line description. The harness is
|
|
13
|
+
* not a plugin (so it has no row in `plugin list`), and its version is a singleton fact rather than a
|
|
14
|
+
* per-plugin one — hence a dedicated tool. The factory lives in core (like `single_turn`) because it is
|
|
15
|
+
* the one cross-runtime home both the node app and the browser bundle register it from, each passing its
|
|
16
|
+
* own app package version.
|
|
17
|
+
*/
|
|
18
|
+
export function createAboutMatbotTool(version: string): Tool<ToolResultOf<'about_matbot'>> {
|
|
19
|
+
const executor: ToolExecutor<ToolResultOf<'about_matbot'>> = {
|
|
20
|
+
async *execute(_input: unknown, _ctx: ToolContext) {
|
|
21
|
+
yield { type: 'result', value: { version, about: ABOUT } };
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
name: 'about_matbot',
|
|
27
|
+
description:
|
|
28
|
+
'Report what you are running: the matbot harness version and a one-line description. Use it when ' +
|
|
29
|
+
'asked what version of matbot this is, or for an "about" of the harness itself (distinct from the ' +
|
|
30
|
+
'plugin tool, which lists loaded plugins).',
|
|
31
|
+
inputSchema: { type: 'object', properties: {} },
|
|
32
|
+
executor,
|
|
33
|
+
};
|
|
34
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -38,8 +38,10 @@ export * from './registry.js';
|
|
|
38
38
|
export * from './settings.js';
|
|
39
39
|
export * from './loader.js';
|
|
40
40
|
export * from './tool-registry.js';
|
|
41
|
+
export * from './provider-registry.js';
|
|
41
42
|
export * from './system-context.js';
|
|
42
43
|
export * from './single-turn.js';
|
|
44
|
+
export * from './about.js';
|
|
43
45
|
export * from './config/index.js';
|
|
44
46
|
export * from './security/index.js';
|
|
45
47
|
export * from './knowledge/index.js';
|
package/src/loader.ts
CHANGED
|
@@ -212,6 +212,7 @@ export async function loadPlugins(
|
|
|
212
212
|
const name = toLoad[i]!.name ?? (services.resolver !== undefined ? await services.resolver.identify(spec) : defaultIdentify(spec));
|
|
213
213
|
const source = sourceOf(spec);
|
|
214
214
|
const runtimes = toLoad[i]!.runtimes;
|
|
215
|
+
const version = services.resolver?.version !== undefined ? await services.resolver.version(spec) : undefined;
|
|
215
216
|
const plugin: MatbotPlugin = {
|
|
216
217
|
...spec_obj,
|
|
217
218
|
name,
|
|
@@ -219,6 +220,7 @@ export async function loadPlugins(
|
|
|
219
220
|
resolvedUrl: toLoad[i]!.resolvedUrl,
|
|
220
221
|
...(source !== undefined ? { source } : {}),
|
|
221
222
|
...(runtimes !== undefined ? { matbotRuntime: runtimes } : {}),
|
|
223
|
+
...(version !== undefined ? { version } : {}),
|
|
222
224
|
};
|
|
223
225
|
|
|
224
226
|
// registered gates the rollback: only an attempt that *itself* registered the
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ProviderConfig, ProviderRegistry } from '@matatbread/matbot-plugin-api';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A `Map` of provider profiles that also satisfies the sanctioned {@link ProviderRegistry} write API.
|
|
5
|
+
* Extending `Map` gives every read-surface consumer (`get`/`has`/`keys`/`values`/iteration) the exact
|
|
6
|
+
* standard-library types, while `register`/`remove`/`revert` are the names plugins and the `provider`
|
|
7
|
+
* tool use to contribute profiles — mirroring `ToolRegistry.register`/`remove`.
|
|
8
|
+
*
|
|
9
|
+
* The entries present at construction are snapshotted as the *boot baseline*: `revert(name)` restores
|
|
10
|
+
* that value (or deletes if there was none), so a plugin that contributed — possibly shadowing — a
|
|
11
|
+
* profile can undo it on unload, returning the set to the host's boot condition.
|
|
12
|
+
*/
|
|
13
|
+
export class ProviderRegistryImpl extends Map<string, ProviderConfig> implements ProviderRegistry {
|
|
14
|
+
private readonly bootBaseline: Map<string, ProviderConfig>;
|
|
15
|
+
|
|
16
|
+
constructor(initial?: Iterable<readonly [string, ProviderConfig]>) {
|
|
17
|
+
super(initial);
|
|
18
|
+
this.bootBaseline = new Map(this);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
register(config: ProviderConfig): void { this.set(config.name, config); }
|
|
22
|
+
remove(name: string): boolean { return this.delete(name); }
|
|
23
|
+
|
|
24
|
+
revert(name: string): void {
|
|
25
|
+
const boot = this.bootBaseline.get(name);
|
|
26
|
+
if (boot !== undefined) this.set(name, boot);
|
|
27
|
+
else this.delete(name);
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/registry.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Tool, ToolRegistry, Hook, PromptFn, FormField, FrontendInfo, PluginRegistryEvent } from './types.js';
|
|
1
|
+
import type { Tool, ToolRegistry, Hook, PromptFn, FormField, FrontendInfo, PluginRegistryEvent, ProviderAdapter, ProviderConfig } from './types.js';
|
|
2
2
|
import { createBroadcaster } from '@matatbread/matbot-plugin-api';
|
|
3
3
|
import type {
|
|
4
|
-
MatbotPlugin, MatbotMachine, Mounted,
|
|
4
|
+
MatbotPlugin, MatbotMachine, MatbotRuntime, Mounted,
|
|
5
5
|
ProviderAdapterFactory, StoreFactory,
|
|
6
6
|
} from './plugin.js';
|
|
7
7
|
import { PLUGIN_API_VERSION, unifyServices } from './plugin.js';
|
|
@@ -145,17 +145,47 @@ export function registerPlugin(plugin: MatbotPlugin): void {
|
|
|
145
145
|
|
|
146
146
|
// ── Resolution ────────────────────────────────────────────────────────────────
|
|
147
147
|
|
|
148
|
-
export function
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
148
|
+
export function tryResolveProviderFactory(module: string): ProviderAdapterFactory | undefined {
|
|
149
|
+
return state.providers.get(module);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Config → live adapter, loading the adapter module on demand. The fast path resolves an already
|
|
154
|
+
* registered factory. When the factory is absent (the common case now the boot pre-scan is disabled, or
|
|
155
|
+
* a provider profile a storage backend replayed from its medium ahead of its adapter module) the module
|
|
156
|
+
* is force-loaded and the loaded plugin's own factory is used — sidestepping the canonical-name keying the
|
|
157
|
+
* factory registry uses. Returns `null` (and warns, never throws) if the module can't be loaded or carries
|
|
158
|
+
* no adapter, so one unusable profile can't abort a turn.
|
|
159
|
+
*/
|
|
160
|
+
export async function instantiateProvider(services: MatbotRuntime, config: ProviderConfig): Promise<ProviderAdapter | null> {
|
|
161
|
+
// Resolve the factory tolerantly. `config.module` may be the canonical plugin name or any specifier that
|
|
162
|
+
// identifies the plugin — the factory registry is keyed by plugin name, so when a raw specifier misses we
|
|
163
|
+
// map it to the loaded plugin's name (a sibling profile sharing this adapter module may have loaded it
|
|
164
|
+
// already). The stored profile is NEVER rewritten: it stays exactly as the source wrote it (yaml path,
|
|
165
|
+
// npm name, or a storage-backend manifest entry), so `provider list` reports the source truth rather than
|
|
166
|
+
// a mix of specifiers and package names that depends on which profiles happen to have been used.
|
|
167
|
+
const name = tryResolveProviderFactory(config.module) !== undefined
|
|
168
|
+
? config.module
|
|
169
|
+
: getPluginNameForSpecifier(config.module);
|
|
170
|
+
if (name !== undefined) {
|
|
171
|
+
const factory = tryResolveProviderFactory(name);
|
|
172
|
+
if (factory !== undefined) return factory(config);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Not loaded yet — force-load the adapter module on demand (a runtime-contributed profile, or a disabled
|
|
176
|
+
// pre-scan). Warn and return null rather than throw, so one unusable profile can't abort a turn. The next
|
|
177
|
+
// resolution hits the fast path above via specifier→name, so this loads at most once per adapter module.
|
|
178
|
+
try {
|
|
179
|
+
const plugin = await services.loadPlugin(config.module);
|
|
180
|
+
if (plugin.provider === undefined) {
|
|
181
|
+
console.warn(`[matbot] provider "${config.name}": module "${config.module}" loaded but registered no adapter.`);
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
return plugin.provider(config);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
console.warn(`[matbot] provider "${config.name}": could not load adapter module "${config.module}":`, err);
|
|
187
|
+
return null;
|
|
157
188
|
}
|
|
158
|
-
return factory;
|
|
159
189
|
}
|
|
160
190
|
|
|
161
191
|
export function getRegisteredTools(): readonly Tool[] {
|
package/src/session-runner.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
} from './types.js';
|
|
6
6
|
import type { MatbotPlugin } from './plugin.js';
|
|
7
7
|
import type { HookRegistry } from './hooks.js';
|
|
8
|
+
import type { ToolTypeIndex } from '@matatbread/matbot-plugin-api';
|
|
8
9
|
import { appendMessage, createMessage } from './session.js';
|
|
9
10
|
import { contextSwitch, withUsageScope } from '@matatbread/matbot-plugin-api';
|
|
10
11
|
import { runSession } from './runner.js';
|
|
@@ -13,6 +14,11 @@ export interface SessionRunnerDeps {
|
|
|
13
14
|
store: Store<Session>;
|
|
14
15
|
resolveProvider: (name: string) => Promise<{ adapter: ProviderAdapter; config: ProviderConfig } | null>;
|
|
15
16
|
tools?: ToolRegistry;
|
|
17
|
+
// Resolved live (the service registers after boot): supplies the per-tool wire contract derived from each
|
|
18
|
+
// tool's single contract source (the `ToolContracts` arms, or a source-less tool's `toolContract`), folded
|
|
19
|
+
// into the turn-start tool snapshot's description so the wire carries params/result text. Absent (e.g.
|
|
20
|
+
// browser) ⇒ tools keep their plain description.
|
|
21
|
+
toolTypeIndex?: () => ToolTypeIndex | undefined;
|
|
16
22
|
hooks?: HookRegistry;
|
|
17
23
|
systemContext?: SystemContextRegistry;
|
|
18
24
|
vault?: Vault;
|
|
@@ -207,8 +213,19 @@ export function createSessionRunner(deps: SessionRunnerDeps): SessionRunner {
|
|
|
207
213
|
|
|
208
214
|
const ac = new AbortController();
|
|
209
215
|
s.ac = ac;
|
|
216
|
+
// Fold each tool's wire contract — the `params`/`result` text flattened from its single contract
|
|
217
|
+
// (a source tool's ToolContracts arms, or a source-less tool's `toolContract`) — into its description
|
|
218
|
+
// for this turn, so the model reasons about the real TS shapes, not just the loose inputSchema.
|
|
219
|
+
// Derived here because `services`/ToolTypeIndex are live at dispatch. Tools with only a loose
|
|
220
|
+
// inputSchema (no contract) are left as-is; absent ToolTypeIndex (browser) ⇒ plain descriptions.
|
|
221
|
+
const wire = await deps.toolTypeIndex?.()?.wireContracts();
|
|
210
222
|
const toolMap = deps.tools !== undefined
|
|
211
|
-
? new Map<string, Tool>(deps.tools.list().map(t =>
|
|
223
|
+
? new Map<string, Tool>(deps.tools.list().map(t => {
|
|
224
|
+
const wc = wire?.[t.name];
|
|
225
|
+
return [t.name, wc !== undefined
|
|
226
|
+
? { ...t, description: `${t.description}\n\nTypeScript:\n params: ${wc.params}\n result: ${wc.result}` }
|
|
227
|
+
: t];
|
|
228
|
+
}))
|
|
212
229
|
: undefined;
|
|
213
230
|
|
|
214
231
|
try {
|
package/src/single-turn.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { Tool, ToolExecutor, ToolResultOf, ToolContext, MatbotMachine } from '@matatbread/matbot-plugin-api';
|
|
1
|
+
import type { Tool, ToolExecutor, ToolContract, ToolResultOf, ToolContext, MatbotMachine } from '@matatbread/matbot-plugin-api';
|
|
2
2
|
|
|
3
3
|
declare module '@matatbread/matbot-plugin-api' {
|
|
4
|
-
interface
|
|
5
|
-
single_turn: { text: string }; // the consulted provider's reply text
|
|
4
|
+
interface ToolContracts {
|
|
5
|
+
single_turn: ToolContract<{ text: string }, { provider?: string; prompt: string; system?: string }>; // the consulted provider's reply text
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
|
|
@@ -53,11 +53,7 @@ export function createSingleTurnTool(services: MatbotMachine): Tool<ToolResultOf
|
|
|
53
53
|
'back its text. Use it to consult a different model — e.g. a second, ' +
|
|
54
54
|
'different-lineage model critiquing your draft, or any generation that should run on a specific ' +
|
|
55
55
|
'provider. `provider` is OPTIONAL: omit it to run on the current conversation\'s model, or name ' +
|
|
56
|
-
'a configured provider to switch models (list or add providers with the provider tool)
|
|
57
|
-
'Parameters (TypeScript):\n' +
|
|
58
|
-
'```ts\n' +
|
|
59
|
-
'{ provider?: string; prompt: string; system?: string } // -> { text }\n' +
|
|
60
|
-
'```',
|
|
56
|
+
'a configured provider to switch models (list or add providers with the provider tool).',
|
|
61
57
|
inputSchema: {
|
|
62
58
|
type: 'object',
|
|
63
59
|
required: ['prompt'],
|