@matatbread/matbot-plugin-api 0.2.5 → 0.2.7
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/invoke-tool.ts +5 -5
- package/src/plugin.ts +7 -1
- package/src/types.ts +74 -7
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"node",
|
|
5
5
|
"browser"
|
|
6
6
|
],
|
|
7
|
-
"version": "0.2.
|
|
7
|
+
"version": "0.2.7",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=24.0.0"
|
|
10
10
|
},
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"typescript": "^
|
|
36
|
+
"typescript": "^6.0.3"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"typecheck": "tsc --noEmit"
|
package/src/invoke-tool.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MatbotMachine } from './plugin.js';
|
|
2
|
-
import type { ToolContext, ToolEvent,
|
|
2
|
+
import type { ToolContext, ToolEvent, ToolResultFor, PromptFn, FormField } from './types.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Inputs to {@link invokeTool} that a one-shot caller can't derive from the machine. A tool
|
|
@@ -25,12 +25,12 @@ export type InvokeToolOptions =
|
|
|
25
25
|
* runs non-interactively: any attempt to prompt rejects, which a tool surfaces as a normal error
|
|
26
26
|
* event. Pair with {@link toolText} to collapse the stream to its result string.
|
|
27
27
|
*/
|
|
28
|
-
export function invokeTool<K extends string>(
|
|
28
|
+
export function invokeTool<K extends string, const P>(
|
|
29
29
|
machine: MatbotMachine,
|
|
30
30
|
name: K,
|
|
31
|
-
params:
|
|
31
|
+
params: P,
|
|
32
32
|
opts: InvokeToolOptions,
|
|
33
|
-
): AsyncIterable<ToolEvent<
|
|
33
|
+
): AsyncIterable<ToolEvent<ToolResultFor<K, P>>> {
|
|
34
34
|
const tool = machine.tools.resolve(name);
|
|
35
35
|
if (tool === null) throw new Error(`Tool "${name}" is not registered`);
|
|
36
36
|
|
|
@@ -51,7 +51,7 @@ export function invokeTool<K extends string>(
|
|
|
51
51
|
|
|
52
52
|
// The registry is name-keyed and untyped (`ToolExecutor.execute` yields `ToolEvent<unknown>`); the
|
|
53
53
|
// `ToolResults` mapping is the call-site contract, asserted here so callers read a typed result.
|
|
54
|
-
return tool.executor.execute(params, ctx) as AsyncIterable<ToolEvent<
|
|
54
|
+
return tool.executor.execute(params, ctx) as AsyncIterable<ToolEvent<ToolResultFor<K, P>>>;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
package/src/plugin.ts
CHANGED
|
@@ -491,7 +491,13 @@ export interface MatbotPluginSpec {
|
|
|
491
491
|
*/
|
|
492
492
|
export interface MatbotPlugin extends MatbotPluginSpec {
|
|
493
493
|
readonly name: string; // resolver.identify(specifier)
|
|
494
|
-
readonly specifier: string; // how it was loaded
|
|
494
|
+
readonly specifier: string; // how it was loaded (config entry / pre-resolved)
|
|
495
|
+
/** The stable URL the loader actually imported, minus any reload cache-bust stamp — a `file:` URL
|
|
496
|
+
* for local/remote-cached plugins, the bare specifier when nothing was pre-resolved. Lets a consumer
|
|
497
|
+
* map a loaded plugin back to its on-disk source without re-running specifier resolution (e.g.
|
|
498
|
+
* skills_compiler builds a types program over the live plugin set). Set by the loader; absent only
|
|
499
|
+
* on hosts that construct MatbotPlugin by hand. */
|
|
500
|
+
readonly resolvedUrl?: string;
|
|
495
501
|
readonly source?: PluginSource;
|
|
496
502
|
// The package.json `matbotRuntime`, captured at load from the same value used for the pre-import
|
|
497
503
|
// gate (the host reads it once; downstream just reads this). Absent ⇒ undeclared. Stored rather
|
package/src/types.ts
CHANGED
|
@@ -431,6 +431,11 @@ export interface KnowledgeEntry {
|
|
|
431
431
|
|
|
432
432
|
export interface KnowledgeIndex {
|
|
433
433
|
index(entry: KnowledgeEntry): Promise<void>;
|
|
434
|
+
/** Remove the entry with this id, if present (idempotent — removing an absent id is a no-op). The
|
|
435
|
+
* id is the index's sole primary key (`index` replaces by it), so retraction is by id alone; the
|
|
436
|
+
* index never inspects an entry's opaque `source`. The party that indexed an entry owns retracting
|
|
437
|
+
* it — e.g. a skill manager retracts a hidden/deleted skill rather than the index policing tenants. */
|
|
438
|
+
remove(id: string): Promise<void>;
|
|
434
439
|
search(terms: Array<{ term: string; context?: string }>, signal: AbortSignal): Promise<KnowledgeEntry[]>;
|
|
435
440
|
/** Enumerate all indexed entries. When present, register('KnowledgeIndex', …) drains these into the incoming backend. */
|
|
436
441
|
entries?(): Iterable<KnowledgeEntry>;
|
|
@@ -465,11 +470,61 @@ export type ToolEvent<Result = unknown> =
|
|
|
465
470
|
* name with no registered entry resolves to `unknown` (the caller must narrow). This is a pure
|
|
466
471
|
* type-level construct — no runtime validation; it pins, at the call site, the contract a tool's
|
|
467
472
|
* executor already produces. Keep the entry in sync with what the executor actually yields.
|
|
473
|
+
*
|
|
474
|
+
* **Multi-action tools** are a weird form of overloaded function: the same tool returns different
|
|
475
|
+
* shapes depending on its params. Register such a tool as a union of {@link ToolResult} *arms*, each
|
|
476
|
+
* pairing a result with the discriminating params *pattern* that selects it — `invokeTool` matches the
|
|
477
|
+
* call's literal params against the patterns and narrows the result to the matching arm:
|
|
478
|
+
*
|
|
479
|
+
* declare module '@matatbread/matbot-plugin-api' {
|
|
480
|
+
* interface ToolResults {
|
|
481
|
+
* session_action:
|
|
482
|
+
* | ToolResult<Session, { action: 'get' }>
|
|
483
|
+
* | ToolResult<{ id: string; title: string }, { action: 'rename' }>;
|
|
484
|
+
* }
|
|
485
|
+
* }
|
|
486
|
+
* // invokeTool(machine, 'session_action', { action: 'get', sessionId }) → result is Session
|
|
487
|
+
*
|
|
488
|
+
* The pattern is *any* discriminating field(s), not just `action` (a tool keying on `interval`'s
|
|
489
|
+
* presence registers `ToolResult<…, { interval: string }>`). It is a *pattern*, not the full input:
|
|
490
|
+
* key only on the discriminant so a call carrying just that field still matches. When no arm's pattern
|
|
491
|
+
* matches (a non-literal discriminant, or an absence-discriminant the positive patterns can't express),
|
|
492
|
+
* the result falls back to the union of every arm — always sound, just less narrow.
|
|
468
493
|
*/
|
|
469
494
|
export interface ToolResults {}
|
|
470
495
|
|
|
471
|
-
/**
|
|
472
|
-
|
|
496
|
+
/**
|
|
497
|
+
* One overload arm of a multi-action tool registered in {@link ToolResults}: the `Result` it yields
|
|
498
|
+
* for a call whose params match the discriminating pattern `Args`. `Args` defaults to `unknown` (an
|
|
499
|
+
* arm that matches any params). Purely type-level — the `__` fields are phantom and never constructed.
|
|
500
|
+
*/
|
|
501
|
+
export interface ToolResult<Result, Args = unknown> {
|
|
502
|
+
readonly __result: Result;
|
|
503
|
+
readonly __args: Args;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
type ToolResultArmed<E> = E extends ToolResult<unknown, unknown> ? true : false;
|
|
507
|
+
type ToolResultUnion<E> = E extends ToolResult<infer R, unknown> ? R : never;
|
|
508
|
+
type ToolResultMatched<E, P> = E extends ToolResult<infer R, infer A> ? (P extends A ? R : never) : never;
|
|
509
|
+
|
|
510
|
+
/** The full set of `result` values a tool named `K` may yield — the union over all its arms (for an
|
|
511
|
+
* arm-based entry), the entry itself (for a plain entry), or `unknown` when `K` is unregistered. This
|
|
512
|
+
* is what an executor binds against (`ToolExecutor<ToolResultOf<'my_tool'>>`): it must cover every arm. */
|
|
513
|
+
export type ToolResultOf<K extends string> =
|
|
514
|
+
K extends keyof ToolResults
|
|
515
|
+
? ToolResultArmed<ToolResults[K]> extends true ? ToolResultUnion<ToolResults[K]> : ToolResults[K]
|
|
516
|
+
: unknown;
|
|
517
|
+
|
|
518
|
+
/** The `result` a call to tool `K` with params `P` yields: for an arm-based entry, the arm whose
|
|
519
|
+
* pattern `P` matches (or the full union when none match — always sound); otherwise {@link ToolResultOf}. */
|
|
520
|
+
export type ToolResultFor<K extends string, P> =
|
|
521
|
+
K extends keyof ToolResults
|
|
522
|
+
? ToolResultArmed<ToolResults[K]> extends true
|
|
523
|
+
? ([ToolResultMatched<ToolResults[K], P>] extends [never]
|
|
524
|
+
? ToolResultUnion<ToolResults[K]>
|
|
525
|
+
: ToolResultMatched<ToolResults[K], P>)
|
|
526
|
+
: ToolResults[K]
|
|
527
|
+
: unknown;
|
|
473
528
|
|
|
474
529
|
/**
|
|
475
530
|
* Ask the user a question and resolve with their answer. The host supplies the
|
|
@@ -520,15 +575,23 @@ export interface ToolContext {
|
|
|
520
575
|
unloadPlugin(specifier: string): Promise<boolean>;
|
|
521
576
|
}
|
|
522
577
|
|
|
523
|
-
|
|
524
|
-
|
|
578
|
+
/**
|
|
579
|
+
* A tool's runtime. `R` is the type of the `value` carried by its `result` event — declared once,
|
|
580
|
+
* at the source, so the executor's yields and the tool's {@link ToolResults} registry entry can't
|
|
581
|
+
* silently drift. The `unknown` default keeps untyped executors compiling untouched, and covariance
|
|
582
|
+
* means a narrower `ToolExecutor<X>` still satisfies `ToolExecutor` (i.e. `ToolExecutor<unknown>`),
|
|
583
|
+
* so the heterogeneous registry boundary (`Tool[]`) accepts any executor. Bind `R` to the registry
|
|
584
|
+
* entry with `ToolExecutor<ToolResultOf<'my_tool'>>` so the single augmentation is the source of truth.
|
|
585
|
+
*/
|
|
586
|
+
export interface ToolExecutor<R = unknown> {
|
|
587
|
+
execute(input: unknown, ctx: ToolContext): AsyncIterable<ToolEvent<R>>;
|
|
525
588
|
}
|
|
526
589
|
|
|
527
|
-
export interface Tool {
|
|
590
|
+
export interface Tool<R = unknown> {
|
|
528
591
|
name: string;
|
|
529
592
|
description: string;
|
|
530
593
|
inputSchema: JSONSchema;
|
|
531
|
-
executor: ToolExecutor
|
|
594
|
+
executor: ToolExecutor<R>;
|
|
532
595
|
pluginName?: string;
|
|
533
596
|
}
|
|
534
597
|
|
|
@@ -604,7 +667,11 @@ export interface VaultSpec {
|
|
|
604
667
|
/** Resolve ${NAME} placeholders by looking up the named value; throws MissingSecretError for any miss. */
|
|
605
668
|
resolve(ref: string): Promise<string>;
|
|
606
669
|
scrub(text: string): string;
|
|
607
|
-
/**
|
|
670
|
+
/**
|
|
671
|
+
* Store `value` under exactly `name`, overwriting. The literal write; no reference/dedup logic.
|
|
672
|
+
* An empty `value` removes the key instead — there is no such thing as an empty secret, so the
|
|
673
|
+
* empty string is the removal signal (idempotent: removing an absent name is a no-op).
|
|
674
|
+
*/
|
|
608
675
|
writeSecret(name: string, value: string): Promise<void>;
|
|
609
676
|
/** Whether a secret is stored under this exact name. */
|
|
610
677
|
hasKey(name: string): boolean;
|