@moikapy/lich 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/README.md +26 -3
  3. package/dist/{chunk-JC2G3XH2.js → chunk-JYURFAGB.js} +2 -2
  4. package/dist/{chunk-7HLVKVIG.js → chunk-QVJCIZIF.js} +732 -10
  5. package/dist/chunk-QVJCIZIF.js.map +1 -0
  6. package/dist/chunk-SAEB3QL3.js +58 -0
  7. package/dist/chunk-SAEB3QL3.js.map +1 -0
  8. package/dist/cli.js +243 -17
  9. package/dist/cli.js.map +1 -1
  10. package/dist/{gateway-RJFZJEUZ.js → gateway-5BG3YCZF.js} +2 -2
  11. package/dist/index.d.ts +260 -154
  12. package/dist/index.js +5 -3
  13. package/dist/{tui-LOUJVZ6A.js → tui-L6RABP2J.js} +4 -4
  14. package/docs/.vitepress/config.mts +4 -0
  15. package/docs/architecture/overview.md +31 -18
  16. package/docs/architecture/plugins.md +1 -1
  17. package/docs/architecture/tools.md +7 -2
  18. package/docs/getting-started.md +7 -6
  19. package/docs/index.md +5 -4
  20. package/docs/user-guide/cli.md +20 -8
  21. package/docs/user-guide/games.md +1 -1
  22. package/docs/user-guide/godot.md +3 -1
  23. package/docs/user-guide/library.md +4 -2
  24. package/docs/user-guide/plugins.md +2 -2
  25. package/docs/user-guide/redot.md +93 -0
  26. package/docs/user-guide/tui.md +2 -2
  27. package/examples/game_bridge/README.md +2 -0
  28. package/optional-mcps/godot/manifest.json +6 -0
  29. package/optional-mcps/redot/manifest.json +18 -0
  30. package/package.json +2 -1
  31. package/dist/chunk-6M6OAQGN.js +0 -17
  32. package/dist/chunk-6M6OAQGN.js.map +0 -1
  33. package/dist/chunk-7HLVKVIG.js.map +0 -1
  34. /package/dist/{chunk-JC2G3XH2.js.map → chunk-JYURFAGB.js.map} +0 -0
  35. /package/dist/{gateway-RJFZJEUZ.js.map → gateway-5BG3YCZF.js.map} +0 -0
  36. /package/dist/{tui-LOUJVZ6A.js.map → tui-L6RABP2J.js.map} +0 -0
package/dist/index.d.ts CHANGED
@@ -23,107 +23,6 @@ interface JsonSchemaObject {
23
23
  additionalProperties?: boolean;
24
24
  }
25
25
 
