@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.
Files changed (289) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +486 -0
  3. package/dist/agent.d.ts +174 -0
  4. package/dist/agent.d.ts.map +1 -0
  5. package/dist/agent.js +575 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/ai-sdk-model.d.ts +26 -0
  8. package/dist/ai-sdk-model.d.ts.map +1 -0
  9. package/dist/ai-sdk-model.js +260 -0
  10. package/dist/ai-sdk-model.js.map +1 -0
  11. package/dist/context.d.ts +75 -0
  12. package/dist/context.d.ts.map +1 -0
  13. package/dist/context.js +2 -0
  14. package/dist/context.js.map +1 -0
  15. package/dist/errors.d.ts +33 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +59 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/extension.d.ts +63 -0
  20. package/dist/extension.d.ts.map +1 -0
  21. package/dist/extension.js +2 -0
  22. package/dist/extension.js.map +1 -0
  23. package/dist/extras/extension-loader.d.ts +205 -0
  24. package/dist/extras/extension-loader.d.ts.map +1 -0
  25. package/dist/extras/extension-loader.js +315 -0
  26. package/dist/extras/extension-loader.js.map +1 -0
  27. package/dist/extras/extensions/auto-steer/extension.d.ts +46 -0
  28. package/dist/extras/extensions/auto-steer/extension.d.ts.map +1 -0
  29. package/dist/extras/extensions/auto-steer/extension.js +52 -0
  30. package/dist/extras/extensions/auto-steer/extension.js.map +1 -0
  31. package/dist/extras/extensions/auto-steer/extension.json +7 -0
  32. package/dist/extras/extensions/auto-steer/index.d.ts +3 -0
  33. package/dist/extras/extensions/auto-steer/index.d.ts.map +1 -0
  34. package/dist/extras/extensions/auto-steer/index.js +2 -0
  35. package/dist/extras/extensions/auto-steer/index.js.map +1 -0
  36. package/dist/extras/extensions/disk-session/extension.d.ts +167 -0
  37. package/dist/extras/extensions/disk-session/extension.d.ts.map +1 -0
  38. package/dist/extras/extensions/disk-session/extension.js +492 -0
  39. package/dist/extras/extensions/disk-session/extension.js.map +1 -0
  40. package/dist/extras/extensions/disk-session/extension.json +6 -0
  41. package/dist/extras/extensions/disk-session/extension.test.d.ts +2 -0
  42. package/dist/extras/extensions/disk-session/extension.test.d.ts.map +1 -0
  43. package/dist/extras/extensions/disk-session/extension.test.js +672 -0
  44. package/dist/extras/extensions/disk-session/extension.test.js.map +1 -0
  45. package/dist/extras/extensions/disk-session/index.d.ts +3 -0
  46. package/dist/extras/extensions/disk-session/index.d.ts.map +1 -0
  47. package/dist/extras/extensions/disk-session/index.js +2 -0
  48. package/dist/extras/extensions/disk-session/index.js.map +1 -0
  49. package/dist/extras/extensions/emoji/extension.d.ts +20 -0
  50. package/dist/extras/extensions/emoji/extension.d.ts.map +1 -0
  51. package/dist/extras/extensions/emoji/extension.js +22 -0
  52. package/dist/extras/extensions/emoji/extension.js.map +1 -0
  53. package/dist/extras/extensions/emoji/extension.json +7 -0
  54. package/dist/extras/extensions/emoji/index.d.ts +3 -0
  55. package/dist/extras/extensions/emoji/index.d.ts.map +1 -0
  56. package/dist/extras/extensions/emoji/index.js +2 -0
  57. package/dist/extras/extensions/emoji/index.js.map +1 -0
  58. package/dist/extras/extensions/filesystem/extension.d.ts +53 -0
  59. package/dist/extras/extensions/filesystem/extension.d.ts.map +1 -0
  60. package/dist/extras/extensions/filesystem/extension.js +350 -0
  61. package/dist/extras/extensions/filesystem/extension.js.map +1 -0
  62. package/dist/extras/extensions/filesystem/extension.json +6 -0
  63. package/dist/extras/extensions/filesystem/extension.test.d.ts +2 -0
  64. package/dist/extras/extensions/filesystem/extension.test.d.ts.map +1 -0
  65. package/dist/extras/extensions/filesystem/extension.test.js +387 -0
  66. package/dist/extras/extensions/filesystem/extension.test.js.map +1 -0
  67. package/dist/extras/extensions/filesystem/index.d.ts +2 -0
  68. package/dist/extras/extensions/filesystem/index.d.ts.map +1 -0
  69. package/dist/extras/extensions/filesystem/index.js +2 -0
  70. package/dist/extras/extensions/filesystem/index.js.map +1 -0
  71. package/dist/extras/extensions/keyword-extractor/extension.d.ts +53 -0
  72. package/dist/extras/extensions/keyword-extractor/extension.d.ts.map +1 -0
  73. package/dist/extras/extensions/keyword-extractor/extension.js +61 -0
  74. package/dist/extras/extensions/keyword-extractor/extension.js.map +1 -0
  75. package/dist/extras/extensions/keyword-extractor/extension.json +6 -0
  76. package/dist/extras/extensions/keyword-extractor/extension.test.d.ts +2 -0
  77. package/dist/extras/extensions/keyword-extractor/extension.test.d.ts.map +1 -0
  78. package/dist/extras/extensions/keyword-extractor/extension.test.js +105 -0
  79. package/dist/extras/extensions/keyword-extractor/extension.test.js.map +1 -0
  80. package/dist/extras/extensions/keyword-extractor/index.d.ts +5 -0
  81. package/dist/extras/extensions/keyword-extractor/index.d.ts.map +1 -0
  82. package/dist/extras/extensions/keyword-extractor/index.js +3 -0
  83. package/dist/extras/extensions/keyword-extractor/index.js.map +1 -0
  84. package/dist/extras/extensions/keyword-extractor/nlp.d.ts +68 -0
  85. package/dist/extras/extensions/keyword-extractor/nlp.d.ts.map +1 -0
  86. package/dist/extras/extensions/keyword-extractor/nlp.js +156 -0
  87. package/dist/extras/extensions/keyword-extractor/nlp.js.map +1 -0
  88. package/dist/extras/extensions/keyword-extractor/nlp.test.d.ts +2 -0
  89. package/dist/extras/extensions/keyword-extractor/nlp.test.d.ts.map +1 -0
  90. package/dist/extras/extensions/keyword-extractor/nlp.test.js +176 -0
  91. package/dist/extras/extensions/keyword-extractor/nlp.test.js.map +1 -0
  92. package/dist/extras/extensions/logger/extension.d.ts +52 -0
  93. package/dist/extras/extensions/logger/extension.d.ts.map +1 -0
  94. package/dist/extras/extensions/logger/extension.js +68 -0
  95. package/dist/extras/extensions/logger/extension.js.map +1 -0
  96. package/dist/extras/extensions/logger/extension.json +7 -0
  97. package/dist/extras/extensions/logger/index.d.ts +3 -0
  98. package/dist/extras/extensions/logger/index.d.ts.map +1 -0
  99. package/dist/extras/extensions/logger/index.js +2 -0
  100. package/dist/extras/extensions/logger/index.js.map +1 -0
  101. package/dist/extras/extensions/mcp/extension.d.ts +63 -0
  102. package/dist/extras/extensions/mcp/extension.d.ts.map +1 -0
  103. package/dist/extras/extensions/mcp/extension.js +437 -0
  104. package/dist/extras/extensions/mcp/extension.js.map +1 -0
  105. package/dist/extras/extensions/mcp/extension.json +7 -0
  106. package/dist/extras/extensions/mcp/extension.test.d.ts +2 -0
  107. package/dist/extras/extensions/mcp/extension.test.d.ts.map +1 -0
  108. package/dist/extras/extensions/mcp/extension.test.js +286 -0
  109. package/dist/extras/extensions/mcp/extension.test.js.map +1 -0
  110. package/dist/extras/extensions/mcp/index.d.ts +3 -0
  111. package/dist/extras/extensions/mcp/index.d.ts.map +1 -0
  112. package/dist/extras/extensions/mcp/index.js +2 -0
  113. package/dist/extras/extensions/mcp/index.js.map +1 -0
  114. package/dist/extras/extensions/mem-session/extension.d.ts +24 -0
  115. package/dist/extras/extensions/mem-session/extension.d.ts.map +1 -0
  116. package/dist/extras/extensions/mem-session/extension.js +49 -0
  117. package/dist/extras/extensions/mem-session/extension.js.map +1 -0
  118. package/dist/extras/extensions/mem-session/extension.json +6 -0
  119. package/dist/extras/extensions/mem-session/index.d.ts +2 -0
  120. package/dist/extras/extensions/mem-session/index.d.ts.map +1 -0
  121. package/dist/extras/extensions/mem-session/index.js +2 -0
  122. package/dist/extras/extensions/mem-session/index.js.map +1 -0
  123. package/dist/extras/extensions/memory/extension.d.ts +54 -0
  124. package/dist/extras/extensions/memory/extension.d.ts.map +1 -0
  125. package/dist/extras/extensions/memory/extension.js +143 -0
  126. package/dist/extras/extensions/memory/extension.js.map +1 -0
  127. package/dist/extras/extensions/memory/extension.json +7 -0
  128. package/dist/extras/extensions/memory/index.d.ts +5 -0
  129. package/dist/extras/extensions/memory/index.d.ts.map +1 -0
  130. package/dist/extras/extensions/memory/index.js +3 -0
  131. package/dist/extras/extensions/memory/index.js.map +1 -0
  132. package/dist/extras/extensions/memory/llm-extractor.d.ts +42 -0
  133. package/dist/extras/extensions/memory/llm-extractor.d.ts.map +1 -0
  134. package/dist/extras/extensions/memory/llm-extractor.js +89 -0
  135. package/dist/extras/extensions/memory/llm-extractor.js.map +1 -0
  136. package/dist/extras/extensions/memory/types.d.ts +142 -0
  137. package/dist/extras/extensions/memory/types.d.ts.map +1 -0
  138. package/dist/extras/extensions/memory/types.js +2 -0
  139. package/dist/extras/extensions/memory/types.js.map +1 -0
  140. package/dist/extras/extensions/otel/extension.d.ts +57 -0
  141. package/dist/extras/extensions/otel/extension.d.ts.map +1 -0
  142. package/dist/extras/extensions/otel/extension.js +253 -0
  143. package/dist/extras/extensions/otel/extension.js.map +1 -0
  144. package/dist/extras/extensions/otel/extension.json +7 -0
  145. package/dist/extras/extensions/otel/extension.test.d.ts +2 -0
  146. package/dist/extras/extensions/otel/extension.test.d.ts.map +1 -0
  147. package/dist/extras/extensions/otel/extension.test.js +399 -0
  148. package/dist/extras/extensions/otel/extension.test.js.map +1 -0
  149. package/dist/extras/extensions/otel/index.d.ts +4 -0
  150. package/dist/extras/extensions/otel/index.d.ts.map +1 -0
  151. package/dist/extras/extensions/otel/index.js +3 -0
  152. package/dist/extras/extensions/otel/index.js.map +1 -0
  153. package/dist/extras/extensions/otel/jsonl-span-exporter.d.ts +60 -0
  154. package/dist/extras/extensions/otel/jsonl-span-exporter.d.ts.map +1 -0
  155. package/dist/extras/extensions/otel/jsonl-span-exporter.js +134 -0
  156. package/dist/extras/extensions/otel/jsonl-span-exporter.js.map +1 -0
  157. package/dist/extras/extensions/scripts/extension.d.ts +180 -0
  158. package/dist/extras/extensions/scripts/extension.d.ts.map +1 -0
  159. package/dist/extras/extensions/scripts/extension.js +618 -0
  160. package/dist/extras/extensions/scripts/extension.js.map +1 -0
  161. package/dist/extras/extensions/scripts/extension.json +7 -0
  162. package/dist/extras/extensions/scripts/extension.test.d.ts +2 -0
  163. package/dist/extras/extensions/scripts/extension.test.d.ts.map +1 -0
  164. package/dist/extras/extensions/scripts/extension.test.js +552 -0
  165. package/dist/extras/extensions/scripts/extension.test.js.map +1 -0
  166. package/dist/extras/extensions/scripts/index.d.ts +3 -0
  167. package/dist/extras/extensions/scripts/index.d.ts.map +1 -0
  168. package/dist/extras/extensions/scripts/index.js +2 -0
  169. package/dist/extras/extensions/scripts/index.js.map +1 -0
  170. package/dist/extras/extensions/skills/extension.d.ts +105 -0
  171. package/dist/extras/extensions/skills/extension.d.ts.map +1 -0
  172. package/dist/extras/extensions/skills/extension.js +419 -0
  173. package/dist/extras/extensions/skills/extension.js.map +1 -0
  174. package/dist/extras/extensions/skills/extension.json +7 -0
  175. package/dist/extras/extensions/skills/extension.test.d.ts +2 -0
  176. package/dist/extras/extensions/skills/extension.test.d.ts.map +1 -0
  177. package/dist/extras/extensions/skills/extension.test.js +184 -0
  178. package/dist/extras/extensions/skills/extension.test.js.map +1 -0
  179. package/dist/extras/extensions/skills/index.d.ts +4 -0
  180. package/dist/extras/extensions/skills/index.d.ts.map +1 -0
  181. package/dist/extras/extensions/skills/index.js +3 -0
  182. package/dist/extras/extensions/skills/index.js.map +1 -0
  183. package/dist/extras/extensions/streaming/extension.d.ts +15 -0
  184. package/dist/extras/extensions/streaming/extension.d.ts.map +1 -0
  185. package/dist/extras/extensions/streaming/extension.js +41 -0
  186. package/dist/extras/extensions/streaming/extension.js.map +1 -0
  187. package/dist/extras/extensions/streaming/extension.json +6 -0
  188. package/dist/extras/extensions/streaming/index.d.ts +2 -0
  189. package/dist/extras/extensions/streaming/index.d.ts.map +1 -0
  190. package/dist/extras/extensions/streaming/index.js +2 -0
  191. package/dist/extras/extensions/streaming/index.js.map +1 -0
  192. package/dist/extras/extensions/structured-output/extension.d.ts +11 -0
  193. package/dist/extras/extensions/structured-output/extension.d.ts.map +1 -0
  194. package/dist/extras/extensions/structured-output/extension.js +102 -0
  195. package/dist/extras/extensions/structured-output/extension.js.map +1 -0
  196. package/dist/extras/extensions/structured-output/extension.json +7 -0
  197. package/dist/extras/extensions/structured-output/index.d.ts +3 -0
  198. package/dist/extras/extensions/structured-output/index.d.ts.map +1 -0
  199. package/dist/extras/extensions/structured-output/index.js +2 -0
  200. package/dist/extras/extensions/structured-output/index.js.map +1 -0
  201. package/dist/extras/extensions/timestamp/extension.d.ts +11 -0
  202. package/dist/extras/extensions/timestamp/extension.d.ts.map +1 -0
  203. package/dist/extras/extensions/timestamp/extension.js +21 -0
  204. package/dist/extras/extensions/timestamp/extension.js.map +1 -0
  205. package/dist/extras/extensions/timestamp/extension.json +6 -0
  206. package/dist/extras/extensions/timestamp/index.d.ts +2 -0
  207. package/dist/extras/extensions/timestamp/index.d.ts.map +1 -0
  208. package/dist/extras/extensions/timestamp/index.js +2 -0
  209. package/dist/extras/extensions/timestamp/index.js.map +1 -0
  210. package/dist/extras/extensions/token-stats/extension.d.ts +50 -0
  211. package/dist/extras/extensions/token-stats/extension.d.ts.map +1 -0
  212. package/dist/extras/extensions/token-stats/extension.js +99 -0
  213. package/dist/extras/extensions/token-stats/extension.js.map +1 -0
  214. package/dist/extras/extensions/token-stats/extension.json +7 -0
  215. package/dist/extras/extensions/token-stats/extension.test.d.ts +2 -0
  216. package/dist/extras/extensions/token-stats/extension.test.d.ts.map +1 -0
  217. package/dist/extras/extensions/token-stats/extension.test.js +197 -0
  218. package/dist/extras/extensions/token-stats/extension.test.js.map +1 -0
  219. package/dist/extras/extensions/token-stats/index.d.ts +3 -0
  220. package/dist/extras/extensions/token-stats/index.d.ts.map +1 -0
  221. package/dist/extras/extensions/token-stats/index.js +2 -0
  222. package/dist/extras/extensions/token-stats/index.js.map +1 -0
  223. package/dist/extras/extensions/tool-buffer/extension.d.ts +89 -0
  224. package/dist/extras/extensions/tool-buffer/extension.d.ts.map +1 -0
  225. package/dist/extras/extensions/tool-buffer/extension.js +182 -0
  226. package/dist/extras/extensions/tool-buffer/extension.js.map +1 -0
  227. package/dist/extras/extensions/tool-buffer/extension.json +7 -0
  228. package/dist/extras/extensions/tool-buffer/extension.test.d.ts +2 -0
  229. package/dist/extras/extensions/tool-buffer/extension.test.d.ts.map +1 -0
  230. package/dist/extras/extensions/tool-buffer/extension.test.js +549 -0
  231. package/dist/extras/extensions/tool-buffer/extension.test.js.map +1 -0
  232. package/dist/extras/extensions/tool-buffer/index.d.ts +3 -0
  233. package/dist/extras/extensions/tool-buffer/index.d.ts.map +1 -0
  234. package/dist/extras/extensions/tool-buffer/index.js +2 -0
  235. package/dist/extras/extensions/tool-buffer/index.js.map +1 -0
  236. package/dist/extras/extensions/weather-tool/extension.d.ts +22 -0
  237. package/dist/extras/extensions/weather-tool/extension.d.ts.map +1 -0
  238. package/dist/extras/extensions/weather-tool/extension.js +33 -0
  239. package/dist/extras/extensions/weather-tool/extension.js.map +1 -0
  240. package/dist/extras/extensions/weather-tool/extension.json +7 -0
  241. package/dist/extras/extensions/weather-tool/index.d.ts +3 -0
  242. package/dist/extras/extensions/weather-tool/index.d.ts.map +1 -0
  243. package/dist/extras/extensions/weather-tool/index.js +2 -0
  244. package/dist/extras/extensions/weather-tool/index.js.map +1 -0
  245. package/dist/extras/models/ai-sdk-resolver.d.ts +15 -0
  246. package/dist/extras/models/ai-sdk-resolver.d.ts.map +1 -0
  247. package/dist/extras/models/ai-sdk-resolver.js +57 -0
  248. package/dist/extras/models/ai-sdk-resolver.js.map +1 -0
  249. package/dist/extras/models/index.d.ts +5 -0
  250. package/dist/extras/models/index.d.ts.map +1 -0
  251. package/dist/extras/models/index.js +3 -0
  252. package/dist/extras/models/index.js.map +1 -0
  253. package/dist/extras/models/routing-model.d.ts +18 -0
  254. package/dist/extras/models/routing-model.d.ts.map +1 -0
  255. package/dist/extras/models/routing-model.js +24 -0
  256. package/dist/extras/models/routing-model.js.map +1 -0
  257. package/dist/extras/openai-provider/index.d.ts +3 -0
  258. package/dist/extras/openai-provider/index.d.ts.map +1 -0
  259. package/dist/extras/openai-provider/index.js +2 -0
  260. package/dist/extras/openai-provider/index.js.map +1 -0
  261. package/dist/extras/openai-provider/server.d.ts +34 -0
  262. package/dist/extras/openai-provider/server.d.ts.map +1 -0
  263. package/dist/extras/openai-provider/server.js +335 -0
  264. package/dist/extras/openai-provider/server.js.map +1 -0
  265. package/dist/handle.d.ts +56 -0
  266. package/dist/handle.d.ts.map +1 -0
  267. package/dist/handle.js +2 -0
  268. package/dist/handle.js.map +1 -0
  269. package/dist/hooks.d.ts +98 -0
  270. package/dist/hooks.d.ts.map +1 -0
  271. package/dist/hooks.js +53 -0
  272. package/dist/hooks.js.map +1 -0
  273. package/dist/index.d.ts +16 -0
  274. package/dist/index.d.ts.map +1 -0
  275. package/dist/index.js +9 -0
  276. package/dist/index.js.map +1 -0
  277. package/dist/model.d.ts +75 -0
  278. package/dist/model.d.ts.map +1 -0
  279. package/dist/model.js +2 -0
  280. package/dist/model.js.map +1 -0
  281. package/dist/tool.d.ts +78 -0
  282. package/dist/tool.d.ts.map +1 -0
  283. package/dist/tool.js +78 -0
  284. package/dist/tool.js.map +1 -0
  285. package/dist/types.d.ts +83 -0
  286. package/dist/types.d.ts.map +1 -0
  287. package/dist/types.js +21 -0
  288. package/dist/types.js.map +1 -0
  289. 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,7 @@
1
+ {
2
+ "name": "auto-steer",
3
+ "description": "Steers the agent to wrap up when approaching max iterations, preventing empty max_iterations responses.",
4
+ "enabled": true,
5
+ "priority": 90,
6
+ "configKeys": ["maxIterations", "threshold", "steerMessage"]
7
+ }
@@ -0,0 +1,3 @@
1
+ export { default as createAutoSteerExtension } from './extension.js';
2
+ export type { AutoSteerConfig } from './extension.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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,2 @@
1
+ export { default as createAutoSteerExtension } from './extension.js';
2
+ //# sourceMappingURL=index.js.map
@@ -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"}