@xandout/libra-harness 0.1.8
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/LICENSE +21 -0
- package/README.md +486 -0
- package/dist/agent.d.ts +174 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +575 -0
- package/dist/agent.js.map +1 -0
- package/dist/ai-sdk-model.d.ts +26 -0
- package/dist/ai-sdk-model.d.ts.map +1 -0
- package/dist/ai-sdk-model.js +260 -0
- package/dist/ai-sdk-model.js.map +1 -0
- package/dist/context.d.ts +75 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +2 -0
- package/dist/context.js.map +1 -0
- package/dist/errors.d.ts +33 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +59 -0
- package/dist/errors.js.map +1 -0
- package/dist/extension.d.ts +63 -0
- package/dist/extension.d.ts.map +1 -0
- package/dist/extension.js +2 -0
- package/dist/extension.js.map +1 -0
- package/dist/extras/extension-loader.d.ts +205 -0
- package/dist/extras/extension-loader.d.ts.map +1 -0
- package/dist/extras/extension-loader.js +315 -0
- package/dist/extras/extension-loader.js.map +1 -0
- package/dist/extras/extensions/auto-steer/extension.d.ts +46 -0
- package/dist/extras/extensions/auto-steer/extension.d.ts.map +1 -0
- package/dist/extras/extensions/auto-steer/extension.js +52 -0
- package/dist/extras/extensions/auto-steer/extension.js.map +1 -0
- package/dist/extras/extensions/auto-steer/extension.json +7 -0
- package/dist/extras/extensions/auto-steer/index.d.ts +3 -0
- package/dist/extras/extensions/auto-steer/index.d.ts.map +1 -0
- package/dist/extras/extensions/auto-steer/index.js +2 -0
- package/dist/extras/extensions/auto-steer/index.js.map +1 -0
- package/dist/extras/extensions/disk-session/extension.d.ts +167 -0
- package/dist/extras/extensions/disk-session/extension.d.ts.map +1 -0
- package/dist/extras/extensions/disk-session/extension.js +492 -0
- package/dist/extras/extensions/disk-session/extension.js.map +1 -0
- package/dist/extras/extensions/disk-session/extension.json +6 -0
- package/dist/extras/extensions/disk-session/extension.test.d.ts +2 -0
- package/dist/extras/extensions/disk-session/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/disk-session/extension.test.js +672 -0
- package/dist/extras/extensions/disk-session/extension.test.js.map +1 -0
- package/dist/extras/extensions/disk-session/index.d.ts +3 -0
- package/dist/extras/extensions/disk-session/index.d.ts.map +1 -0
- package/dist/extras/extensions/disk-session/index.js +2 -0
- package/dist/extras/extensions/disk-session/index.js.map +1 -0
- package/dist/extras/extensions/emoji/extension.d.ts +20 -0
- package/dist/extras/extensions/emoji/extension.d.ts.map +1 -0
- package/dist/extras/extensions/emoji/extension.js +22 -0
- package/dist/extras/extensions/emoji/extension.js.map +1 -0
- package/dist/extras/extensions/emoji/extension.json +7 -0
- package/dist/extras/extensions/emoji/index.d.ts +3 -0
- package/dist/extras/extensions/emoji/index.d.ts.map +1 -0
- package/dist/extras/extensions/emoji/index.js +2 -0
- package/dist/extras/extensions/emoji/index.js.map +1 -0
- package/dist/extras/extensions/filesystem/extension.d.ts +53 -0
- package/dist/extras/extensions/filesystem/extension.d.ts.map +1 -0
- package/dist/extras/extensions/filesystem/extension.js +350 -0
- package/dist/extras/extensions/filesystem/extension.js.map +1 -0
- package/dist/extras/extensions/filesystem/extension.json +6 -0
- package/dist/extras/extensions/filesystem/extension.test.d.ts +2 -0
- package/dist/extras/extensions/filesystem/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/filesystem/extension.test.js +387 -0
- package/dist/extras/extensions/filesystem/extension.test.js.map +1 -0
- package/dist/extras/extensions/filesystem/index.d.ts +2 -0
- package/dist/extras/extensions/filesystem/index.d.ts.map +1 -0
- package/dist/extras/extensions/filesystem/index.js +2 -0
- package/dist/extras/extensions/filesystem/index.js.map +1 -0
- package/dist/extras/extensions/keyword-extractor/extension.d.ts +53 -0
- package/dist/extras/extensions/keyword-extractor/extension.d.ts.map +1 -0
- package/dist/extras/extensions/keyword-extractor/extension.js +61 -0
- package/dist/extras/extensions/keyword-extractor/extension.js.map +1 -0
- package/dist/extras/extensions/keyword-extractor/extension.json +6 -0
- package/dist/extras/extensions/keyword-extractor/extension.test.d.ts +2 -0
- package/dist/extras/extensions/keyword-extractor/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/keyword-extractor/extension.test.js +105 -0
- package/dist/extras/extensions/keyword-extractor/extension.test.js.map +1 -0
- package/dist/extras/extensions/keyword-extractor/index.d.ts +5 -0
- package/dist/extras/extensions/keyword-extractor/index.d.ts.map +1 -0
- package/dist/extras/extensions/keyword-extractor/index.js +3 -0
- package/dist/extras/extensions/keyword-extractor/index.js.map +1 -0
- package/dist/extras/extensions/keyword-extractor/nlp.d.ts +68 -0
- package/dist/extras/extensions/keyword-extractor/nlp.d.ts.map +1 -0
- package/dist/extras/extensions/keyword-extractor/nlp.js +156 -0
- package/dist/extras/extensions/keyword-extractor/nlp.js.map +1 -0
- package/dist/extras/extensions/keyword-extractor/nlp.test.d.ts +2 -0
- package/dist/extras/extensions/keyword-extractor/nlp.test.d.ts.map +1 -0
- package/dist/extras/extensions/keyword-extractor/nlp.test.js +176 -0
- package/dist/extras/extensions/keyword-extractor/nlp.test.js.map +1 -0
- package/dist/extras/extensions/logger/extension.d.ts +52 -0
- package/dist/extras/extensions/logger/extension.d.ts.map +1 -0
- package/dist/extras/extensions/logger/extension.js +68 -0
- package/dist/extras/extensions/logger/extension.js.map +1 -0
- package/dist/extras/extensions/logger/extension.json +7 -0
- package/dist/extras/extensions/logger/index.d.ts +3 -0
- package/dist/extras/extensions/logger/index.d.ts.map +1 -0
- package/dist/extras/extensions/logger/index.js +2 -0
- package/dist/extras/extensions/logger/index.js.map +1 -0
- package/dist/extras/extensions/mcp/extension.d.ts +63 -0
- package/dist/extras/extensions/mcp/extension.d.ts.map +1 -0
- package/dist/extras/extensions/mcp/extension.js +437 -0
- package/dist/extras/extensions/mcp/extension.js.map +1 -0
- package/dist/extras/extensions/mcp/extension.json +7 -0
- package/dist/extras/extensions/mcp/extension.test.d.ts +2 -0
- package/dist/extras/extensions/mcp/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/mcp/extension.test.js +286 -0
- package/dist/extras/extensions/mcp/extension.test.js.map +1 -0
- package/dist/extras/extensions/mcp/index.d.ts +3 -0
- package/dist/extras/extensions/mcp/index.d.ts.map +1 -0
- package/dist/extras/extensions/mcp/index.js +2 -0
- package/dist/extras/extensions/mcp/index.js.map +1 -0
- package/dist/extras/extensions/mem-session/extension.d.ts +24 -0
- package/dist/extras/extensions/mem-session/extension.d.ts.map +1 -0
- package/dist/extras/extensions/mem-session/extension.js +49 -0
- package/dist/extras/extensions/mem-session/extension.js.map +1 -0
- package/dist/extras/extensions/mem-session/extension.json +6 -0
- package/dist/extras/extensions/mem-session/index.d.ts +2 -0
- package/dist/extras/extensions/mem-session/index.d.ts.map +1 -0
- package/dist/extras/extensions/mem-session/index.js +2 -0
- package/dist/extras/extensions/mem-session/index.js.map +1 -0
- package/dist/extras/extensions/memory/extension.d.ts +54 -0
- package/dist/extras/extensions/memory/extension.d.ts.map +1 -0
- package/dist/extras/extensions/memory/extension.js +143 -0
- package/dist/extras/extensions/memory/extension.js.map +1 -0
- package/dist/extras/extensions/memory/extension.json +7 -0
- package/dist/extras/extensions/memory/index.d.ts +5 -0
- package/dist/extras/extensions/memory/index.d.ts.map +1 -0
- package/dist/extras/extensions/memory/index.js +3 -0
- package/dist/extras/extensions/memory/index.js.map +1 -0
- package/dist/extras/extensions/memory/llm-extractor.d.ts +42 -0
- package/dist/extras/extensions/memory/llm-extractor.d.ts.map +1 -0
- package/dist/extras/extensions/memory/llm-extractor.js +89 -0
- package/dist/extras/extensions/memory/llm-extractor.js.map +1 -0
- package/dist/extras/extensions/memory/types.d.ts +142 -0
- package/dist/extras/extensions/memory/types.d.ts.map +1 -0
- package/dist/extras/extensions/memory/types.js +2 -0
- package/dist/extras/extensions/memory/types.js.map +1 -0
- package/dist/extras/extensions/otel/extension.d.ts +57 -0
- package/dist/extras/extensions/otel/extension.d.ts.map +1 -0
- package/dist/extras/extensions/otel/extension.js +253 -0
- package/dist/extras/extensions/otel/extension.js.map +1 -0
- package/dist/extras/extensions/otel/extension.json +7 -0
- package/dist/extras/extensions/otel/extension.test.d.ts +2 -0
- package/dist/extras/extensions/otel/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/otel/extension.test.js +399 -0
- package/dist/extras/extensions/otel/extension.test.js.map +1 -0
- package/dist/extras/extensions/otel/index.d.ts +4 -0
- package/dist/extras/extensions/otel/index.d.ts.map +1 -0
- package/dist/extras/extensions/otel/index.js +3 -0
- package/dist/extras/extensions/otel/index.js.map +1 -0
- package/dist/extras/extensions/otel/jsonl-span-exporter.d.ts +60 -0
- package/dist/extras/extensions/otel/jsonl-span-exporter.d.ts.map +1 -0
- package/dist/extras/extensions/otel/jsonl-span-exporter.js +134 -0
- package/dist/extras/extensions/otel/jsonl-span-exporter.js.map +1 -0
- package/dist/extras/extensions/scripts/extension.d.ts +180 -0
- package/dist/extras/extensions/scripts/extension.d.ts.map +1 -0
- package/dist/extras/extensions/scripts/extension.js +618 -0
- package/dist/extras/extensions/scripts/extension.js.map +1 -0
- package/dist/extras/extensions/scripts/extension.json +7 -0
- package/dist/extras/extensions/scripts/extension.test.d.ts +2 -0
- package/dist/extras/extensions/scripts/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/scripts/extension.test.js +552 -0
- package/dist/extras/extensions/scripts/extension.test.js.map +1 -0
- package/dist/extras/extensions/scripts/index.d.ts +3 -0
- package/dist/extras/extensions/scripts/index.d.ts.map +1 -0
- package/dist/extras/extensions/scripts/index.js +2 -0
- package/dist/extras/extensions/scripts/index.js.map +1 -0
- package/dist/extras/extensions/skills/extension.d.ts +105 -0
- package/dist/extras/extensions/skills/extension.d.ts.map +1 -0
- package/dist/extras/extensions/skills/extension.js +419 -0
- package/dist/extras/extensions/skills/extension.js.map +1 -0
- package/dist/extras/extensions/skills/extension.json +7 -0
- package/dist/extras/extensions/skills/extension.test.d.ts +2 -0
- package/dist/extras/extensions/skills/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/skills/extension.test.js +184 -0
- package/dist/extras/extensions/skills/extension.test.js.map +1 -0
- package/dist/extras/extensions/skills/index.d.ts +4 -0
- package/dist/extras/extensions/skills/index.d.ts.map +1 -0
- package/dist/extras/extensions/skills/index.js +3 -0
- package/dist/extras/extensions/skills/index.js.map +1 -0
- package/dist/extras/extensions/streaming/extension.d.ts +15 -0
- package/dist/extras/extensions/streaming/extension.d.ts.map +1 -0
- package/dist/extras/extensions/streaming/extension.js +41 -0
- package/dist/extras/extensions/streaming/extension.js.map +1 -0
- package/dist/extras/extensions/streaming/extension.json +6 -0
- package/dist/extras/extensions/streaming/index.d.ts +2 -0
- package/dist/extras/extensions/streaming/index.d.ts.map +1 -0
- package/dist/extras/extensions/streaming/index.js +2 -0
- package/dist/extras/extensions/streaming/index.js.map +1 -0
- package/dist/extras/extensions/structured-output/extension.d.ts +11 -0
- package/dist/extras/extensions/structured-output/extension.d.ts.map +1 -0
- package/dist/extras/extensions/structured-output/extension.js +102 -0
- package/dist/extras/extensions/structured-output/extension.js.map +1 -0
- package/dist/extras/extensions/structured-output/extension.json +7 -0
- package/dist/extras/extensions/structured-output/index.d.ts +3 -0
- package/dist/extras/extensions/structured-output/index.d.ts.map +1 -0
- package/dist/extras/extensions/structured-output/index.js +2 -0
- package/dist/extras/extensions/structured-output/index.js.map +1 -0
- package/dist/extras/extensions/timestamp/extension.d.ts +11 -0
- package/dist/extras/extensions/timestamp/extension.d.ts.map +1 -0
- package/dist/extras/extensions/timestamp/extension.js +21 -0
- package/dist/extras/extensions/timestamp/extension.js.map +1 -0
- package/dist/extras/extensions/timestamp/extension.json +6 -0
- package/dist/extras/extensions/timestamp/index.d.ts +2 -0
- package/dist/extras/extensions/timestamp/index.d.ts.map +1 -0
- package/dist/extras/extensions/timestamp/index.js +2 -0
- package/dist/extras/extensions/timestamp/index.js.map +1 -0
- package/dist/extras/extensions/token-stats/extension.d.ts +50 -0
- package/dist/extras/extensions/token-stats/extension.d.ts.map +1 -0
- package/dist/extras/extensions/token-stats/extension.js +99 -0
- package/dist/extras/extensions/token-stats/extension.js.map +1 -0
- package/dist/extras/extensions/token-stats/extension.json +7 -0
- package/dist/extras/extensions/token-stats/extension.test.d.ts +2 -0
- package/dist/extras/extensions/token-stats/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/token-stats/extension.test.js +197 -0
- package/dist/extras/extensions/token-stats/extension.test.js.map +1 -0
- package/dist/extras/extensions/token-stats/index.d.ts +3 -0
- package/dist/extras/extensions/token-stats/index.d.ts.map +1 -0
- package/dist/extras/extensions/token-stats/index.js +2 -0
- package/dist/extras/extensions/token-stats/index.js.map +1 -0
- package/dist/extras/extensions/tool-buffer/extension.d.ts +89 -0
- package/dist/extras/extensions/tool-buffer/extension.d.ts.map +1 -0
- package/dist/extras/extensions/tool-buffer/extension.js +182 -0
- package/dist/extras/extensions/tool-buffer/extension.js.map +1 -0
- package/dist/extras/extensions/tool-buffer/extension.json +7 -0
- package/dist/extras/extensions/tool-buffer/extension.test.d.ts +2 -0
- package/dist/extras/extensions/tool-buffer/extension.test.d.ts.map +1 -0
- package/dist/extras/extensions/tool-buffer/extension.test.js +549 -0
- package/dist/extras/extensions/tool-buffer/extension.test.js.map +1 -0
- package/dist/extras/extensions/tool-buffer/index.d.ts +3 -0
- package/dist/extras/extensions/tool-buffer/index.d.ts.map +1 -0
- package/dist/extras/extensions/tool-buffer/index.js +2 -0
- package/dist/extras/extensions/tool-buffer/index.js.map +1 -0
- package/dist/extras/extensions/weather-tool/extension.d.ts +22 -0
- package/dist/extras/extensions/weather-tool/extension.d.ts.map +1 -0
- package/dist/extras/extensions/weather-tool/extension.js +33 -0
- package/dist/extras/extensions/weather-tool/extension.js.map +1 -0
- package/dist/extras/extensions/weather-tool/extension.json +7 -0
- package/dist/extras/extensions/weather-tool/index.d.ts +3 -0
- package/dist/extras/extensions/weather-tool/index.d.ts.map +1 -0
- package/dist/extras/extensions/weather-tool/index.js +2 -0
- package/dist/extras/extensions/weather-tool/index.js.map +1 -0
- package/dist/extras/models/ai-sdk-resolver.d.ts +15 -0
- package/dist/extras/models/ai-sdk-resolver.d.ts.map +1 -0
- package/dist/extras/models/ai-sdk-resolver.js +57 -0
- package/dist/extras/models/ai-sdk-resolver.js.map +1 -0
- package/dist/extras/models/index.d.ts +5 -0
- package/dist/extras/models/index.d.ts.map +1 -0
- package/dist/extras/models/index.js +3 -0
- package/dist/extras/models/index.js.map +1 -0
- package/dist/extras/models/routing-model.d.ts +18 -0
- package/dist/extras/models/routing-model.d.ts.map +1 -0
- package/dist/extras/models/routing-model.js +24 -0
- package/dist/extras/models/routing-model.js.map +1 -0
- package/dist/extras/openai-provider/index.d.ts +3 -0
- package/dist/extras/openai-provider/index.d.ts.map +1 -0
- package/dist/extras/openai-provider/index.js +2 -0
- package/dist/extras/openai-provider/index.js.map +1 -0
- package/dist/extras/openai-provider/server.d.ts +34 -0
- package/dist/extras/openai-provider/server.d.ts.map +1 -0
- package/dist/extras/openai-provider/server.js +335 -0
- package/dist/extras/openai-provider/server.js.map +1 -0
- package/dist/handle.d.ts +56 -0
- package/dist/handle.d.ts.map +1 -0
- package/dist/handle.js +2 -0
- package/dist/handle.js.map +1 -0
- package/dist/hooks.d.ts +98 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +53 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/model.d.ts +75 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +2 -0
- package/dist/model.js.map +1 -0
- package/dist/tool.d.ts +78 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +78 -0
- package/dist/tool.js.map +1 -0
- package/dist/types.d.ts +83 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/package.json +178 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import type { Extension } from '../extension.js';
|
|
2
|
+
/**
|
|
3
|
+
* Absolute path to the shared extensions directory (bundled with libra).
|
|
4
|
+
*
|
|
5
|
+
* Computed at runtime from `import.meta.url`, so it always resolves to
|
|
6
|
+
* the correct location regardless of where libra is installed.
|
|
7
|
+
*
|
|
8
|
+
* Use with {@link loadExtensions} to load built-in extensions (logging,
|
|
9
|
+
* session, timestamp, emoji, weather-tool, streaming, structured-output,
|
|
10
|
+
* mcp, skills) alongside your own local extensions in a single call:
|
|
11
|
+
*
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { loadExtensions, sharedExtensionsDir } from '@xandout/libra-harness/extras';
|
|
14
|
+
*
|
|
15
|
+
* const loaded = await loadExtensions(
|
|
16
|
+
* [sharedExtensionsDir, './extensions'],
|
|
17
|
+
* { skillsDirs: './skills', mcpConfigPaths: './mcpServers.json' },
|
|
18
|
+
* );
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* For simpler cases, you can also import individual extensions directly:
|
|
22
|
+
* `import { createLoggerExtension } from '@xandout/libra-harness/extras/logger'`.
|
|
23
|
+
*/
|
|
24
|
+
export declare const sharedExtensionsDir: string;
|
|
25
|
+
/**
|
|
26
|
+
* Optional manifest for an extension folder.
|
|
27
|
+
*
|
|
28
|
+
* If present (`extension.json`), it provides metadata that can be read
|
|
29
|
+
* without importing the entry point — useful for listing/discovering
|
|
30
|
+
* extensions cheaply (analogous to SKILL.md frontmatter).
|
|
31
|
+
*/
|
|
32
|
+
export interface ExtensionManifest {
|
|
33
|
+
/** Display name. Defaults to the directory name. */
|
|
34
|
+
name?: string;
|
|
35
|
+
/** What the extension does. */
|
|
36
|
+
description?: string;
|
|
37
|
+
/** If false, the extension is discovered but not loaded. Defaults to true. */
|
|
38
|
+
enabled?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Load priority. Higher = loaded first. Extensions with the same
|
|
41
|
+
* priority are sorted alphabetically by name. Default: 0.
|
|
42
|
+
*
|
|
43
|
+
* Use this to ensure observability extensions (logging) load before
|
|
44
|
+
* mutators (session, skills) so their before-hooks see raw state.
|
|
45
|
+
*/
|
|
46
|
+
priority?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Config keys this extension expects to receive from the host's config
|
|
49
|
+
* object. Documented here for discoverability — the loader passes the
|
|
50
|
+
* entire config object to every factory, and each factory picks out
|
|
51
|
+
* the keys it needs. Keys listed here are informational only (not
|
|
52
|
+
* enforced), but tools can inspect them to show what an extension
|
|
53
|
+
* wants.
|
|
54
|
+
*/
|
|
55
|
+
configKeys?: string[];
|
|
56
|
+
/** Arbitrary key-value metadata. */
|
|
57
|
+
metadata?: Record<string, string>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A discovered extension folder — manifest metadata plus the directory path.
|
|
61
|
+
* Available without importing the entry point (progressive disclosure
|
|
62
|
+
* level 1).
|
|
63
|
+
*/
|
|
64
|
+
export interface DiscoveredExtension {
|
|
65
|
+
/** Name from the manifest, or the directory name. */
|
|
66
|
+
name: string;
|
|
67
|
+
/** Description from the manifest, if any. */
|
|
68
|
+
description?: string;
|
|
69
|
+
/** Whether the manifest enabled this extension. */
|
|
70
|
+
enabled: boolean;
|
|
71
|
+
/** Load priority (higher = loaded first). Default: 0. */
|
|
72
|
+
priority: number;
|
|
73
|
+
/** Config keys this extension expects (informational, from manifest). */
|
|
74
|
+
configKeys?: string[];
|
|
75
|
+
/** Arbitrary metadata from the manifest. */
|
|
76
|
+
metadata?: Record<string, string>;
|
|
77
|
+
/** Absolute path to the extension directory. */
|
|
78
|
+
dir: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A loaded extension — the discovered metadata plus the installed
|
|
82
|
+
* `Extension` object (progressive disclosure level 2).
|
|
83
|
+
*/
|
|
84
|
+
export interface LoadedExtension extends DiscoveredExtension {
|
|
85
|
+
/** The installed extension instance. */
|
|
86
|
+
extension: Extension;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Discover extension folders under one or more directories.
|
|
90
|
+
*
|
|
91
|
+
* Each immediate subdirectory is treated as a candidate extension. A
|
|
92
|
+
* subdirectory is considered an extension if it contains either an
|
|
93
|
+
* `extension.json` manifest or one of the entry-point files
|
|
94
|
+
* (`extension.ts`/`extension.js`/`index.ts`/`index.js`).
|
|
95
|
+
*
|
|
96
|
+
* Only metadata is read — no code is imported. This is the cheap,
|
|
97
|
+
* progressive-disclosure level 1.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* const found = discoverExtensions(['./extensions', './more']);
|
|
101
|
+
* console.log(found.map((e) => e.name));
|
|
102
|
+
*/
|
|
103
|
+
export declare function discoverExtensions(dirs: string | string[]): DiscoveredExtension[];
|
|
104
|
+
/**
|
|
105
|
+
* Load a single discovered extension by importing its entry point.
|
|
106
|
+
*
|
|
107
|
+
* The entry point's default export may be either:
|
|
108
|
+
* - An `Extension` object (with `name` and `install`) — config is ignored.
|
|
109
|
+
* - A factory function `(config) => Extension | Promise<Extension | undefined>`.
|
|
110
|
+
*
|
|
111
|
+
* When a factory is used, the `config` object is passed to it. The
|
|
112
|
+
* factory extracts the keys it needs (declared in the manifest's
|
|
113
|
+
* `configKeys`). A factory may return `undefined` to opt out (e.g. when
|
|
114
|
+
* required config is missing or a config file doesn't exist).
|
|
115
|
+
*
|
|
116
|
+
* The extension's own `name` field takes precedence over the manifest/dir
|
|
117
|
+
* name once loaded.
|
|
118
|
+
*/
|
|
119
|
+
export declare function loadExtension(discovered: DiscoveredExtension, config?: Record<string, unknown>): Promise<LoadedExtension | undefined>;
|
|
120
|
+
/**
|
|
121
|
+
* A factory function that creates an Extension, optionally using config.
|
|
122
|
+
* May return `undefined` to opt out (e.g. when required config is missing).
|
|
123
|
+
*/
|
|
124
|
+
export type ExtensionFactory = (config?: Record<string, unknown>) => Extension | Promise<Extension | undefined>;
|
|
125
|
+
/**
|
|
126
|
+
* Items accepted by {@link loadExtensions}. Mix freely:
|
|
127
|
+
* - `string` — a directory path to discover extensions in
|
|
128
|
+
* - `ExtensionFactory` — a factory function called with config
|
|
129
|
+
* - `Extension` — a ready-to-install extension object
|
|
130
|
+
*/
|
|
131
|
+
export type ExtensionInput = string | ExtensionFactory | Extension;
|
|
132
|
+
/**
|
|
133
|
+
* Discover and load extensions from a mix of sources.
|
|
134
|
+
*
|
|
135
|
+
* Accepts an array of directory paths, factory functions, and/or
|
|
136
|
+
* ready-made `Extension` objects. Directory paths are scanned for
|
|
137
|
+
* extension subdirectories (each with `extension.json` and/or an entry
|
|
138
|
+
* point). Factory functions are called with the `config` object. All
|
|
139
|
+
* results are merged and sorted by priority (higher first, then
|
|
140
|
+
* alphabetical).
|
|
141
|
+
*
|
|
142
|
+
* The `config` object is passed to every factory function. Each factory
|
|
143
|
+
* extracts the keys it needs. Factories may return `undefined` to opt
|
|
144
|
+
* out (e.g. when a required config file doesn't exist).
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* // Directory only — discover all extensions in a folder.
|
|
148
|
+
* const loaded = await loadExtensions('./extensions', config);
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* // Mix built-in factories with local directory discovery.
|
|
152
|
+
* import { createLoggerExtension } from '@xandout/libra-harness/extras/logger';
|
|
153
|
+
* import { createMcpExtension } from '@xandout/libra-harness/extras/mcp';
|
|
154
|
+
*
|
|
155
|
+
* const loaded = await loadExtensions(
|
|
156
|
+
* [createLoggerExtension, createMcpExtension, './extensions'],
|
|
157
|
+
* { mcpConfigPaths: './mcpServers.json' },
|
|
158
|
+
* );
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* // Ready-made Extension objects alongside directories.
|
|
162
|
+
* const loaded = await loadExtensions(
|
|
163
|
+
* [myCustomExtension, './more-extensions'],
|
|
164
|
+
* config,
|
|
165
|
+
* );
|
|
166
|
+
*/
|
|
167
|
+
export declare function loadExtensions(inputs: ExtensionInput | ExtensionInput[], config?: Record<string, unknown>): Promise<LoadedExtension[]>;
|
|
168
|
+
/**
|
|
169
|
+
* Install all loaded extensions onto an agent, in priority order.
|
|
170
|
+
*
|
|
171
|
+
* This is the standard companion to {@link loadExtensions} — the loader
|
|
172
|
+
* sorts by priority, this calls `agent.use()` for each in that order.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* const loaded = await loadExtensions('./extensions', config);
|
|
176
|
+
* installExtensions(loaded, agent);
|
|
177
|
+
*/
|
|
178
|
+
export declare function installExtensions(loaded: LoadedExtension[], agent: {
|
|
179
|
+
use(e: Extension): unknown;
|
|
180
|
+
}): void;
|
|
181
|
+
/**
|
|
182
|
+
* Unload all extensions from an agent, in reverse priority order.
|
|
183
|
+
*
|
|
184
|
+
* Calls `agent.unload(name)` for each extension — removes hooks and tools
|
|
185
|
+
* registered by that extension, and calls `close()` if the extension has
|
|
186
|
+
* one. Useful for hot-reloading or reconfiguring an agent without
|
|
187
|
+
* recreating it.
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* await unloadExtensions(loaded, agent);
|
|
191
|
+
*/
|
|
192
|
+
export declare function unloadExtensions(loaded: LoadedExtension[], agent: {
|
|
193
|
+
unload(name: string): Promise<unknown>;
|
|
194
|
+
}): Promise<void>;
|
|
195
|
+
/**
|
|
196
|
+
* Close all extensions that have a `close()` method (e.g. MCP clients).
|
|
197
|
+
*
|
|
198
|
+
* Calls `close()` in reverse priority order (last-loaded closes first).
|
|
199
|
+
* Extensions without `close()` are skipped silently.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* await closeExtensions(loaded);
|
|
203
|
+
*/
|
|
204
|
+
export declare function closeExtensions(loaded: LoadedExtension[]): Promise<void>;
|
|
205
|
+
//# sourceMappingURL=extension-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension-loader.d.ts","sourceRoot":"","sources":["../../src/extras/extension-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAKjD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,OAAO,EAAE,OAAO,CAAC;IACjB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,MAAM,WAAW,eAAgB,SAAQ,mBAAmB;IAC1D,wCAAwC;IACxC,SAAS,EAAE,SAAS,CAAC;CACtB;AAkCD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,mBAAmB,EAAE,CAiCjF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,mBAAmB,EAC/B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CA2CtC;AAED;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,SAAS,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,GAAG,cAAc,EAAE,EACzC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,eAAe,EAAE,CAAC,CAmE5B;AAkBD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,KAAK,EAAE;IAClE,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;CAC5B,GAAG,IAAI,CAKP;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,KAAK,EAAE;IACvE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACxC,GAAG,OAAO,CAAC,IAAI,CAAC,CAMhB;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAO9E"}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { readdirSync, statSync, existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
4
|
+
/**
|
|
5
|
+
* Absolute path to the shared extensions directory (bundled with libra).
|
|
6
|
+
*
|
|
7
|
+
* Computed at runtime from `import.meta.url`, so it always resolves to
|
|
8
|
+
* the correct location regardless of where libra is installed.
|
|
9
|
+
*
|
|
10
|
+
* Use with {@link loadExtensions} to load built-in extensions (logging,
|
|
11
|
+
* session, timestamp, emoji, weather-tool, streaming, structured-output,
|
|
12
|
+
* mcp, skills) alongside your own local extensions in a single call:
|
|
13
|
+
*
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { loadExtensions, sharedExtensionsDir } from '@xandout/libra-harness/extras';
|
|
16
|
+
*
|
|
17
|
+
* const loaded = await loadExtensions(
|
|
18
|
+
* [sharedExtensionsDir, './extensions'],
|
|
19
|
+
* { skillsDirs: './skills', mcpConfigPaths: './mcpServers.json' },
|
|
20
|
+
* );
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* For simpler cases, you can also import individual extensions directly:
|
|
24
|
+
* `import { createLoggerExtension } from '@xandout/libra-harness/extras/logger'`.
|
|
25
|
+
*/
|
|
26
|
+
export const sharedExtensionsDir = fileURLToPath(new URL('./extensions', import.meta.url));
|
|
27
|
+
/** Entry-point filenames tried in order. */
|
|
28
|
+
const ENTRY_POINTS = ['extension.ts', 'extension.js', 'index.ts', 'index.js'];
|
|
29
|
+
/**
|
|
30
|
+
* Read the optional `extension.json` manifest from a directory.
|
|
31
|
+
* Returns `{}` if the file is absent or invalid.
|
|
32
|
+
*/
|
|
33
|
+
function readManifest(dir) {
|
|
34
|
+
const manifestPath = join(dir, 'extension.json');
|
|
35
|
+
if (!existsSync(manifestPath))
|
|
36
|
+
return {};
|
|
37
|
+
try {
|
|
38
|
+
const raw = readFileSync(manifestPath, 'utf-8');
|
|
39
|
+
return JSON.parse(raw);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Find the entry-point file for an extension directory.
|
|
47
|
+
* Returns the absolute path, or `undefined` if none of the candidates exist.
|
|
48
|
+
*/
|
|
49
|
+
function findEntryPoint(dir) {
|
|
50
|
+
for (const candidate of ENTRY_POINTS) {
|
|
51
|
+
const path = join(dir, candidate);
|
|
52
|
+
if (existsSync(path) && statSync(path).isFile()) {
|
|
53
|
+
return path;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Discover extension folders under one or more directories.
|
|
60
|
+
*
|
|
61
|
+
* Each immediate subdirectory is treated as a candidate extension. A
|
|
62
|
+
* subdirectory is considered an extension if it contains either an
|
|
63
|
+
* `extension.json` manifest or one of the entry-point files
|
|
64
|
+
* (`extension.ts`/`extension.js`/`index.ts`/`index.js`).
|
|
65
|
+
*
|
|
66
|
+
* Only metadata is read — no code is imported. This is the cheap,
|
|
67
|
+
* progressive-disclosure level 1.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* const found = discoverExtensions(['./extensions', './more']);
|
|
71
|
+
* console.log(found.map((e) => e.name));
|
|
72
|
+
*/
|
|
73
|
+
export function discoverExtensions(dirs) {
|
|
74
|
+
const searchDirs = Array.isArray(dirs) ? dirs : [dirs];
|
|
75
|
+
const found = [];
|
|
76
|
+
for (const searchDir of searchDirs) {
|
|
77
|
+
if (!existsSync(searchDir) || !statSync(searchDir).isDirectory())
|
|
78
|
+
continue;
|
|
79
|
+
for (const entry of readdirSync(searchDir)) {
|
|
80
|
+
const extDir = join(searchDir, entry);
|
|
81
|
+
if (!statSync(extDir).isDirectory())
|
|
82
|
+
continue;
|
|
83
|
+
const manifest = readManifest(extDir);
|
|
84
|
+
const hasEntryPoint = findEntryPoint(extDir) !== undefined;
|
|
85
|
+
// Skip directories that are neither manifest-declared nor code-bearing.
|
|
86
|
+
if (!hasEntryPoint && Object.keys(manifest).length === 0)
|
|
87
|
+
continue;
|
|
88
|
+
found.push({
|
|
89
|
+
name: manifest.name ?? entry,
|
|
90
|
+
...(manifest.description && { description: manifest.description }),
|
|
91
|
+
enabled: manifest.enabled !== false,
|
|
92
|
+
priority: manifest.priority ?? 0,
|
|
93
|
+
...(manifest.configKeys && { configKeys: manifest.configKeys }),
|
|
94
|
+
...(manifest.metadata && { metadata: manifest.metadata }),
|
|
95
|
+
dir: extDir,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Sort by priority (descending — higher loads first), then name (ascending).
|
|
100
|
+
found.sort((a, b) => b.priority - a.priority || a.name.localeCompare(b.name));
|
|
101
|
+
return found;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Load a single discovered extension by importing its entry point.
|
|
105
|
+
*
|
|
106
|
+
* The entry point's default export may be either:
|
|
107
|
+
* - An `Extension` object (with `name` and `install`) — config is ignored.
|
|
108
|
+
* - A factory function `(config) => Extension | Promise<Extension | undefined>`.
|
|
109
|
+
*
|
|
110
|
+
* When a factory is used, the `config` object is passed to it. The
|
|
111
|
+
* factory extracts the keys it needs (declared in the manifest's
|
|
112
|
+
* `configKeys`). A factory may return `undefined` to opt out (e.g. when
|
|
113
|
+
* required config is missing or a config file doesn't exist).
|
|
114
|
+
*
|
|
115
|
+
* The extension's own `name` field takes precedence over the manifest/dir
|
|
116
|
+
* name once loaded.
|
|
117
|
+
*/
|
|
118
|
+
export async function loadExtension(discovered, config) {
|
|
119
|
+
const entryPath = findEntryPoint(discovered.dir);
|
|
120
|
+
if (!entryPath) {
|
|
121
|
+
throw new Error(`Extension "${discovered.name}" has no entry point in ${discovered.dir}`);
|
|
122
|
+
}
|
|
123
|
+
// file:// URL is required for dynamic import on Windows and is harmless on POSIX.
|
|
124
|
+
const mod = await import(pathToFileURL(entryPath).href);
|
|
125
|
+
const exported = mod.default;
|
|
126
|
+
if (exported === undefined || exported === null) {
|
|
127
|
+
throw new Error(`Extension "${discovered.name}" entry point ${entryPath} has no default export`);
|
|
128
|
+
}
|
|
129
|
+
let extension;
|
|
130
|
+
if (typeof exported === 'function') {
|
|
131
|
+
// Factory function — pass config. May return undefined to opt out.
|
|
132
|
+
extension = await exported(config);
|
|
133
|
+
}
|
|
134
|
+
else if (typeof exported === 'object' &&
|
|
135
|
+
typeof exported.name === 'string' &&
|
|
136
|
+
typeof exported.install === 'function') {
|
|
137
|
+
extension = exported;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
throw new Error(`Extension "${discovered.name}" entry point ${entryPath} must default-export an Extension or a factory function`);
|
|
141
|
+
}
|
|
142
|
+
if (!extension) {
|
|
143
|
+
// Factory opted out — return undefined so the caller can skip it.
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
...discovered,
|
|
148
|
+
// The extension's own name wins once code is loaded.
|
|
149
|
+
name: extension.name ?? discovered.name,
|
|
150
|
+
extension,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Discover and load extensions from a mix of sources.
|
|
155
|
+
*
|
|
156
|
+
* Accepts an array of directory paths, factory functions, and/or
|
|
157
|
+
* ready-made `Extension` objects. Directory paths are scanned for
|
|
158
|
+
* extension subdirectories (each with `extension.json` and/or an entry
|
|
159
|
+
* point). Factory functions are called with the `config` object. All
|
|
160
|
+
* results are merged and sorted by priority (higher first, then
|
|
161
|
+
* alphabetical).
|
|
162
|
+
*
|
|
163
|
+
* The `config` object is passed to every factory function. Each factory
|
|
164
|
+
* extracts the keys it needs. Factories may return `undefined` to opt
|
|
165
|
+
* out (e.g. when a required config file doesn't exist).
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* // Directory only — discover all extensions in a folder.
|
|
169
|
+
* const loaded = await loadExtensions('./extensions', config);
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* // Mix built-in factories with local directory discovery.
|
|
173
|
+
* import { createLoggerExtension } from '@xandout/libra-harness/extras/logger';
|
|
174
|
+
* import { createMcpExtension } from '@xandout/libra-harness/extras/mcp';
|
|
175
|
+
*
|
|
176
|
+
* const loaded = await loadExtensions(
|
|
177
|
+
* [createLoggerExtension, createMcpExtension, './extensions'],
|
|
178
|
+
* { mcpConfigPaths: './mcpServers.json' },
|
|
179
|
+
* );
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* // Ready-made Extension objects alongside directories.
|
|
183
|
+
* const loaded = await loadExtensions(
|
|
184
|
+
* [myCustomExtension, './more-extensions'],
|
|
185
|
+
* config,
|
|
186
|
+
* );
|
|
187
|
+
*/
|
|
188
|
+
export async function loadExtensions(inputs, config) {
|
|
189
|
+
const items = Array.isArray(inputs) ? inputs : [inputs];
|
|
190
|
+
const loaded = [];
|
|
191
|
+
let total = 0;
|
|
192
|
+
let skipped = 0;
|
|
193
|
+
for (const item of items) {
|
|
194
|
+
if (typeof item === 'string') {
|
|
195
|
+
// Directory path — discover and load all extensions in it.
|
|
196
|
+
const discovered = discoverExtensions(item);
|
|
197
|
+
const enabled = discovered.filter((e) => e.enabled);
|
|
198
|
+
for (const ext of enabled) {
|
|
199
|
+
total++;
|
|
200
|
+
try {
|
|
201
|
+
const result = await loadExtension(ext, config);
|
|
202
|
+
if (result) {
|
|
203
|
+
loaded.push(result);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
skipped++;
|
|
207
|
+
console.log(`[extensions] skipped "${ext.name}" (factory opted out)`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
console.error(`[extensions] failed to load "${ext.name}" from ${ext.dir}:`, err instanceof Error ? err.message : err);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (typeof item === 'function') {
|
|
216
|
+
// Factory function — call with config.
|
|
217
|
+
total++;
|
|
218
|
+
try {
|
|
219
|
+
const extension = await item(config);
|
|
220
|
+
if (extension) {
|
|
221
|
+
loaded.push(makeLoaded(extension));
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
skipped++;
|
|
225
|
+
const name = item.name ?? 'anonymous';
|
|
226
|
+
console.log(`[extensions] skipped "${name}" (factory opted out)`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
const name = item.name ?? 'anonymous';
|
|
231
|
+
console.error(`[extensions] failed to load factory "${name}":`, err instanceof Error ? err.message : err);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
else if (item && typeof item.name === 'string' && typeof item.install === 'function') {
|
|
235
|
+
// Ready-made Extension object.
|
|
236
|
+
total++;
|
|
237
|
+
loaded.push(makeLoaded(item));
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
console.warn('[extensions] skipping unknown input type:', item);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// Sort by priority (descending), then name (ascending).
|
|
244
|
+
loaded.sort((a, b) => b.priority - a.priority || a.name.localeCompare(b.name));
|
|
245
|
+
console.log(`[extensions] loaded ${loaded.length}/${total} extension(s)` +
|
|
246
|
+
(skipped > 0 ? ` (${skipped} skipped)` : '') +
|
|
247
|
+
(loaded.length > 0 ? ': ' + loaded.map((e) => e.name).join(', ') : ''));
|
|
248
|
+
return loaded;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Wrap a ready-made Extension (from a factory or direct object) into a
|
|
252
|
+
* LoadedExtension, reading priority from the Extension itself.
|
|
253
|
+
*/
|
|
254
|
+
function makeLoaded(extension) {
|
|
255
|
+
return {
|
|
256
|
+
name: extension.name,
|
|
257
|
+
enabled: true,
|
|
258
|
+
priority: extension.priority ?? 0,
|
|
259
|
+
dir: '',
|
|
260
|
+
extension,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
// ── Install / Unload / Close helpers ──────────────────────────────────
|
|
264
|
+
/**
|
|
265
|
+
* Install all loaded extensions onto an agent, in priority order.
|
|
266
|
+
*
|
|
267
|
+
* This is the standard companion to {@link loadExtensions} — the loader
|
|
268
|
+
* sorts by priority, this calls `agent.use()` for each in that order.
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* const loaded = await loadExtensions('./extensions', config);
|
|
272
|
+
* installExtensions(loaded, agent);
|
|
273
|
+
*/
|
|
274
|
+
export function installExtensions(loaded, agent) {
|
|
275
|
+
for (const { extension, name, priority } of loaded) {
|
|
276
|
+
agent.use(extension);
|
|
277
|
+
console.log(`[extensions] installed "${name}" (priority ${priority})`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Unload all extensions from an agent, in reverse priority order.
|
|
282
|
+
*
|
|
283
|
+
* Calls `agent.unload(name)` for each extension — removes hooks and tools
|
|
284
|
+
* registered by that extension, and calls `close()` if the extension has
|
|
285
|
+
* one. Useful for hot-reloading or reconfiguring an agent without
|
|
286
|
+
* recreating it.
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* await unloadExtensions(loaded, agent);
|
|
290
|
+
*/
|
|
291
|
+
export async function unloadExtensions(loaded, agent) {
|
|
292
|
+
// Reverse order — unload last-installed first.
|
|
293
|
+
for (const { name } of [...loaded].reverse()) {
|
|
294
|
+
await agent.unload(name);
|
|
295
|
+
console.log(`[extensions] unloaded "${name}"`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Close all extensions that have a `close()` method (e.g. MCP clients).
|
|
300
|
+
*
|
|
301
|
+
* Calls `close()` in reverse priority order (last-loaded closes first).
|
|
302
|
+
* Extensions without `close()` are skipped silently.
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* await closeExtensions(loaded);
|
|
306
|
+
*/
|
|
307
|
+
export async function closeExtensions(loaded) {
|
|
308
|
+
for (const { extension, name } of [...loaded].reverse()) {
|
|
309
|
+
if (typeof extension.close === 'function') {
|
|
310
|
+
await extension.close();
|
|
311
|
+
console.log(`[extensions] closed "${name}"`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=extension-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension-loader.js","sourceRoot":"","sources":["../../src/extras/extension-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Extension } from '../../../extension.js';
|
|
2
|
+
/**
|
|
3
|
+
* Config for the auto-steer extension.
|
|
4
|
+
*/
|
|
5
|
+
export interface AutoSteerConfig {
|
|
6
|
+
/**
|
|
7
|
+
* The max iterations the agent is configured for. The extension steers
|
|
8
|
+
* at `threshold` iterations before this limit. Default: 25 (matching
|
|
9
|
+
* the agent's default).
|
|
10
|
+
*/
|
|
11
|
+
maxIterations?: number;
|
|
12
|
+
/**
|
|
13
|
+
* How many iterations before the limit to steer. Default: 3.
|
|
14
|
+
* With maxIterations=25 and threshold=3, the steer fires at iteration 22.
|
|
15
|
+
*/
|
|
16
|
+
threshold?: number;
|
|
17
|
+
/**
|
|
18
|
+
* The steering message to inject. Default: a prompt to wrap up.
|
|
19
|
+
*/
|
|
20
|
+
steerMessage?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create an auto-steer extension.
|
|
24
|
+
*
|
|
25
|
+
* Watches iteration count and steers the agent to wrap up when it's
|
|
26
|
+
* approaching the max iterations limit. This prevents the agent from
|
|
27
|
+
* hitting the limit with no response — instead of silently running out
|
|
28
|
+
* of turns, the agent gets a nudge like "You're running low on turns,
|
|
29
|
+
* summarize and respond now."
|
|
30
|
+
*
|
|
31
|
+
* Tracks iterations by counting `afterLLM` hook calls per turn. The
|
|
32
|
+
* count resets on each `beforeTurn`.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* import { createAutoSteerExtension } from '@xandout/libra-harness/extras/auto-steer';
|
|
37
|
+
*
|
|
38
|
+
* const autoSteer = createAutoSteerExtension({
|
|
39
|
+
* maxIterations: 15,
|
|
40
|
+
* threshold: 3,
|
|
41
|
+
* });
|
|
42
|
+
* agent.use(autoSteer);
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export default function createAutoSteerExtension(config?: AutoSteerConfig): Extension;
|
|
46
|
+
//# sourceMappingURL=extension.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../../../../src/extras/extensions/auto-steer/extension.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,OAAO,UAAU,wBAAwB,CAC9C,MAAM,CAAC,EAAE,eAAe,GACvB,SAAS,CA+BX"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create an auto-steer extension.
|
|
3
|
+
*
|
|
4
|
+
* Watches iteration count and steers the agent to wrap up when it's
|
|
5
|
+
* approaching the max iterations limit. This prevents the agent from
|
|
6
|
+
* hitting the limit with no response — instead of silently running out
|
|
7
|
+
* of turns, the agent gets a nudge like "You're running low on turns,
|
|
8
|
+
* summarize and respond now."
|
|
9
|
+
*
|
|
10
|
+
* Tracks iterations by counting `afterLLM` hook calls per turn. The
|
|
11
|
+
* count resets on each `beforeTurn`.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { createAutoSteerExtension } from '@xandout/libra-harness/extras/auto-steer';
|
|
16
|
+
*
|
|
17
|
+
* const autoSteer = createAutoSteerExtension({
|
|
18
|
+
* maxIterations: 15,
|
|
19
|
+
* threshold: 3,
|
|
20
|
+
* });
|
|
21
|
+
* agent.use(autoSteer);
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export default function createAutoSteerExtension(config) {
|
|
25
|
+
const maxIter = config?.maxIterations ?? 25;
|
|
26
|
+
const threshold = config?.threshold ?? 3;
|
|
27
|
+
const steerAt = maxIter - threshold;
|
|
28
|
+
const message = config?.steerMessage ??
|
|
29
|
+
"You're running low on turns. Stop calling tools and respond to the user now with what you have so far. Be concise.";
|
|
30
|
+
let iterationCount = 0;
|
|
31
|
+
let steered = false;
|
|
32
|
+
return {
|
|
33
|
+
name: 'auto-steer',
|
|
34
|
+
priority: 90,
|
|
35
|
+
install(agent) {
|
|
36
|
+
// Reset counter at the start of each turn.
|
|
37
|
+
agent.hook('beforeTurn', 'auto-steer', async () => {
|
|
38
|
+
iterationCount = 0;
|
|
39
|
+
steered = false;
|
|
40
|
+
});
|
|
41
|
+
// Count LLM calls and steer when approaching the limit.
|
|
42
|
+
agent.hook('afterLLM', 'auto-steer', async (ctx) => {
|
|
43
|
+
iterationCount++;
|
|
44
|
+
if (!steered && iterationCount >= steerAt) {
|
|
45
|
+
steered = true;
|
|
46
|
+
ctx.turn.steer(message);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../../../../src/extras/extensions/auto-steer/extension.ts"],"names":[],"mappings":"AAuBA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,OAAO,UAAU,wBAAwB,CAC9C,MAAwB;IAExB,MAAM,OAAO,GAAG,MAAM,EAAE,aAAa,IAAI,EAAE,CAAC;IAC5C,MAAM,SAAS,GAAG,MAAM,EAAE,SAAS,IAAI,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;IACpC,MAAM,OAAO,GAAG,MAAM,EAAE,YAAY;QAClC,oHAAoH,CAAC;IAEvH,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,QAAQ,EAAE,EAAE;QACZ,OAAO,CAAC,KAAK;YACX,2CAA2C;YAC3C,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,IAAI,EAAE;gBAChD,cAAc,GAAG,CAAC,CAAC;gBACnB,OAAO,GAAG,KAAK,CAAC;YAClB,CAAC,CAAC,CAAC;YAEH,wDAAwD;YACxD,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;gBACjD,cAAc,EAAE,CAAC;gBAEjB,IAAI,CAAC,OAAO,IAAI,cAAc,IAAI,OAAO,EAAE,CAAC;oBAC1C,OAAO,GAAG,IAAI,CAAC;oBACf,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/extras/extensions/auto-steer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AACrE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/extras/extensions/auto-steer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,gBAAgB,CAAC"}
|