26
- interface ToolResult {
27
- ok: boolean;
28
- output: string;
29
- error?: string;
30
- }
31
- interface ToolContext {
32
- work_dir: string;
33
- env: Record<string, string>;
34
- signal?: AbortSignal;
35
- }
36
- interface Tool {
37
- name: string;
38
- description: string;
39
- parameters: JsonSchemaObject;
40
- /** Per-tool executor timeout override in ms; unset tools get the 30s default. */
41
- timeout_ms?: number;
42
- execute(args: Record<string, unknown>, context: ToolContext): Promise<ToolResult>;
43
- }
44
- interface Toolset {
45
- name: string;
46
- tools: Tool[];
47
- }
48
-
49
- /**
50
- * Plugin surface: user-authored tools and lifecycle hooks loaded at startup.
51
- *
52
- * A plugin is a plain object exported from a TS/JS module. Hooks observe and
53
- * (in the case of before_tool_call) veto tool executions; tools merge into the
54
- * agent registry after builtin filtering.
55
- */
56
-
57
- /** Runtime info handed to every hook call. */
58
- interface HookContext {
59
- work_dir: string;
60
- /**
61
- * This plugin's own per-run state sub-map. Every hook invocation receives
62
- * a ctx exposing only the invoking plugin's bag; the bag is swapped fresh
63
- * at each run start. Absent only on hand-built contexts outside the runner.
64
- */
65
- state?: Map<string, unknown>;
66
- }
67
- /** Argument passed to before_tool_call hooks. */
68
- interface BeforeToolCallInfo {
69
- tool_name: string;
70
- args: Record<string, unknown>;
71
- }
72
- /** Return value that vetoes a tool call; other hooks still run. */
73
- interface BeforeToolCallResult {
74
- block?: boolean;
75
- reason?: string;
76
- }
77
- /** Argument passed to after_tool_call hooks. */
78
- interface AfterToolCallInfo extends BeforeToolCallInfo {
79
- result_summary: string;
80
- /** Structured executor outcome; gate on this, never parse result_summary. */
81
- ok: boolean;
82
- error?: string;
83
- }
84
- interface RunEndInfo {
85
- stopped_reason: string;
86
- turns_used: number;
87
- }
88
- /** Optional lifecycle hooks a plugin may implement. All hooks are awaited. */
89
- interface PluginHooks {
90
- before_tool_call?(info: BeforeToolCallInfo, ctx: HookContext): Promise<BeforeToolCallResult | void> | BeforeToolCallResult | void;
91
- after_tool_call?(info: AfterToolCallInfo, ctx: HookContext): Promise<void> | void;
92
- on_run_start?(info: {
93
- input_chars: number;
94
- }, ctx: HookContext): Promise<void> | void;
95
- on_run_end?(info: RunEndInfo, ctx: HookContext): Promise<void> | void;
96
- }
97
- /** A user plugin: required unique name, optional tools and hooks. */
98
- interface Plugin {
99
- name: string;
100
- version?: string;
101
- tools?: Tool[];
102
- hooks?: PluginHooks;
103
- }
104
-
105
- /** A successfully loaded plugin plus the entry path it came from. */
106
- interface LoadedPlugin {
107
- plugin: Plugin;
108
- entry: string;
109
- }
110
- /** One failed entry: the specifier and why it failed. */
111
- interface PluginLoadError {
112
- entry: string;
113
- error_message: string;
114
- }
115
- /**
116
- * Load plugins from explicit entry paths (relative to `base_dir` or absolute).
117
- * Broken imports, missing exports, and duplicate names become error entries;
118
- * nothing is thrown for a bad plugin.
119
- */
120
- declare function load_plugins(entries: readonly string[], base_dir: string): Promise<{
121
- plugins: LoadedPlugin[];
122
- errors: PluginLoadError[];
123
- }>;
124
- /** Joined one-liner per load error, for a single warn log. */
125
- declare function plugin_errors_summary(errors: readonly PluginLoadError[]): string;
126
-
127
26
  type FinishReason = "stop" | "tool_calls" | "length" | "error" | "unknown";
128
27
  type ProviderErrorKind = "rate_limit" | "network" | "auth" | "overflow" | "bad_request" | "unknown";
129
28
  interface ToolCall {
@@ -270,10 +169,52 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
270
169
  }>>;
271
170
  log_level: z.ZodDefault<z.ZodEnum<["debug", "info", "warn", "error"]>>;
272
171
  theme: z.ZodDefault<z.ZodString>;
172
+ /** Named MCP servers. Each entry is stdio or loopback http. Default off. */
173
+ mcp_servers: z.ZodOptional<z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodObject<{
174
+ enabled: z.ZodDefault<z.ZodBoolean>;
175
+ command: z.ZodString;
176
+ args: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
177
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
178
+ }, "strict", z.ZodTypeAny, {
179
+ enabled: boolean;
180
+ command: string;
181
+ args: string[];
182
+ env?: Record<string, string> | undefined;
183
+ }, {
184
+ command: string;
185
+ enabled?: boolean | undefined;
186
+ args?: string[] | undefined;
187
+ env?: Record<string, string> | undefined;
188
+ }>, z.ZodObject<{
189
+ enabled: z.ZodDefault<z.ZodBoolean>;
190
+ url: z.ZodString;
191
+ }, "strict", z.ZodTypeAny, {
192
+ url: string;
193
+ enabled: boolean;
194
+ }, {
195
+ url: string;
196
+ enabled?: boolean | undefined;
197
+ }>]>>, Record<string, {
198
+ enabled: boolean;
199
+ command: string;
200
+ args: string[];
201
+ env?: Record<string, string> | undefined;
202
+ } | {
203
+ url: string;
204
+ enabled: boolean;
205
+ }>, Record<string, {
206
+ command: string;
207
+ enabled?: boolean | undefined;
208
+ args?: string[] | undefined;
209
+ env?: Record<string, string> | undefined;
210
+ } | {
211
+ url: string;
212
+ enabled?: boolean | undefined;
213
+ }>>>;
273
214
  }, "strip", z.ZodTypeAny, {
274
- plugins: string[];
275
- agent_name: string;
276
215
  max_turns: number;
216
+ log_level: "debug" | "info" | "warn" | "error";
217
+ theme: string;
277
218
  providers: z.objectOutputType<{
278
219
  kind: z.ZodEnum<["openai_compat", "anthropic", "ollama"]>;
279
220
  name: z.ZodString;
@@ -285,21 +226,30 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
285
226
  /** Injectable fetch, mainly for tests; passes through untouched. */
286
227
  fetch_fn: z.ZodOptional<z.ZodType<typeof fetch, z.ZodTypeDef, typeof fetch>>;
287
228
  }, z.ZodTypeAny, "passthrough">[];
229
+ agent_name: string;
288
230
  tools_enabled: string[] | "all";
289
231
  context_budget_tokens: number;
290
232
  compress_threshold: number;
291
233
  terminal_timeout_ms: number;
292
- log_level: "debug" | "info" | "warn" | "error";
293
- theme: string;
294
- temperature?: number | undefined;
295
- max_tokens?: number | undefined;
296
- system_prompt?: string | undefined;
234
+ plugins: string[];
297
235
  work_dir?: string | undefined;
236
+ system_prompt?: string | undefined;
298
237
  session_dir?: string | undefined;
299
238
  gateway?: {
300
239
  platforms: ("webhook" | "telegram" | "discord" | "twitch")[];
301
240
  token_envs: Record<string, string>;
302
241
  } | undefined;
242
+ temperature?: number | undefined;
243
+ max_tokens?: number | undefined;
244
+ mcp_servers?: Record<string, {
245
+ enabled: boolean;
246
+ command: string;
247
+ args: string[];
248
+ env?: Record<string, string> | undefined;
249
+ } | {
250
+ url: string;
251
+ enabled: boolean;
252
+ }> | undefined;
303
253
  }, {
304
254
  providers: z.objectInputType<{
305
255
  kind: z.ZodEnum<["openai_compat", "anthropic", "ollama"]>;
@@ -312,44 +262,62 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
312
262
  /** Injectable fetch, mainly for tests; passes through untouched. */
313
263
  fetch_fn: z.ZodOptional<z.ZodType<typeof fetch, z.ZodTypeDef, typeof fetch>>;
314
264
  }, z.ZodTypeAny, "passthrough">[];
315
- plugins?: string[] | undefined;
316
- temperature?: number | undefined;
317
- max_tokens?: number | undefined;
318
- agent_name?: string | undefined;
319
- system_prompt?: string | undefined;
320
- max_turns?: number | undefined;
321
265
  work_dir?: string | undefined;
322
- tools_enabled?: string[] | "all" | undefined;
323
- context_budget_tokens?: number | undefined;
324
- compress_threshold?: number | undefined;
266
+ max_turns?: number | undefined;
267
+ system_prompt?: string | undefined;
325
268
  session_dir?: string | undefined;
326
- terminal_timeout_ms?: number | undefined;
269
+ log_level?: "debug" | "info" | "warn" | "error" | undefined;
270
+ theme?: string | undefined;
327
271
  gateway?: {
328
272
  platforms?: ("webhook" | "telegram" | "discord" | "twitch")[] | undefined;
329
273
  token_envs?: Record<string, string> | undefined;
330
274
  } | undefined;
331
- log_level?: "debug" | "info" | "warn" | "error" | undefined;
332
- theme?: string | undefined;
275
+ agent_name?: string | undefined;
276
+ tools_enabled?: string[] | "all" | undefined;
277
+ temperature?: number | undefined;
278
+ max_tokens?: number | undefined;
279
+ context_budget_tokens?: number | undefined;
280
+ compress_threshold?: number | undefined;
281
+ terminal_timeout_ms?: number | undefined;
282
+ plugins?: string[] | undefined;
283
+ mcp_servers?: Record<string, {
284
+ command: string;
285
+ enabled?: boolean | undefined;
286
+ args?: string[] | undefined;
287
+ env?: Record<string, string> | undefined;
288
+ } | {
289
+ url: string;
290
+ enabled?: boolean | undefined;
291
+ }> | undefined;
333
292
  }>, {
334
293
  work_dir: string;
335
294
  providers: ProviderConfig[];
336
295
  session_dir: string;
337
- plugins: string[];
338
- agent_name: string;
339
296
  max_turns: number;
297
+ log_level: "debug" | "info" | "warn" | "error";
298
+ theme: string;
299
+ agent_name: string;
340
300
  tools_enabled: string[] | "all";
341
301
  context_budget_tokens: number;
342
302
  compress_threshold: number;
343
303
  terminal_timeout_ms: number;
344
- log_level: "debug" | "info" | "warn" | "error";
345
- theme: string;
346
- temperature?: number | undefined;
347
- max_tokens?: number | undefined;
304
+ plugins: string[];
348
305
  system_prompt?: string | undefined;
349
306
  gateway?: {
350
307
  platforms: ("webhook" | "telegram" | "discord" | "twitch")[];
351
308
  token_envs: Record<string, string>;
352
309
  } | undefined;
310
+ temperature?: number | undefined;
311
+ max_tokens?: number | undefined;
312
+ mcp_servers?: Record<string, {
313
+ enabled: boolean;
314
+ command: string;
315
+ args: string[];
316
+ env?: Record<string, string> | undefined;
317
+ } | {
318
+ url: string;
319
+ enabled: boolean;
320
+ }> | undefined;
353
321
  }, {
354
322
  providers: z.objectInputType<{
355
323
  kind: z.ZodEnum<["openai_compat", "anthropic", "ollama"]>;
@@ -362,28 +330,172 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
362
330
  /** Injectable fetch, mainly for tests; passes through untouched. */
363
331
  fetch_fn: z.ZodOptional<z.ZodType<typeof fetch, z.ZodTypeDef, typeof fetch>>;
364
332
  }, z.ZodTypeAny, "passthrough">[];
365
- plugins?: string[] | undefined;
366
- temperature?: number | undefined;
367
- max_tokens?: number | undefined;
368
- agent_name?: string | undefined;
369
- system_prompt?: string | undefined;
370
- max_turns?: number | undefined;
371
333
  work_dir?: string | undefined;
372
- tools_enabled?: string[] | "all" | undefined;
373
- context_budget_tokens?: number | undefined;
374
- compress_threshold?: number | undefined;
334
+ max_turns?: number | undefined;
335
+ system_prompt?: string | undefined;
375
336
  session_dir?: string | undefined;
376
- terminal_timeout_ms?: number | undefined;
337
+ log_level?: "debug" | "info" | "warn" | "error" | undefined;
338
+ theme?: string | undefined;
377
339
  gateway?: {
378
340
  platforms?: ("webhook" | "telegram" | "discord" | "twitch")[] | undefined;
379
341
  token_envs?: Record<string, string> | undefined;
380
342
  } | undefined;
381
- log_level?: "debug" | "info" | "warn" | "error" | undefined;
382
- theme?: string | undefined;
343
+ agent_name?: string | undefined;
344
+ tools_enabled?: string[] | "all" | undefined;
345
+ temperature?: number | undefined;
346
+ max_tokens?: number | undefined;
347
+ context_budget_tokens?: number | undefined;
348
+ compress_threshold?: number | undefined;
349
+ terminal_timeout_ms?: number | undefined;
350
+ plugins?: string[] | undefined;
351
+ mcp_servers?: Record<string, {
352
+ command: string;
353
+ enabled?: boolean | undefined;
354
+ args?: string[] | undefined;
355
+ env?: Record<string, string> | undefined;
356
+ } | {
357
+ url: string;
358
+ enabled?: boolean | undefined;
359
+ }> | undefined;
383
360
  }>;
384
361
  type AgentConfig = z.infer<typeof agent_config_schema>;
385
362
  declare function parse_agent_config(raw: unknown): AgentConfig;
386
363
 
364
+ interface ToolResult {
365
+ ok: boolean;
366
+ output: string;
367
+ error?: string;
368
+ }
369
+ interface ToolContext {
370
+ work_dir: string;
371
+ env: Record<string, string>;
372
+ signal?: AbortSignal;
373
+ }
374
+ interface Tool {
375
+ name: string;
376
+ description: string;
377
+ parameters: JsonSchemaObject;
378
+ /** Per-tool executor timeout override in ms; unset tools get the 30s default. */
379
+ timeout_ms?: number;
380
+ execute(args: Record<string, unknown>, context: ToolContext): Promise<ToolResult>;
381
+ }
382
+ interface Toolset {
383
+ name: string;
384
+ tools: Tool[];
385
+ }
386
+
387
+ /**
388
+ * Name-keyed registry of tools and toolsets. Registration rejects duplicate
389
+ * tool names so conflicting builtins fail loudly at startup.
390
+ */
391
+ declare class ToolRegistry {
392
+ private readonly tools;
393
+ register(tool: Tool): void;
394
+ register_toolset(toolset: Toolset): void;
395
+ get(name: string): Tool | undefined;
396
+ has(name: string): boolean;
397
+ list(): Tool[];
398
+ /** Map registered tools onto the provider-facing wire shape. */
399
+ definitions(): ToolDefinition[];
400
+ }
401
+
402
+ interface LineChild {
403
+ write_line(line: string): void;
404
+ read_line(): Promise<string | undefined>;
405
+ stop(): void;
406
+ failed(): string | undefined;
407
+ }
408
+ type LineSpawner = (command: string, args: readonly string[], env?: Record<string, string>) => LineChild;
409
+
410
+ /**
411
+ * Discover tools/list for each enabled server and register mcp_<server>_<tool>.
412
+ * tools_enabled filters them. An empty allowlist never connects.
413
+ */
414
+
415
+ interface McpRuntime {
416
+ spawn?: LineSpawner;
417
+ fetch_fn?: typeof fetch;
418
+ env_path?: string;
419
+ }
420
+
421
+ /**
422
+ * Plugin surface: user-authored tools and lifecycle hooks loaded at startup.
423
+ *
424
+ * A plugin is a plain object exported from a TS/JS module. Hooks observe and
425
+ * (in the case of before_tool_call) veto tool executions; tools merge into the
426
+ * agent registry after builtin filtering.
427
+ */
428
+
429
+ /** Runtime info handed to every hook call. */
430
+ interface HookContext {
431
+ work_dir: string;
432
+ /**
433
+ * This plugin's own per-run state sub-map. Every hook invocation receives
434
+ * a ctx exposing only the invoking plugin's bag; the bag is swapped fresh
435
+ * at each run start. Absent only on hand-built contexts outside the runner.
436
+ */
437
+ state?: Map<string, unknown>;
438
+ }
439
+ /** Argument passed to before_tool_call hooks. */
440
+ interface BeforeToolCallInfo {
441
+ tool_name: string;
442
+ args: Record<string, unknown>;
443
+ }
444
+ /** Return value that vetoes a tool call; other hooks still run. */
445
+ interface BeforeToolCallResult {
446
+ block?: boolean;
447
+ reason?: string;
448
+ }
449
+ /** Argument passed to after_tool_call hooks. */
450
+ interface AfterToolCallInfo extends BeforeToolCallInfo {
451
+ result_summary: string;
452
+ /** Structured executor outcome; gate on this, never parse result_summary. */
453
+ ok: boolean;
454
+ error?: string;
455
+ }
456
+ interface RunEndInfo {
457
+ stopped_reason: string;
458
+ turns_used: number;
459
+ }
460
+ /** Optional lifecycle hooks a plugin may implement. All hooks are awaited. */
461
+ interface PluginHooks {
462
+ before_tool_call?(info: BeforeToolCallInfo, ctx: HookContext): Promise<BeforeToolCallResult | void> | BeforeToolCallResult | void;
463
+ after_tool_call?(info: AfterToolCallInfo, ctx: HookContext): Promise<void> | void;
464
+ on_run_start?(info: {
465
+ input_chars: number;
466
+ }, ctx: HookContext): Promise<void> | void;
467
+ on_run_end?(info: RunEndInfo, ctx: HookContext): Promise<void> | void;
468
+ }
469
+ /** A user plugin: required unique name, optional tools and hooks. */
470
+ interface Plugin {
471
+ name: string;
472
+ version?: string;
473
+ tools?: Tool[];
474
+ hooks?: PluginHooks;
475
+ }
476
+
477
+ /** A successfully loaded plugin plus the entry path it came from. */
478
+ interface LoadedPlugin {
479
+ plugin: Plugin;
480
+ entry: string;
481
+ }
482
+ /** One failed entry: the specifier and why it failed. */
483
+ interface PluginLoadError {
484
+ entry: string;
485
+ error_message: string;
486
+ }
487
+ /**
488
+ * Load plugins from explicit entry paths (relative to `base_dir` or absolute).
489
+ * Broken imports, missing exports, and duplicate names become error entries;
490
+ * nothing is thrown for a bad plugin.
491
+ */
492
+ declare function load_plugins(entries: readonly string[], base_dir: string): Promise<{
493
+ plugins: LoadedPlugin[];
494
+ errors: PluginLoadError[];
495
+ }>;
496
+ /** Joined one-liner per load error, for a single warn log. */
497
+ declare function plugin_errors_summary(errors: readonly PluginLoadError[]): string;
498
+
387
499
  /**
388
500
  * Typed event emitter for the agent loop.
389
501
  *
@@ -492,8 +604,14 @@ declare class Agent {
492
604
  private readonly registry;
493
605
  private readonly executor;
494
606
  private readonly hook_runner;
495
- constructor(config: AgentConfig, plugins?: readonly LoadedPlugin[]);
607
+ private readonly mcp_runtime;
608
+ private mcp_attached;
609
+ constructor(config: AgentConfig, plugins?: readonly LoadedPlugin[], runtime?: {
610
+ mcp?: McpRuntime;
611
+ });
496
612
  run(options: AgentRunOptions): Promise<AgentRunResult>;
613
+ /** tools/list once, before the model sees definitions. Empty allowlists never connect. */
614
+ private attach_mcp_once;
497
615
  /** Per-run deps: the built-once ToolContext threads through every tool execution. */
498
616
  private loop_deps;
499
617
  /** Best-effort on_run_start fan-out; hook errors are logged, never fatal. */
@@ -511,20 +629,8 @@ declare function run_agent(raw_config: unknown, input: string, options?: {
511
629
  label?: string;
512
630
  }): Promise<AgentRunResult>;
513
631
 
514
- /**
515
- * Name-keyed registry of tools and toolsets. Registration rejects duplicate
516
- * tool names so conflicting builtins fail loudly at startup.
517
- */
518
- declare class ToolRegistry {
519
- private readonly tools;
520
- register(tool: Tool): void;
521
- register_toolset(toolset: Toolset): void;
522
- get(name: string): Tool | undefined;
523
- has(name: string): boolean;
524
- list(): Tool[];
525
- /** Map registered tools onto the provider-facing wire shape. */
526
- definitions(): ToolDefinition[];
527
- }
632
+ /** Write the client entry. The user sets enabled. Godot has nothing to write. */
633
+ declare function catalog_client_entry(name: string, substitutes?: Record<string, string>): Record<string, unknown> | string;
528
634
 
529
635
  interface ExecutorDefaults {
530
636
  work_dir?: string;
@@ -588,4 +694,4 @@ declare class HookedToolRunner {
588
694
 
589
695
  declare const LICH_VERSION: string;
590
696
 
591
- export { type AfterToolCallInfo, Agent, type AgentConfig, AgentEmitter, type AgentEvent, type AgentEventHandler, type AgentEvents, type AgentRunOptions, type AgentRunResult, type BeforeToolCallInfo, type BeforeToolCallResult, type ChatOptions, type ChatResult, type HookContext, HookedToolRunner, LICH_VERSION, type LoadedPlugin, type LoopOutcome, type LoopParams, type Message, type Plugin, type PluginHooks, type PluginLoadError, type ProviderConfig, ProviderError, type RunEndInfo, type Tool, type ToolCall, type ToolContext, type ToolDefinition, ToolExecutor, ToolRegistry, type ToolResult, type Usage, create_agent, create_agent_with_plugins, load_plugins, parse_agent_config, plugin_errors_summary, register_builtin_tools, run_agent };
697
+ export { type AfterToolCallInfo, Agent, type AgentConfig, AgentEmitter, type AgentEvent, type AgentEventHandler, type AgentEvents, type AgentRunOptions, type AgentRunResult, type BeforeToolCallInfo, type BeforeToolCallResult, type ChatOptions, type ChatResult, type HookContext, HookedToolRunner, LICH_VERSION, type LoadedPlugin, type LoopOutcome, type LoopParams, type Message, type Plugin, type PluginHooks, type PluginLoadError, type ProviderConfig, ProviderError, type RunEndInfo, type Tool, type ToolCall, type ToolContext, type ToolDefinition, ToolExecutor, ToolRegistry, type ToolResult, type Usage, catalog_client_entry, create_agent, create_agent_with_plugins, load_plugins, parse_agent_config, plugin_errors_summary, register_builtin_tools, run_agent };
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
- LICH_VERSION
3
- } from "./chunk-6M6OAQGN.js";
2
+ LICH_VERSION,
3
+ catalog_client_entry
4
+ } from "./chunk-SAEB3QL3.js";
4
5
  import {
5
6
  Agent,
6
7
  AgentEmitter,
@@ -15,7 +16,7 @@ import {
15
16
  plugin_errors_summary,
16
17
  register_builtin_tools,
17
18
  run_agent
18
- } from "./chunk-7HLVKVIG.js";
19
+ } from "./chunk-QVJCIZIF.js";
19
20
  export {
20
21
  Agent,
21
22
  AgentEmitter,
@@ -24,6 +25,7 @@ export {
24
25
  ProviderError,
25
26
  ToolExecutor,
26
27
  ToolRegistry,
28
+ catalog_client_entry,
27
29
  create_agent,
28
30
  create_agent_with_plugins,
29
31
  load_plugins,
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  fill_template,
3
3
  load_theme
4
- } from "./chunk-JC2G3XH2.js";
4
+ } from "./chunk-JYURFAGB.js";
5
5
  import {
6
6
  LICH_VERSION
7
- } from "./chunk-6M6OAQGN.js";
7
+ } from "./chunk-SAEB3QL3.js";
8
8
  import {
9
9
  create_agent_with_plugins,
10
10
  truncate_text
11
- } from "./chunk-7HLVKVIG.js";
11
+ } from "./chunk-QVJCIZIF.js";
12
12
 
13
13
  // src/tui.tsx
14
14
  import { render } from "ink";
@@ -435,4 +435,4 @@ async function run_tui(config) {
435
435
  export {
436
436
  run_tui
437
437
  };
438
- //# sourceMappingURL=tui-LOUJVZ6A.js.map
438
+ //# sourceMappingURL=tui-L6RABP2J.js.map
@@ -29,6 +29,9 @@ export default defineConfig({
29
29
  { text: 'TUI guide', link: '/user-guide/tui' },
30
30
  { text: 'Gateway guide', link: '/user-guide/gateway' },
31
31
  { text: 'Library guide', link: '/user-guide/library' },
32
+ { text: 'Plugins guide', link: '/user-guide/plugins' },
33
+ { text: 'Godot guide', link: '/user-guide/godot' },
34
+ { text: 'Redot guide', link: '/user-guide/redot' },
32
35
  { text: 'Games guide', link: '/user-guide/games' }
33
36
  ]
34
37
  },
@@ -39,6 +42,7 @@ export default defineConfig({
39
42
  { text: 'Agent loop', link: '/architecture/agent-loop' },
40
43
  { text: 'Providers', link: '/architecture/providers' },
41
44
  { text: 'Tools', link: '/architecture/tools' },
45
+ { text: 'Plugins', link: '/architecture/plugins' },
42
46
  { text: 'Extending', link: '/architecture/extending' }
43
47
  ]
44
48
  }
@@ -1,21 +1,24 @@
1
1
  # Architecture Overview
2
2
 
3
- Lich v0.3.0 is a small TypeScript AI agent harness: it drives a chat model in a
3
+ Lich is a small TypeScript AI agent harness: it drives a chat model in a
4
4
  think-act-observe loop, lets the model call tools, compresses history when the
5
- context budget demands it, and persists transcripts. It runs on Bun, is ESM
6
- with NodeNext resolution, and its only runtime dependencies are `zod` (config
7
- validation) and `ink` (the TUI). Everything else is Node/Bun built-ins.
5
+ context budget demands it, and persists transcripts. The published npm package
6
+ is 0.6.0. This tree also includes unreleased editor MCP (changelog 0.7.0);
7
+ `package.json` is still 0.6.0, so `lich --version` prints `0.6.0`. It runs on
8
+ Node >= 20 and on Bun, is ESM with NodeNext resolution, and its runtime
9
+ dependencies are `zod` (config validation), `ink`, and `react` (the TUI).
10
+ Everything else is Node/Bun built-ins.
8
11
 
9
12
  This page is the map. The follow-up pages go deep on each area:
10
13
  [agent loop](./agent-loop.md), [providers](./providers.md),
11
- [tools](./tools.md), and [extending](./extending.md).
14
+ [tools](./tools.md), [plugins](./plugins.md), and [extending](./extending.md).
12
15
 
13
16
  ## Layer diagram
14
17
 
15
18
  ```mermaid
16
19
  flowchart TB
17
20
  subgraph entry["Entry surfaces"]
18
- CLI["src/cli.ts<br/>one-shot and chat"]
21
+ CLI["src/cli.ts<br/>one-shot, chat, tui,<br/>gateway, mcp"]
19
22
  TUI["src/tui/app.tsx<br/>ink TUI"]
20
23
  GW["src/gateway/runner.ts<br/>webhook, telegram,<br/>discord, twitch"]
21
24
  LIB["src/index.ts<br/>library exports"]
@@ -94,36 +97,42 @@ Why this matters:
94
97
  Walkthrough of a single `Agent.run({ input })` call
95
98
  ([`src/agent/agent.ts`](../../src/agent/agent.ts)):
96
99
 
97
- 1. **Usage collector attached.** `run()` subscribes a `collect_usage` handler
100
+ 1. **MCP attach, once.** Before the first model call, enabled `mcp_servers`
101
+ are connected (`initialize`, `notifications/initialized`, then `tools/list`)
102
+ and registered as `mcp_<server>_<tool>`. An empty `tools_enabled` never
103
+ connects. Disabled servers are skipped. A connect failure logs a warning
104
+ and the run continues. This client is in this source only (0.7.0 unreleased).
105
+ 2. **Usage collector attached.** `run()` subscribes a `collect_usage` handler
98
106
  on `agent.events`; every `llm_end` event adds the call's token usage into a
99
107
  per-run `Usage` total. The subscription is removed in a `finally` block.
100
- 2. **Seed messages.** The caller's `history` (if any) is copied into a fresh
108
+ 3. **Seed messages.** The caller's `history` (if any) is copied into a fresh
101
109
  array and the new user message is appended. The caller's array is never
102
110
  mutated.
103
- 3. **Loop starts.** `run_conversation(deps, seed, params)` first applies the
111
+ 4. **Loop starts.** `run_conversation(deps, seed, params)` first applies the
104
112
  system prompt via `seed_system_prompt` (prepend, or replace an existing
105
113
  system message if its content differs) and then enters the turn loop
106
114
  described in [agent loop](./agent-loop.md).
107
- 4. **Each turn.** Abort check at the top of the turn, optional compression
115
+ 5. **Each turn.** Abort check at the top of the turn, optional compression
108
116
  check, then one LLM call through the router (`chat_with_failover`, which
109
117
  walks providers with bounded in-place retries). The assistant message is
110
118
  pushed onto the history.
111
- 5. **Tools.** If the assistant message carries `tool_calls`, each call runs
112
- through the `ToolExecutor` (30 s timeout, abort linking, output clamping)
113
- and a `tool` message is appended per call. The loop then starts the next
119
+ 6. **Tools.** If the assistant message carries `tool_calls`, each call runs
120
+ through the `ToolExecutor` (per-tool `timeout_ms`, else 30 s; abort linking,
121
+ output clamping) and a `tool` message is appended per call. `terminal` sets
122
+ 300000 ms; `run_tests` sets 600000. The loop then starts the next
114
123
  turn. A turn with no tool calls is the final turn.
115
- 6. **Outcome.** The loop returns a `LoopOutcome`: the full `messages` array,
124
+ 7. **Outcome.** The loop returns a `LoopOutcome`: the full `messages` array,
116
125
  the final assistant message (or the last one seen), the last `ChatResult`
117
126
  on a real final, `turns_used`, and a `stopped_reason` of `final`, `budget`,
118
127
  or `aborted`.
119
- 7. **Session persist.** `persist_session()` appends one `meta` record
128
+ 8. **Session persist.** `persist_session()` appends one `meta` record
120
129
  (`run_start`), then one `message` record per outcome message, then a
121
130
  `budget_exhausted` meta record if the budget stopped the run, then a
122
131
  `run_end` meta record (`stopped_reason`, `usage`) for every completed
123
132
  run, to a JSONL file under `session_dir` (default
124
133
  `<work_dir>/.lich/sessions`). Persistence is best-effort: failures are
125
134
  logged and the run still succeeds with `session_path: undefined`.
126
- 8. **Return.** `AgentRunResult` bundles the outcome, the full transcript
135
+ 9. **Return.** `AgentRunResult` bundles the outcome, the full transcript
127
136
  (prior history plus the new exchange), the collected `usage_total`, and the
128
137
  session path.
129
138
 
@@ -150,8 +159,11 @@ Walkthrough of a single `Agent.run({ input })` call
150
159
  | Path | Responsibility |
151
160
  | --- | --- |
152
161
  | `src/index.ts` | Public library surface; pure re-exports plus `LICH_VERSION`. |
153
- | `src/cli.ts` | Zero-dependency CLI: one-shot, `chat`, `tui`, `gateway`, `config`. |
154
- | `src/cli_config.ts` | Config file discovery, loading, flag overrides, template. |
162
+ | `src/cli.ts` | CLI: one-shot, `chat`, `tui`, `gateway`, `init`, `config`, `update`, `mcp`. |
163
+ | `src/cli_config.ts` | Config file discovery, loading, flag overrides, template, writer. |
164
+ | `src/cli_update.ts` | `lich update`: npm view, then `npm install -g` when newer. Git clones are told to `git pull`. |
165
+ | `src/cli_mcp.ts` | `lich mcp` list/add/enable/disable/remove against work-dir config. |
166
+ | `src/mcp/*` | MCP client: catalog, stdio/loopback HTTP, tool registration. |
155
167
  | `src/agent/agent.ts` | `Agent`: wires router, registry, executor; sessions; usage. |
156
168
  | `src/agent/loop.ts` | `run_conversation`: the think-act-observe loop. |
157
169
  | `src/agent/config.ts` | Zod config schema, defaults, derived `session_dir`, freeze. |
@@ -170,6 +182,7 @@ Walkthrough of a single `Agent.run({ input })` call
170
182
  | `src/tools/registry.ts` | Name-keyed tool registry; duplicate rejection. |
171
183
  | `src/tools/executor.ts` | Never-throw execution with timeout and abort. |
172
184
  | `src/tools/builtin/*` | Builtin tools, including `run_tests` (see [tools](./tools.md)). |
185
+ | `src/plugins/*` | Plugin loader and hooks. Gatekeeper registers `git_commit` in code. |
173
186
  | `src/gateway/bus.ts` | Conversation-keyed runner over one shared `Agent`. |
174
187
  | `src/gateway/runner.ts` | Adapter construction, signal handling, process lifetime. |
175
188
  | `src/gateway/{telegram,discord,twitch,webhook}.ts` | Platform adapters. |