@moikapy/lich 0.8.0 → 0.9.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 (35) hide show
  1. package/CHANGELOG.md +49 -1
  2. package/README.md +38 -19
  3. package/dist/{chunk-QOTECFCN.js → chunk-4N3S6ACI.js} +2 -2
  4. package/dist/{chunk-EDRUZF22.js → chunk-KOEZQUIX.js} +1207 -512
  5. package/dist/chunk-KOEZQUIX.js.map +1 -0
  6. package/dist/{chunk-W6JXZBUE.js → chunk-P2YEHNM4.js} +2 -2
  7. package/dist/chunk-P2YEHNM4.js.map +1 -0
  8. package/dist/{chunk-VKEOHUCB.js → chunk-TYRKVBWQ.js} +26 -8
  9. package/dist/chunk-TYRKVBWQ.js.map +1 -0
  10. package/dist/cli.js +82 -44
  11. package/dist/cli.js.map +1 -1
  12. package/dist/{gateway-WU5G4ODO.js → gateway-OSB7254F.js} +209 -47
  13. package/dist/gateway-OSB7254F.js.map +1 -0
  14. package/dist/index.d.ts +105 -60
  15. package/dist/index.js +12 -5
  16. package/dist/{store-COOLBAHB.js → store-YYLEB7JG.js} +2 -2
  17. package/dist/{tui-4BP3TI7J.js → tui-XXIU4W7K.js} +17 -5
  18. package/dist/tui-XXIU4W7K.js.map +1 -0
  19. package/docs/architecture/overview.md +2 -2
  20. package/docs/architecture/tools.md +3 -1
  21. package/docs/getting-started.md +6 -6
  22. package/docs/index.md +1 -1
  23. package/docs/user-guide/cli.md +9 -7
  24. package/docs/user-guide/games.md +2 -1
  25. package/docs/user-guide/library.md +1 -1
  26. package/docs/user-guide/redot.md +2 -2
  27. package/docs/user-guide/tui.md +2 -2
  28. package/package.json +1 -1
  29. package/dist/chunk-EDRUZF22.js.map +0 -1
  30. package/dist/chunk-VKEOHUCB.js.map +0 -1
  31. package/dist/chunk-W6JXZBUE.js.map +0 -1
  32. package/dist/gateway-WU5G4ODO.js.map +0 -1
  33. package/dist/tui-4BP3TI7J.js.map +0 -1
  34. /package/dist/{chunk-QOTECFCN.js.map → chunk-4N3S6ACI.js.map} +0 -0
  35. /package/dist/{store-COOLBAHB.js.map → store-YYLEB7JG.js.map} +0 -0
package/dist/index.d.ts CHANGED
@@ -42,6 +42,12 @@ interface AssistantMessage {
42
42
  role: "assistant";
43
43
  content: string;
44
44
  tool_calls?: ToolCall[];
45
+ /**
46
+ * Opaque provider content blocks preserved for round-trip (e.g. Anthropic
47
+ * thinking / redacted_thinking). When present, Anthropic replays these
48
+ * instead of reconstructing text + tool_use from content/tool_calls.
49
+ */
50
+ provider_content?: readonly Record<string, unknown>[];
45
51
  }
46
52
  interface ToolMessage {
47
53
  role: "tool";
@@ -83,10 +89,14 @@ interface ProviderConfig {
83
89
  api_key?: string;
84
90
  api_key_env?: string;
85
91
  timeout_ms?: number;
92
+ /** Anthropic-only: forward ChatOptions.temperature (default: omit). */
93
+ send_temperature?: boolean;
86
94
  /** Ollama-only: request thinking mode (adds think:true to /api/chat). */
87
95
  think?: boolean;
88
96
  /** Ollama-only: how long the model stays loaded (e.g. "10m"). */
89
97
  keep_alive?: string;
98
+ /** Ollama-only: context window size sent as options.num_ctx. */
99
+ num_ctx?: number;
90
100
  /** Injectable fetch, mainly for tests. Defaults to global fetch. */
91
101
  fetch_fn?: typeof fetch;
92
102
  }
@@ -115,7 +125,7 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
115
125
  agent_name: z.ZodDefault<z.ZodString>;
116
126
  system_prompt: z.ZodOptional<z.ZodString>;
117
127
  max_turns: z.ZodDefault<z.ZodNumber>;
118
- providers: z.ZodArray<z.ZodObject<{
128
+ providers: z.ZodEffects<z.ZodArray<z.ZodObject<{
119
129
  kind: z.ZodEnum<["openai_compat", "anthropic", "ollama"]>;
120
130
  name: z.ZodString;
121
131
  model: z.ZodString;
@@ -145,7 +155,27 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
145
155
  timeout_ms: z.ZodOptional<z.ZodNumber>;
146
156
  /** Injectable fetch, mainly for tests; passes through untouched. */
147
157
  fetch_fn: z.ZodOptional<z.ZodType<typeof fetch, z.ZodTypeDef, typeof fetch>>;
148
- }, z.ZodTypeAny, "passthrough">>, "many">;
158
+ }, z.ZodTypeAny, "passthrough">>, "many">, z.objectOutputType<{
159
+ kind: z.ZodEnum<["openai_compat", "anthropic", "ollama"]>;
160
+ name: z.ZodString;
161
+ model: z.ZodString;
162
+ base_url: z.ZodOptional<z.ZodString>;
163
+ api_key: z.ZodOptional<z.ZodString>;
164
+ api_key_env: z.ZodOptional<z.ZodString>;
165
+ timeout_ms: z.ZodOptional<z.ZodNumber>;
166
+ /** Injectable fetch, mainly for tests; passes through untouched. */
167
+ fetch_fn: z.ZodOptional<z.ZodType<typeof fetch, z.ZodTypeDef, typeof fetch>>;
168
+ }, z.ZodTypeAny, "passthrough">[], z.objectInputType<{
169
+ kind: z.ZodEnum<["openai_compat", "anthropic", "ollama"]>;
170
+ name: z.ZodString;
171
+ model: z.ZodString;
172
+ base_url: z.ZodOptional<z.ZodString>;
173
+ api_key: z.ZodOptional<z.ZodString>;
174
+ api_key_env: z.ZodOptional<z.ZodString>;
175
+ timeout_ms: z.ZodOptional<z.ZodNumber>;
176
+ /** Injectable fetch, mainly for tests; passes through untouched. */
177
+ fetch_fn: z.ZodOptional<z.ZodType<typeof fetch, z.ZodTypeDef, typeof fetch>>;
178
+ }, z.ZodTypeAny, "passthrough">[]>;
149
179
  work_dir: z.ZodOptional<z.ZodString>;
150
180
  tools_enabled: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<"all">, z.ZodArray<z.ZodString, "many">]>>;
151
181
  temperature: z.ZodOptional<z.ZodNumber>;
@@ -168,13 +198,13 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
168
198
  tools_enabled: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<"all">, z.ZodArray<z.ZodString, "many">]>>;
169
199
  }, "strip", z.ZodTypeAny, {
170
200
  tools_enabled: string[] | "all";
171
- platforms: ("webhook" | "telegram" | "discord" | "twitch")[];
201
+ platforms: ("telegram" | "discord" | "twitch" | "webhook")[];
172
202
  token_envs: Record<string, string>;
173
203
  allowed_users: Record<string, string[]>;
174
204
  allowed_chats: Record<string, string[]>;
175
205
  }, {
176
206
  tools_enabled?: string[] | "all" | undefined;
177
- platforms?: ("webhook" | "telegram" | "discord" | "twitch")[] | undefined;
207
+ platforms?: ("telegram" | "discord" | "twitch" | "webhook")[] | undefined;
178
208
  token_envs?: Record<string, string> | undefined;
179
209
  allowed_users?: Record<string, string[]> | undefined;
180
210
  allowed_chats?: Record<string, string[]> | undefined;
@@ -224,9 +254,9 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
224
254
  enabled?: boolean | undefined;
225
255
  }>>>;
226
256
  }, "strip", z.ZodTypeAny, {
257
+ plugins: string[];
258
+ agent_name: string;
227
259
  max_turns: number;
228
- log_level: "debug" | "info" | "warn" | "error";
229
- theme: string;
230
260
  providers: z.objectOutputType<{
231
261
  kind: z.ZodEnum<["openai_compat", "anthropic", "ollama"]>;
232
262
  name: z.ZodString;
@@ -238,24 +268,24 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
238
268
  /** Injectable fetch, mainly for tests; passes through untouched. */
239
269
  fetch_fn: z.ZodOptional<z.ZodType<typeof fetch, z.ZodTypeDef, typeof fetch>>;
240
270
  }, z.ZodTypeAny, "passthrough">[];
241
- agent_name: string;
242
271
  tools_enabled: string[] | "all";
243
272
  context_budget_tokens: number;
244
273
  compress_threshold: number;
245
274
  terminal_timeout_ms: number;
246
- plugins: string[];
247
- work_dir?: string | undefined;
275
+ log_level: "debug" | "info" | "warn" | "error";
276
+ theme: string;
248
277
  system_prompt?: string | undefined;
278
+ work_dir?: string | undefined;
279
+ temperature?: number | undefined;
280
+ max_tokens?: number | undefined;
249
281
  session_dir?: string | undefined;
250
282
  gateway?: {
251
283
  tools_enabled: string[] | "all";
252
- platforms: ("webhook" | "telegram" | "discord" | "twitch")[];
284
+ platforms: ("telegram" | "discord" | "twitch" | "webhook")[];
253
285
  token_envs: Record<string, string>;
254
286
  allowed_users: Record<string, string[]>;
255
287
  allowed_chats: Record<string, string[]>;
256
288
  } | undefined;
257
- temperature?: number | undefined;
258
- max_tokens?: number | undefined;
259
289
  mcp_servers?: Record<string, {
260
290
  enabled: boolean;
261
291
  command: string;
@@ -277,27 +307,27 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
277
307
  /** Injectable fetch, mainly for tests; passes through untouched. */
278
308
  fetch_fn: z.ZodOptional<z.ZodType<typeof fetch, z.ZodTypeDef, typeof fetch>>;
279
309
  }, z.ZodTypeAny, "passthrough">[];
280
- work_dir?: string | undefined;
281
- max_turns?: number | undefined;
310
+ plugins?: string[] | undefined;
311
+ agent_name?: string | undefined;
282
312
  system_prompt?: string | undefined;
313
+ max_turns?: number | undefined;
314
+ work_dir?: string | undefined;
315
+ tools_enabled?: string[] | "all" | undefined;
316
+ temperature?: number | undefined;
317
+ max_tokens?: number | undefined;
318
+ context_budget_tokens?: number | undefined;
319
+ compress_threshold?: number | undefined;
283
320
  session_dir?: string | undefined;
284
- log_level?: "debug" | "info" | "warn" | "error" | undefined;
285
- theme?: string | undefined;
321
+ terminal_timeout_ms?: number | undefined;
286
322
  gateway?: {
287
323
  tools_enabled?: string[] | "all" | undefined;
288
- platforms?: ("webhook" | "telegram" | "discord" | "twitch")[] | undefined;
324
+ platforms?: ("telegram" | "discord" | "twitch" | "webhook")[] | undefined;
289
325
  token_envs?: Record<string, string> | undefined;
290
326
  allowed_users?: Record<string, string[]> | undefined;
291
327
  allowed_chats?: Record<string, string[]> | undefined;
292
328
  } | undefined;
293
- agent_name?: string | undefined;
294
- tools_enabled?: string[] | "all" | undefined;
295
- temperature?: number | undefined;
296
- max_tokens?: number | undefined;
297
- context_budget_tokens?: number | undefined;
298
- compress_threshold?: number | undefined;
299
- terminal_timeout_ms?: number | undefined;
300
- plugins?: string[] | undefined;
329
+ log_level?: "debug" | "info" | "warn" | "error" | undefined;
330
+ theme?: string | undefined;
301
331
  mcp_servers?: Record<string, {
302
332
  command: string;
303
333
  enabled?: boolean | undefined;
@@ -311,25 +341,25 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
311
341
  work_dir: string;
312
342
  providers: ProviderConfig[];
313
343
  session_dir: string;
314
- max_turns: number;
315
- log_level: "debug" | "info" | "warn" | "error";
316
- theme: string;
344
+ plugins: string[];
317
345
  agent_name: string;
346
+ max_turns: number;
318
347
  tools_enabled: string[] | "all";
319
348
  context_budget_tokens: number;
320
349
  compress_threshold: number;
321
350
  terminal_timeout_ms: number;
322
- plugins: string[];
351
+ log_level: "debug" | "info" | "warn" | "error";
352
+ theme: string;
323
353
  system_prompt?: string | undefined;
354
+ temperature?: number | undefined;
355
+ max_tokens?: number | undefined;
324
356
  gateway?: {
325
357
  tools_enabled: string[] | "all";
326
- platforms: ("webhook" | "telegram" | "discord" | "twitch")[];
358
+ platforms: ("telegram" | "discord" | "twitch" | "webhook")[];
327
359
  token_envs: Record<string, string>;
328
360
  allowed_users: Record<string, string[]>;
329
361
  allowed_chats: Record<string, string[]>;
330
362
  } | undefined;
331
- temperature?: number | undefined;
332
- max_tokens?: number | undefined;
333
363
  mcp_servers?: Record<string, {
334
364
  enabled: boolean;
335
365
  command: string;
@@ -351,27 +381,27 @@ declare const agent_config_schema: z.ZodEffects<z.ZodObject<{
351
381
  /** Injectable fetch, mainly for tests; passes through untouched. */
352
382
  fetch_fn: z.ZodOptional<z.ZodType<typeof fetch, z.ZodTypeDef, typeof fetch>>;
353
383
  }, z.ZodTypeAny, "passthrough">[];
354
- work_dir?: string | undefined;
355
- max_turns?: number | undefined;
384
+ plugins?: string[] | undefined;
385
+ agent_name?: string | undefined;
356
386
  system_prompt?: string | undefined;
387
+ max_turns?: number | undefined;
388
+ work_dir?: string | undefined;
389
+ tools_enabled?: string[] | "all" | undefined;
390
+ temperature?: number | undefined;
391
+ max_tokens?: number | undefined;
392
+ context_budget_tokens?: number | undefined;
393
+ compress_threshold?: number | undefined;
357
394
  session_dir?: string | undefined;
358
- log_level?: "debug" | "info" | "warn" | "error" | undefined;
359
- theme?: string | undefined;
395
+ terminal_timeout_ms?: number | undefined;
360
396
  gateway?: {
361
397
  tools_enabled?: string[] | "all" | undefined;
362
- platforms?: ("webhook" | "telegram" | "discord" | "twitch")[] | undefined;
398
+ platforms?: ("telegram" | "discord" | "twitch" | "webhook")[] | undefined;
363
399
  token_envs?: Record<string, string> | undefined;
364
400
  allowed_users?: Record<string, string[]> | undefined;
365
401
  allowed_chats?: Record<string, string[]> | undefined;
366
402
  } | undefined;
367
- agent_name?: string | undefined;
368
- tools_enabled?: string[] | "all" | undefined;
369
- temperature?: number | undefined;
370
- max_tokens?: number | undefined;
371
- context_budget_tokens?: number | undefined;
372
- compress_threshold?: number | undefined;
373
- terminal_timeout_ms?: number | undefined;
374
- plugins?: string[] | undefined;
403
+ log_level?: "debug" | "info" | "warn" | "error" | undefined;
404
+ theme?: string | undefined;
375
405
  mcp_servers?: Record<string, {
376
406
  command: string;
377
407
  enabled?: boolean | undefined;
@@ -455,8 +485,9 @@ interface HookContext {
455
485
  work_dir: string;
456
486
  /**
457
487
  * This plugin's own per-run state sub-map. Every hook invocation receives
458
- * a ctx exposing only the invoking plugin's bag; the bag is swapped fresh
459
- * at each run start. Absent only on hand-built contexts outside the runner.
488
+ * a ctx exposing only the invoking plugin's bag; the bag is scoped per
489
+ * Agent.run via AsyncLocalStorage (and swapped fresh at each run start).
490
+ * Absent only on hand-built contexts outside the runner.
460
491
  */
461
492
  state?: Map<string, unknown>;
462
493
  }
@@ -531,6 +562,8 @@ interface SessionHandle {
531
562
  path: string;
532
563
  append(record: SessionRecord): Promise<void>;
533
564
  }
565
+ declare function open_session(dir: string, label?: string): Promise<SessionHandle>;
566
+ declare function read_session_messages(file_path: string): Promise<Message[]>;
534
567
 
535
568
  /**
536
569
  * Typed event emitter for the agent loop.
@@ -564,8 +597,10 @@ interface AgentEvents {
564
597
  compress_start: {
565
598
  estimated_tokens: number;
566
599
  };
600
+ /** Summarizer usage is optional so the recorder never treats it as an assistant turn. */
567
601
  compress_end: {
568
602
  summary_chars: number;
603
+ usage?: Usage;
569
604
  };
570
605
  turn_end: {
571
606
  turn: number;
@@ -594,6 +629,10 @@ declare class AgentEmitter {
594
629
  clear(): void;
595
630
  }
596
631
 
632
+ interface ChatFn {
633
+ (messages: readonly Message[], tools: readonly ToolDefinition[], options?: ChatOptions): Promise<ChatResult>;
634
+ }
635
+
597
636
  /**
598
637
  * The Think-Act-Observe agent loop.
599
638
  *
@@ -603,6 +642,17 @@ declare class AgentEmitter {
603
642
  * loop never imports provider routers or the tool executor directly.
604
643
  */
605
644
 
645
+ interface ToolRunner {
646
+ execute(name: string, args: Record<string, unknown>, context?: ToolContext): Promise<ToolResult>;
647
+ }
648
+ interface LoopDeps {
649
+ chat: ChatFn;
650
+ tools: ToolRunner;
651
+ definitions: () => ToolDefinition[];
652
+ emitter?: AgentEmitter;
653
+ /** Per-run ToolContext threaded to every tool execution (A6). */
654
+ tool_context?: ToolContext;
655
+ }
606
656
  interface LoopParams {
607
657
  system_prompt?: string;
608
658
  max_turns: number;
@@ -619,6 +669,7 @@ interface LoopOutcome {
619
669
  turns_used: number;
620
670
  stopped_reason: "final" | "budget" | "aborted";
621
671
  }
672
+ declare function run_conversation(deps: LoopDeps, messages: readonly Message[], params: LoopParams): Promise<LoopOutcome>;
622
673
 
623
674
  interface AgentRunOptions {
624
675
  input: string;
@@ -650,6 +701,7 @@ declare class Agent {
650
701
  mcp?: McpRuntime;
651
702
  });
652
703
  run(options: AgentRunOptions): Promise<AgentRunResult>;
704
+ private run_body;
653
705
  /** Close MCP sessions so stdio children do not keep the event loop alive. */
654
706
  close(): void;
655
707
  /** tools/list once, before the model sees definitions. Empty allowlists never connect. */
@@ -697,18 +749,6 @@ declare class ToolExecutor {
697
749
  /** Register every builtin tool onto a fresh registry (idempotent per registry). */
698
750
  declare function register_builtin_tools(registry: ToolRegistry, context?: ToolContext): void;
699
751
 
700
- /**
701
- * HookedToolRunner: wraps the ToolExecutor with plugin hooks.
702
- *
703
- * before_tool_call hooks run in registration order and may veto a call (first
704
- * blocker wins; the wrapped executor is never called). Hook errors are warned
705
- * and skipped, never fatal. after_tool_call hooks observe the result summary
706
- * plus the executor's structured ok/error fields. Every hook invocation
707
- * receives a ctx exposing only its own plugin's state sub-map: the bag is a
708
- * module-internal WeakMap keyed on the plugin object, swapped fresh at each
709
- * call_run_start and shared by the lifecycle and per-tool ctx build sites.
710
- */
711
-
712
752
  /** Structural ToolRunner shape accepted from the wrapped executor. */
713
753
  interface WrappedToolRunner {
714
754
  execute(name: string, args: Record<string, unknown>, context?: ToolContext): Promise<ToolResult>;
@@ -722,6 +762,11 @@ declare class HookedToolRunner {
722
762
  private readonly wrapped;
723
763
  private readonly hooked_plugins;
724
764
  constructor(wrapped: WrappedToolRunner, plugins: readonly Plugin[]);
765
+ /**
766
+ * Isolate plugin state for one Agent.run so concurrent runs on the same
767
+ * Agent cannot reset each other's bags (M-6).
768
+ */
769
+ run_scope<T>(fn: () => Promise<T>): Promise<T>;
725
770
  /** Run before hooks in order; the first {block: true} verdict wins. */
726
771
  private run_before_hooks;
727
772
  /** Fire-and-forget in spirit but awaited here so runs settle cleanly. */
@@ -737,4 +782,4 @@ declare class HookedToolRunner {
737
782
 
738
783
  declare const LICH_VERSION: string;
739
784
 
740
- 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 };
785
+ export { type AfterToolCallInfo, Agent, type AgentConfig, AgentEmitter, type AgentEvent, type AgentEventHandler, type AgentEvents, type AgentRunOptions, type AgentRunResult, type AssistantMessage, type BeforeToolCallInfo, type BeforeToolCallResult, type ChatOptions, type ChatResult, type HookContext, HookedToolRunner, type JsonSchemaObject, LICH_VERSION, type LoadedPlugin, type LoopDeps, 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 ToolRunner, type Usage, catalog_client_entry, create_agent, create_agent_with_plugins, load_plugins, open_session, parse_agent_config, plugin_errors_summary, read_session_messages, register_builtin_tools, run_agent, run_conversation };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  LICH_VERSION,
3
3
  catalog_client_entry
4
- } from "./chunk-W6JXZBUE.js";
4
+ } from "./chunk-P2YEHNM4.js";
5
5
  import {
6
6
  Agent,
7
7
  AgentEmitter,
@@ -15,9 +15,13 @@ import {
15
15
  parse_agent_config,
16
16
  plugin_errors_summary,
17
17
  register_builtin_tools,
18
- run_agent
19
- } from "./chunk-EDRUZF22.js";
20
- import "./chunk-VKEOHUCB.js";
18
+ run_agent,
19
+ run_conversation
20
+ } from "./chunk-KOEZQUIX.js";
21
+ import {
22
+ open_session,
23
+ read_session_messages
24
+ } from "./chunk-TYRKVBWQ.js";
21
25
  export {
22
26
  Agent,
23
27
  AgentEmitter,
@@ -30,9 +34,12 @@ export {
30
34
  create_agent,
31
35
  create_agent_with_plugins,
32
36
  load_plugins,
37
+ open_session,
33
38
  parse_agent_config,
34
39
  plugin_errors_summary,
40
+ read_session_messages,
35
41
  register_builtin_tools,
36
- run_agent
42
+ run_agent,
43
+ run_conversation
37
44
  };
38
45
  //# sourceMappingURL=index.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  open_session,
3
3
  read_session_messages
4
- } from "./chunk-VKEOHUCB.js";
4
+ } from "./chunk-TYRKVBWQ.js";
5
5
  export {
6
6
  open_session,
7
7
  read_session_messages
8
8
  };
9
- //# sourceMappingURL=store-COOLBAHB.js.map
9
+ //# sourceMappingURL=store-YYLEB7JG.js.map
@@ -1,22 +1,22 @@
1
1
  import {
2
2
  fill_template,
3
3
  load_theme
4
- } from "./chunk-QOTECFCN.js";
4
+ } from "./chunk-4N3S6ACI.js";
5
5
  import {
6
6
  LICH_VERSION
7
- } from "./chunk-W6JXZBUE.js";
7
+ } from "./chunk-P2YEHNM4.js";
8
8
  import {
9
9
  resolve_session_path
10
10
  } from "./chunk-PL6MKRKE.js";
11
11
  import {
12
12
  create_agent_with_plugins,
13
13
  logger
14
- } from "./chunk-EDRUZF22.js";
14
+ } from "./chunk-KOEZQUIX.js";
15
15
  import {
16
16
  open_session,
17
17
  read_session_messages,
18
18
  truncate_text
19
- } from "./chunk-VKEOHUCB.js";
19
+ } from "./chunk-TYRKVBWQ.js";
20
20
 
21
21
  // src/tui.tsx
22
22
  import { render } from "ink";
@@ -63,6 +63,18 @@ function apply_event(state, event) {
63
63
  total_tokens: state.usage.total_tokens + event.result.usage.total_tokens
64
64
  }
65
65
  };
66
+ case "compress_end":
67
+ if (event.usage === void 0) {
68
+ return state;
69
+ }
70
+ return {
71
+ ...state,
72
+ usage: {
73
+ prompt_tokens: state.usage.prompt_tokens + event.usage.prompt_tokens,
74
+ completion_tokens: state.usage.completion_tokens + event.usage.completion_tokens,
75
+ total_tokens: state.usage.total_tokens + event.usage.total_tokens
76
+ }
77
+ };
66
78
  case "tool_call_start":
67
79
  return { ...state, phase: "tool", active_tool: event.call };
68
80
  case "tool_call_end":
@@ -629,4 +641,4 @@ async function run_tui(config, options = {}) {
629
641
  export {
630
642
  run_tui
631
643
  };
632
- //# sourceMappingURL=tui-4BP3TI7J.js.map
644
+ //# sourceMappingURL=tui-XXIU4W7K.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tui.tsx","../src/tui/app.tsx","../src/tui/load_resume.ts","../src/tui/state.ts","../src/tui/message_view.tsx","../src/tui/status_bar.tsx","../src/tui/command_bar.tsx"],"sourcesContent":["/**\n * TUI entry: builds the agent from the parsed config and renders the ink app\n * until exit. The CLI dynamic-imports this module for `lich tui`.\n */\nimport { render } from \"ink\";\nimport { create_agent_with_plugins } from \"./agent/agent.js\";\nimport type { AgentConfig } from \"./agent/config.js\";\nimport type { Message } from \"./providers/types.js\";\nimport { open_session } from \"./session/store.js\";\nimport { TuiApp } from \"./tui/app.js\";\nimport { load_theme } from \"./util/theme.js\";\nimport { logger } from \"./util/log.js\";\n\nexport interface RunTuiOptions {\n readonly initial_history?: readonly Message[];\n readonly resumed_id?: string;\n}\n\nexport async function run_tui(config: AgentConfig, options: RunTuiOptions = {}): Promise<number> {\n const agent = await create_agent_with_plugins(config);\n try {\n const theme = load_theme(config.theme);\n const session = await open_session(config.session_dir, \"tui\").catch((error: unknown) => {\n logger.warn(\"session persistence failed; continuing without transcript\", error);\n return undefined;\n });\n const instance = render(\n <TuiApp\n agent={agent}\n theme={theme}\n session={session}\n initial_history={options.initial_history}\n resumed_id={options.resumed_id}\n />,\n );\n await instance.waitUntilExit();\n return 0;\n } finally {\n agent.close();\n }\n}\n","/**\n * Root ink component for the lich TUI: wires agent events into the UI state\n * machine, drives agent.run with history continuity, and lays out header,\n * transcript, status bar, and the command input row.\n */\nimport { useCallback, useEffect, useRef, useState, type Dispatch, type SetStateAction } from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { Agent, AgentRunResult } from \"../agent/agent.js\";\nimport type { AgentEvent } from \"../agent/events.js\";\nimport type { AgentConfig } from \"../agent/config.js\";\nimport type { Message } from \"../providers/types.js\";\nimport type { SessionHandle } from \"../session/store.js\";\nimport { LICH_VERSION } from \"../index.js\";\nimport type { ThemeSpec } from \"../util/lore.js\";\nimport { readdir, stat } from \"node:fs/promises\";\nimport { load_resume_view } from \"./load_resume.js\";\nimport {\n apply_event,\n apply_run_result,\n compress_notice_block,\n error_notice_block,\n help_block,\n HISTORY_CAP,\n INITIAL_UI_STATE,\n model_label_block,\n parse_command,\n tui_banner_text,\n resume_banner_count,\n resume_banner_line,\n resume_missing_args_block,\n run_notice_blocks,\n session_list_block,\n split_history_blocks,\n tool_result_block,\n unknown_command_block,\n usage_notice_block,\n type HistoryBlock,\n type ParsedInput,\n type SessionEntryInfo,\n type UiState,\n} from \"./state.js\";\nimport { MessageView } from \"./message_view.js\";\nimport { StatusBar } from \"./status_bar.js\";\nimport { CommandBar } from \"./command_bar.js\";\n\nconst SESSION_LIST_CAP = 10;\n\ntype SlashInput = Extract<ParsedInput, { kind: \"slash\" }>;\ntype AddBlocks = (added: readonly HistoryBlock[]) => void;\ntype SetUiState = Dispatch<SetStateAction<UiState>>;\ntype SetBlocks = Dispatch<SetStateAction<readonly HistoryBlock[]>>;\ntype SetHistory = (messages: readonly Message[]) => void;\n\ninterface AgentRunControls {\n readonly start_message_run: (text: string) => void;\n readonly set_history: SetHistory;\n}\n\n/** Map one agent event to optional transcript blocks (tool rows, notices). */\nfunction event_blocks(event: AgentEvent, theme: ThemeSpec): readonly HistoryBlock[] {\n if (event.type === \"tool_call_end\") {\n if (event.cancelled === true) {\n return [];\n }\n return [tool_result_block(event.call, event.result.ok === true, event.result.output)];\n }\n if (event.type === \"compress_end\") {\n return [compress_notice_block(event.summary_chars, theme)];\n }\n if (event.type === \"error\") {\n return [error_notice_block(event.error instanceof Error ? event.error.message : String(event.error))];\n }\n return [];\n}\n\n/** One agent turn: subscribe to events, run, unsubscribe in finally. */\nasync function run_agent_turn(\n agent: Agent,\n history: readonly Message[],\n input: string,\n on_event: (event: AgentEvent) => void,\n on_done: (result: AgentRunResult) => void,\n signal: AbortSignal,\n session: SessionHandle | undefined,\n): Promise<void> {\n const stop_listening = agent.events.on(on_event);\n try {\n const result = await agent.run({ input, history, signal, label: \"tui\", session });\n on_done(result);\n } finally {\n stop_listening();\n }\n}\n\n/** Async /sessions listing as a meta block (never throws). */\nasync function sessions_block(config: AgentConfig, theme: ThemeSpec): Promise<HistoryBlock> {\n try {\n const dir_entries = await readdir(config.session_dir, { withFileTypes: true });\n const entries: SessionEntryInfo[] = [];\n for (const entry of dir_entries) {\n if (entry.isFile() === false || entry.name.endsWith(\".jsonl\") === false) {\n continue;\n }\n const info = await stat(`${config.session_dir}/${entry.name}`);\n entries.push({ name: entry.name, size_bytes: info.size, mtime_ms: info.mtimeMs });\n }\n return session_list_block(entries, theme, SESSION_LIST_CAP);\n } catch {\n return { role: \"meta\", lines: [\"· no session files yet\"] };\n }\n}\n\nfunction run_error_text(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** Runs one message exchange; owns history continuity and abort wiring. */\nfunction use_agent_run(\n agent: Agent,\n theme: ThemeSpec,\n add_blocks: AddBlocks,\n set_state: SetUiState,\n set_blocks: SetBlocks,\n initial_history: readonly Message[] | undefined,\n session: SessionHandle | undefined,\n): AgentRunControls {\n const history_ref = useRef<readonly Message[]>(initial_history ?? []);\n const controller_ref = useRef<AbortController | undefined>(undefined);\n\n const set_history = useCallback<SetHistory>((messages: readonly Message[]): void => {\n history_ref.current = messages;\n }, []);\n\n const finish_run = useCallback((result: AgentRunResult): void => {\n history_ref.current = result.messages;\n set_state((current) => apply_run_result(current, result));\n add_blocks(run_notice_blocks(result, theme));\n }, [add_blocks, set_state, theme]);\n\n const start_message_run = useCallback(\n (text: string): void => {\n add_blocks([{ role: \"user\", lines: [`${theme.user_label} › ${text}`] }]);\n set_state((current) => ({ ...current, phase: \"thinking\", active_tool: undefined }));\n const controller = new AbortController();\n controller_ref.current = controller;\n const on_event = (event: AgentEvent): void => {\n set_state((current) => apply_event(current, event));\n set_blocks((current) => [...current, ...event_blocks(event, theme)].slice(-HISTORY_CAP));\n };\n void run_agent_turn(agent, history_ref.current, text, on_event, finish_run, controller.signal, session)\n .catch((error: unknown) => {\n add_blocks([error_notice_block(run_error_text(error))]);\n set_state((current) => ({ ...current, phase: \"idle\" }));\n })\n .finally(() => {\n if (controller_ref.current === controller) {\n controller_ref.current = undefined;\n }\n });\n },\n [agent, add_blocks, finish_run, session, set_blocks, set_state, theme],\n );\n\n useEffect(() => () => controller_ref.current?.abort(), []);\n\n return { start_message_run, set_history };\n}\n\n/** Slash-command dispatch: pure client-side actions, never hits the agent. */\nfunction use_slash_commands(\n agent: Agent,\n theme: ThemeSpec,\n add_blocks: AddBlocks,\n set_blocks: SetBlocks,\n set_history: SetHistory,\n set_resume_line: Dispatch<SetStateAction<string | undefined>>,\n begin_resume_load: () => void,\n end_resume_load: () => void,\n total_tokens: number,\n): (parsed: SlashInput) => void {\n const handle = useCallback(\n (parsed: SlashInput): void => {\n if (parsed.name === \"exit\" || parsed.name === \"quit\" || parsed.name === \"q\") {\n process.exit(0);\n return;\n }\n if (parsed.name === \"help\") {\n add_blocks([help_block()]);\n } else if (parsed.name === \"model\") {\n add_blocks([model_label_block(agent.config)]);\n } else if (parsed.name === \"usage\") {\n add_blocks([usage_notice_block(total_tokens)]);\n } else if (parsed.name === \"clear\") {\n set_blocks([]);\n } else if (parsed.name === \"sessions\") {\n void sessions_block(agent.config, theme).then((block) => add_blocks([block]));\n } else if (parsed.name === \"resume\") {\n if (parsed.args.length === 0) {\n add_blocks([resume_missing_args_block()]);\n return;\n }\n begin_resume_load();\n void load_resume_view(agent.config.session_dir, parsed.args, theme)\n .then((result) => {\n if (result.ok === false) {\n add_blocks([result.block]);\n return;\n }\n set_history(result.messages);\n set_blocks(result.blocks);\n set_resume_line(result.banner_line);\n })\n .finally(() => {\n end_resume_load();\n });\n } else {\n add_blocks([unknown_command_block(parsed.name)]);\n }\n },\n [\n agent,\n add_blocks,\n begin_resume_load,\n end_resume_load,\n set_blocks,\n set_history,\n set_resume_line,\n theme,\n total_tokens,\n ],\n );\n return handle;\n}\n\ninterface TuiAppProps {\n readonly agent: Agent;\n readonly theme: ThemeSpec;\n readonly session?: SessionHandle;\n readonly initial_history?: readonly Message[];\n readonly resumed_id?: string;\n}\n\nfunction initial_blocks(history: readonly Message[] | undefined, theme: ThemeSpec): readonly HistoryBlock[] {\n if (history === undefined || history.length === 0) {\n return [];\n }\n return split_history_blocks(history, HISTORY_CAP, theme);\n}\n\nfunction initial_resume_line(\n resumed_id: string | undefined,\n history: readonly Message[] | undefined,\n): string | undefined {\n if (resumed_id === undefined) {\n return undefined;\n }\n return resume_banner_line(resumed_id, resume_banner_count(history));\n}\n\nexport function TuiApp({ agent, theme, session, initial_history, resumed_id }: TuiAppProps): React.JSX.Element {\n const [blocks, set_blocks] = useState<readonly HistoryBlock[]>(() => initial_blocks(initial_history, theme));\n const [state, set_state] = useState(INITIAL_UI_STATE);\n const [resume_line, set_resume_line] = useState<string | undefined>(() =>\n initial_resume_line(resumed_id, initial_history),\n );\n const resume_loading_ref = useRef(false);\n const [resume_loading, set_resume_loading] = useState(false);\n\n const begin_resume_load = useCallback((): void => {\n resume_loading_ref.current = true;\n set_resume_loading(true);\n }, []);\n\n const end_resume_load = useCallback((): void => {\n resume_loading_ref.current = false;\n set_resume_loading(false);\n }, []);\n\n const add_blocks = useCallback<AddBlocks>((added: readonly HistoryBlock[]): void => {\n if (added.length === 0) {\n return;\n }\n set_blocks((current) => [...current, ...added].slice(-HISTORY_CAP));\n }, []);\n\n const { start_message_run, set_history } = use_agent_run(\n agent,\n theme,\n add_blocks,\n set_state,\n set_blocks,\n initial_history,\n session,\n );\n const handle_slash = use_slash_commands(\n agent,\n theme,\n add_blocks,\n set_blocks,\n set_history,\n set_resume_line,\n begin_resume_load,\n end_resume_load,\n state.usage.total_tokens,\n );\n\n const submit = useCallback(\n (text: string): void => {\n if (resume_loading_ref.current === true) {\n return;\n }\n const parsed = parse_command(text);\n if (parsed.kind === \"message\") {\n if (parsed.text.length > 0) {\n start_message_run(parsed.text);\n }\n return;\n }\n handle_slash(parsed);\n },\n [handle_slash, start_message_run],\n );\n\n const provider = agent.config.providers[0];\n const banner = tui_banner_text(theme, LICH_VERSION, provider?.model ?? \"unknown\", provider?.kind ?? \"unknown\");\n return (\n <Box flexDirection=\"column\" minHeight={8}>\n <Text dimColor>{resume_line === undefined ? banner : `${banner}\\n${resume_line}`}</Text>\n <MessageView blocks={blocks} state={state} />\n <StatusBar state={state} model={provider?.model ?? \"unknown\"} theme={theme} />\n <CommandBar busy={state.phase !== \"idle\" || resume_loading} on_submit={submit} />\n </Box>\n );\n}\n","/**\n * Resolve and read a session transcript for the TUI `/resume` command.\n */\nimport path from \"node:path\";\nimport type { Message } from \"../providers/types.js\";\nimport { resolve_session_path } from \"../session/resolve.js\";\nimport { read_session_messages } from \"../session/store.js\";\nimport type { ThemeSpec } from \"../util/lore.js\";\nimport { error_notice_block, resume_session_view, type HistoryBlock } from \"./state.js\";\n\nexport type LoadResumeResult =\n | { ok: true; id: string; messages: readonly Message[]; blocks: readonly HistoryBlock[]; banner_line: string }\n | { ok: false; block: HistoryBlock };\n\nfunction run_error_text(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** Load a transcript for `/resume`; returns view state or an error notice. */\nexport async function load_resume_view(\n session_dir: string,\n value: string,\n theme: ThemeSpec,\n): Promise<LoadResumeResult> {\n try {\n const transcript = await resolve_session_path(session_dir, value);\n const messages = await read_session_messages(transcript);\n const id = path.basename(transcript, \".jsonl\");\n const view = resume_session_view(id, messages, theme);\n return { ok: true, id, messages, blocks: view.blocks, banner_line: view.banner_line };\n } catch (error) {\n return { ok: false, block: error_notice_block(run_error_text(error)) };\n }\n}\n","/**\n * Pure state logic for the ink TUI: UI-state transitions from agent events,\n * slash-command parsing, transcript block mapping, and display formatters.\n * No ink/react imports here — this module is unit-tested without a TTY.\n */\nimport type { AgentEvent } from \"../agent/events.js\";\nimport type { AgentRunResult } from \"../agent/agent.js\";\nimport type { AssistantMessage, Message, ToolCall, Usage } from \"../providers/types.js\";\nimport { safe_json_parse, truncate_text } from \"../util/json.js\";\nimport type { ThemeSpec } from \"../util/lore.js\";\nimport { fill_template } from \"../util/theme.js\";\n\nexport type UiPhase = \"idle\" | \"thinking\" | \"tool\";\n\nexport interface UiState {\n readonly phase: UiPhase;\n readonly turns_used: number;\n readonly usage: Usage;\n readonly session_path: string | undefined;\n readonly compress_count: number;\n readonly budget_exhausted: boolean;\n readonly last_error: string | undefined;\n readonly active_tool: ToolCall | undefined;\n}\n\nexport const INITIAL_UI_STATE: UiState = {\n phase: \"idle\",\n turns_used: 0,\n usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n session_path: undefined,\n compress_count: 0,\n budget_exhausted: false,\n last_error: undefined,\n active_tool: undefined,\n};\n\nexport const HISTORY_CAP = 50;\nconst TOOL_ARGS_PREVIEW_CHARS = 80;\nconst TOOL_OUTPUT_PREVIEW_CHARS = 120;\nconst ERROR_PREVIEW_CHARS = 300;\n\nfunction error_text(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n}\n\n/** Reducer over UiState; one pure mapping per AgentEvent variant. */\nexport function apply_event(state: UiState, event: AgentEvent): UiState {\n switch (event.type) {\n case \"llm_start\":\n return { ...state, phase: \"thinking\", active_tool: undefined };\n case \"llm_end\":\n return {\n ...state,\n usage: {\n prompt_tokens: state.usage.prompt_tokens + event.result.usage.prompt_tokens,\n completion_tokens: state.usage.completion_tokens + event.result.usage.completion_tokens,\n total_tokens: state.usage.total_tokens + event.result.usage.total_tokens,\n },\n };\n case \"compress_end\":\n if (event.usage === undefined) {\n return state;\n }\n return {\n ...state,\n usage: {\n prompt_tokens: state.usage.prompt_tokens + event.usage.prompt_tokens,\n completion_tokens: state.usage.completion_tokens + event.usage.completion_tokens,\n total_tokens: state.usage.total_tokens + event.usage.total_tokens,\n },\n };\n case \"tool_call_start\":\n return { ...state, phase: \"tool\", active_tool: event.call };\n case \"tool_call_end\":\n return {\n ...state,\n phase: \"thinking\",\n active_tool: undefined,\n last_error:\n event.cancelled === true || event.result.ok === true\n ? state.last_error\n : (event.result.error ?? \"tool failed\"),\n };\n case \"turn_end\":\n return { ...state, turns_used: event.turn };\n case \"compress_start\":\n return { ...state, compress_count: state.compress_count + 1 };\n case \"budget_exhausted\":\n return { ...state, budget_exhausted: true };\n case \"error\":\n return { ...state, last_error: error_text(event.error) };\n default:\n return state;\n }\n}\n\n/** Fold an AgentRunResult back into UiState after the run promise resolves. */\nexport function apply_run_result(state: UiState, result: AgentRunResult): UiState {\n return {\n ...state,\n phase: \"idle\",\n turns_used: result.outcome.turns_used,\n session_path: result.session_path ?? state.session_path,\n last_error: result.outcome.stopped_reason === \"aborted\" ? \"run aborted\" : state.last_error,\n };\n}\n\nexport type ParsedInput = { kind: \"slash\"; name: string; args: string } | { kind: \"message\"; text: string };\n\n/** Split trimmed input into slash command vs plain message (empty input = message). */\nexport function parse_command(raw_input: string): ParsedInput {\n const text = raw_input.trim();\n if (text.startsWith(\"/\") === false) {\n return { kind: \"message\", text };\n }\n const body = text.slice(1);\n const space_index = body.indexOf(\" \");\n if (space_index === -1) {\n return { kind: \"slash\", name: body, args: \"\" };\n }\n return { kind: \"slash\", name: body.slice(0, space_index), args: body.slice(space_index + 1).trim() };\n}\n\n/** Dim header: the theme welcome string, the one tagline placement. */\nexport function tui_banner_text(theme: ThemeSpec, version: string, model: string, kind: string): string {\n return fill_template(theme.welcome, { version, model, kind });\n}\n\n/** Count messages shown in the resume banner (matches split_history_blocks visibility). */\nexport function resume_banner_count(messages: readonly Message[] | undefined): number {\n if (messages === undefined) {\n return 0;\n }\n return messages.filter((message) => message.role !== \"system\").length;\n}\n\n/** Second banner line when a session transcript was loaded via `--resume` or `/resume`. */\nexport function resume_banner_line(id: string, message_count: number): string {\n return `resumed ${id} (${message_count} messages)`;\n}\n\n/** 1234567 -> \"1,234,567\" (US grouping, matching the status bar style). */\nexport function format_usage(total_tokens: number): string {\n return total_tokens.toLocaleString(\"en-US\");\n}\n\nexport type BlockRole = \"user\" | \"lich\" | \"tool\" | \"meta\" | \"error\";\n\nexport interface HistoryBlock {\n readonly role: BlockRole;\n readonly lines: readonly string[];\n}\n\nfunction assistant_tool_line(call: ToolCall): string {\n const args_json = JSON.stringify(call.args) ?? \"{}\";\n return ` \\u23bf ${truncate_text(args_json, TOOL_ARGS_PREVIEW_CHARS)}`;\n}\n\nfunction format_message_lines(message: Message, theme: ThemeSpec): string[] {\n if (message.role === \"user\") {\n return [`${theme.user_label} \\u203a ${message.content}`];\n }\n if (message.role === \"assistant\") {\n const lines = [`${theme.response_label} \\u203a ${message.content}`];\n for (const call of message.tool_calls ?? []) {\n lines.push(assistant_tool_line(call));\n }\n return lines;\n }\n if (message.role === \"tool\") {\n const flag = message.is_error === true ? \"error\" : \"ok\";\n return [` \\u23bf ${message.name}: ${flag} (${truncate_text(message.content, TOOL_OUTPUT_PREVIEW_CHARS)})`];\n }\n return [`\\u00b7 system: ${message.content}`];\n}\n\n/** Map one transcript Message to display lines with its role tag. */\nexport function format_message_block(message: Message, theme: ThemeSpec): HistoryBlock {\n if (message.role === \"tool\") {\n const ok = message.is_error !== true;\n return { role: ok === true ? \"tool\" : \"error\", lines: format_message_lines(message, theme) };\n }\n const roles: Record<Exclude<Message[\"role\"], \"tool\">, BlockRole> = {\n system: \"meta\",\n user: \"user\",\n assistant: \"lich\",\n };\n return { role: roles[message.role], lines: format_message_lines(message, theme) };\n}\n\n/** Keep the newest `cap` non-system messages as renderable blocks. */\nexport function split_history_blocks(messages: readonly Message[], cap: number, theme: ThemeSpec): HistoryBlock[] {\n const visible = messages.filter((message) => message.role !== \"system\");\n const start = Math.max(0, visible.length - cap);\n return visible.slice(start).map((message) => format_message_block(message, theme));\n}\n\n/** Meta notice + transcript blocks after a successful in-TUI `/resume`. */\nexport function resume_session_view(\n id: string,\n messages: readonly Message[],\n theme: ThemeSpec,\n cap: number = HISTORY_CAP,\n): { blocks: readonly HistoryBlock[]; banner_line: string } {\n const banner_line = resume_banner_line(id, resume_banner_count(messages));\n const notice: HistoryBlock = { role: \"meta\", lines: [`\\u00b7 ${banner_line}`] };\n const history_cap = Math.max(0, cap - 1);\n return { blocks: [notice, ...split_history_blocks(messages, history_cap, theme)], banner_line };\n}\n\n/** Missing `/resume` argument notice. */\nexport function resume_missing_args_block(): HistoryBlock {\n return { role: \"error\", lines: [\"\\u00b7 /resume requires <id|latest>\"] };\n}\n\nfunction assistant_result_block(message: AssistantMessage, theme: ThemeSpec): HistoryBlock | undefined {\n if (message.content.length === 0) {\n return undefined;\n }\n return { role: \"lich\", lines: [`${theme.response_label} \\u203a ${message.content}`] };\n}\n\n/** Post-run meta blocks: compression notices, budget, errors, final answer. */\nexport function run_notice_blocks(result: AgentRunResult, theme: ThemeSpec): HistoryBlock[] {\n const blocks: HistoryBlock[] = [];\n if (result.outcome.stopped_reason === \"budget\") {\n blocks.push({ role: \"error\", lines: [`\\u00b7 ${fill_template(theme.notices.budget_exhausted, {})}`] });\n }\n const final_block = result.outcome.final === undefined ? undefined : assistant_result_block(result.outcome.final, theme);\n if (final_block !== undefined) {\n blocks.push(final_block);\n }\n return blocks;\n}\n\nfunction truncate_tool_preview(result_content: string, ok: boolean): string {\n return ` \\u23bf ${ok === true ? \"ok\" : \"error\"} (${truncate_text(result_content, TOOL_OUTPUT_PREVIEW_CHARS)})`;\n}\n\n/** One finalized live tool row; falls back to the transcript copy when absent. */\nexport function tool_result_block(call: ToolCall, ok: boolean, result_content: string): HistoryBlock {\n const args_json = JSON.stringify(call.args) ?? \"{}\";\n const lines = [\n `\\u23fa ${call.name}(${truncate_text(args_json, TOOL_ARGS_PREVIEW_CHARS)})`,\n truncate_tool_preview(result_content, ok),\n ];\n return { role: ok === true ? \"tool\" : \"error\", lines };\n}\n\nexport function parse_tool_message_content(content: string): { ok: boolean; output: string } {\n const parsed = safe_json_parse<{ ok?: unknown; output?: unknown }>(content);\n if (parsed !== undefined && typeof parsed.ok === \"boolean\" && typeof parsed.output === \"string\") {\n return { ok: parsed.ok, output: parsed.output };\n }\n return { ok: true, output: content };\n}\n\nexport function compress_notice_block(summary_chars: number, theme: ThemeSpec): HistoryBlock {\n const line = fill_template(theme.notices.compressed, { chars: summary_chars });\n return { role: \"meta\", lines: [`\\u00b7 ${line}`] };\n}\n\nexport function error_notice_block(message: string): HistoryBlock {\n return { role: \"error\", lines: [`\\u00b7 error: ${truncate_text(message, ERROR_PREVIEW_CHARS)}`] };\n}\n\nexport function tool_args_preview(args: Record<string, unknown>): string {\n const args_json = JSON.stringify(args) ?? \"{}\";\n return truncate_text(args_json, TOOL_ARGS_PREVIEW_CHARS);\n}\n\nexport function help_block(): HistoryBlock {\n return { role: \"meta\", lines: [...HELP_LINES] };\n}\n\nexport function model_label_block(config: { providers: readonly { model: string; kind: string }[] }): HistoryBlock {\n const provider = config.providers[0];\n return {\n role: \"meta\",\n lines: [`\\u00b7 model: ${provider?.model ?? \"unknown\"} \\u00b7 provider: ${provider?.kind ?? \"unknown\"}`],\n };\n}\n\nexport function usage_notice_block(total_tokens: number): HistoryBlock {\n return { role: \"meta\", lines: [`\\u00b7 tokens used this session: ${format_usage(total_tokens)}`] };\n}\n\nexport function unknown_command_block(name: string): HistoryBlock {\n return { role: \"error\", lines: [`\\u00b7 unknown command: /${name} (try /help)`] };\n}\n\nexport interface SessionEntryInfo {\n readonly name: string;\n readonly size_bytes: number;\n readonly mtime_ms: number;\n}\n\n/** Newest-first session listing, capped at `cap` entries. */\nexport function session_list_block(entries: readonly SessionEntryInfo[], theme: ThemeSpec, cap: number = 10): HistoryBlock {\n const sorted = [...entries].sort((a, b) => b.mtime_ms - a.mtime_ms).slice(0, cap);\n if (sorted.length === 0) {\n return { role: \"meta\", lines: [\"\\u00b7 no session files yet\"] };\n }\n const label = fill_template(theme.notices.sessions, { count: sorted.length });\n const lines: string[] = [`\\u00b7 ${label}`];\n for (const entry of sorted) {\n lines.push(` ${entry.name} (${format_usage(entry.size_bytes)} bytes)`);\n }\n return { role: \"meta\", lines };\n}\n\nexport const SLASH_COMMAND_NAMES: readonly string[] = [\n \"exit\",\n \"quit\",\n \"q\",\n \"help\",\n \"model\",\n \"usage\",\n \"clear\",\n \"sessions\",\n \"resume\",\n];\n\nexport const HELP_LINES: readonly string[] = [\n \"commands: /help /model /usage /clear /sessions /resume /exit (aliases: /quit /q)\",\n \"enter submits \\u00b7 backspace deletes \\u00b7 up/down recalls history \\u00b7 pasted newlines become spaces\",\n];","/**\n * Transcript rendering: maps HistoryBlock descriptors to ink elements with\n * role-based colors, and shows an animated braille spinner while thinking.\n * Blocks are pre-capped by the app, so a plain flex column is sufficient.\n */\nimport { useEffect, useState } from \"react\";\nimport { Box, Text } from \"ink\";\nimport { tool_args_preview, type HistoryBlock, type UiState } from \"./state.js\";\n\nconst SPINNER_FRAMES: readonly string[] = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\nconst SPINNER_INTERVAL_MS = 80;\n\n/** Braille spinner frames on an 80ms interval; clears on unmount. */\nfunction use_spinner(): string {\n const [frame, set_frame] = useState(SPINNER_FRAMES[0] ?? \"⠋\");\n useEffect(() => {\n const timer = setInterval(() => {\n const next = SPINNER_FRAMES[(SPINNER_FRAMES.indexOf(frame) + 1) % SPINNER_FRAMES.length];\n set_frame(next ?? \"⠋\");\n }, SPINNER_INTERVAL_MS);\n return () => {\n clearInterval(timer);\n };\n }, [frame]);\n return frame;\n}\n\nfunction ThinkingLine(): React.JSX.Element {\n const frame = use_spinner();\n return <Text dimColor>{`${frame} thinking…`}</Text>;\n}\n\nconst ROLE_COLORS: Record<HistoryBlock[\"role\"], string | undefined> = {\n user: \"white\",\n lich: \"green\",\n tool: \"cyan\",\n meta: undefined,\n error: \"red\",\n};\n\nfunction BlockLines({ block }: { block: HistoryBlock }): React.JSX.Element {\n const color = ROLE_COLORS[block.role];\n return (\n <>\n {block.lines.map((line, index) => (\n <Text key={index} color={color} dimColor={color === undefined}>{line}</Text>\n ))}\n </>\n );\n}\n\ninterface MessageViewProps {\n readonly blocks: readonly HistoryBlock[];\n readonly state: UiState;\n}\n\n/** Transcript column plus the live phase line (spinner / running tool row). */\nexport function MessageView({ blocks, state }: MessageViewProps): React.JSX.Element {\n return (\n <Box flexDirection=\"column\" flexGrow={1}>\n {blocks.map((block, index) => (\n <Box key={index} flexDirection=\"column\">\n <BlockLines block={block} />\n </Box>\n ))}\n {state.phase === \"thinking\" ? <ThinkingLine /> : null}\n {state.phase === \"tool\" && state.active_tool !== undefined ? (\n <Text color=\"cyan\">{`⏺ ${state.active_tool.name}(${tool_args_preview(state.active_tool.args)})`}</Text>\n ) : null}\n </Box>\n );\n}","/**\n * Bottom status line: model, turns, token totals, phase tag, compression\n * count, and the session path once the agent has persisted a transcript.\n */\nimport { Box, Text } from \"ink\";\nimport type { ThemeSpec } from \"../util/lore.js\";\nimport { format_usage, type UiState } from \"./state.js\";\n\ninterface StatusBarProps {\n readonly state: UiState;\n readonly model: string;\n readonly theme: ThemeSpec;\n}\n\nexport function StatusBar({ state, model, theme }: StatusBarProps): React.JSX.Element {\n const phase = theme.phase_labels[state.phase];\n return (\n <Box>\n <Text dimColor>\n {`model ${model} · turns ${state.turns_used} · tokens ${format_usage(state.usage.total_tokens)} · [${phase}]`}\n {state.compress_count > 0 ? ` · compressed ${state.compress_count}` : \"\"}\n {state.session_path !== undefined ? ` · ${state.session_path}` : \"\"}\n </Text>\n {state.budget_exhausted ? <Text color=\"red\">{` · ${theme.notices.budget_exhausted}`}</Text> : null}\n </Box>\n );\n}","/**\n * Input row: printable characters accumulate in a buffer, Enter submits,\n * Backspace/Delete edits, Up/Down walk a 20-entry recall ring, and pasted\n * newlines collapse to spaces. Ctrl+C is left to ink's default handling.\n */\nimport { useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\n\nconst INPUT_HISTORY_CAP = 20;\n\ninterface CommandBarProps {\n readonly busy: boolean;\n readonly on_submit: (text: string) => void;\n}\n\n/** Push onto a capped ring (newest first) without mutating the source. */\nfunction push_history(ring: readonly string[], entry: string): readonly string[] {\n return [entry, ...ring.filter((item) => item !== entry)].slice(0, INPUT_HISTORY_CAP);\n}\n\nexport function CommandBar({ busy, on_submit }: CommandBarProps): React.JSX.Element {\n const [buffer, set_buffer] = useState(\"\");\n const [recall_ring, set_recall_ring] = useState<readonly string[]>([]);\n const [recall_index, set_recall_index] = useState<number | undefined>(undefined);\n\n const submit_buffer = (): void => {\n if (busy === true) {\n return;\n }\n const text = buffer.trim();\n set_buffer(\"\");\n set_recall_index(undefined);\n if (text.length > 0) {\n set_recall_ring((current) => push_history(current, text));\n on_submit(text);\n }\n };\n\n /** Newest-first ring: Up walks toward older (higher index), Down toward newer. */\n const walk_recall = (direction: 1 | -1): void => {\n if (recall_ring.length === 0) {\n return;\n }\n if (direction === -1 && recall_index === 0) {\n set_recall_index(undefined);\n set_buffer(\"\");\n return;\n }\n if (direction === -1 && recall_index === undefined) {\n return;\n }\n const current = recall_index ?? -direction;\n const next = Math.min(Math.max(current + direction, 0), recall_ring.length - 1);\n set_recall_index(next);\n set_buffer(recall_ring[next] ?? \"\");\n };\n\n useInput((input, key) => {\n if (key.return === true) {\n submit_buffer();\n return;\n }\n if (key.upArrow === true) {\n walk_recall(1);\n return;\n }\n if (key.downArrow === true) {\n walk_recall(-1);\n return;\n }\n if (key.backspace === true || key.delete === true) {\n set_buffer((current) => current.slice(0, -1));\n return;\n }\n if (key.ctrl === true || key.escape === true || key.tab === true || key.meta === true) {\n return;\n }\n if (input.length > 0) {\n set_buffer((current) => current + input.replaceAll(\"\\n\", \" \").replaceAll(\"\\r\", \" \"));\n }\n });\n\n return (\n <Box>\n <Text dimColor>{busy ? \" … \" : \"› \"}</Text>\n <Text>{buffer}</Text>\n <Text dimColor>▌</Text>\n </Box>\n );\n}\n"],"mappings":"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","names":["useEffect","useState","Box","Text","Box","Text","jsx","jsxs","Box","Text","useState","Box","Text","jsx","jsxs","jsx","jsxs","run_error_text","useEffect","useState","Box","Text","jsx"]}
@@ -3,7 +3,7 @@
3
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
5
  context budget demands it, and persists transcripts. The published npm package
6
- is 0.7.0, including editor MCP (`mcp_servers`, `lich mcp`). It runs on
6
+ is 0.8.0, including editor MCP (`mcp_servers`, `lich mcp`). It runs on
7
7
  Node >= 20 and on Bun, is ESM with NodeNext resolution, and its runtime
8
8
  dependencies are `zod` (config validation), `ink`, and `react` (the TUI).
9
9
  Everything else is Node/Bun built-ins.
@@ -100,7 +100,7 @@ Walkthrough of a single `Agent.run({ input })` call
100
100
  are connected (`initialize`, `notifications/initialized`, then `tools/list`)
101
101
  and registered as `mcp_<server>_<tool>`. An empty `tools_enabled` never
102
102
  connects. Disabled servers are skipped. A connect failure logs a warning
103
- and the run continues. This client ships in 0.7.0.
103
+ and the run continues. This client has shipped since 0.7.0 (current package 0.8.0).
104
104
  2. **Usage collector attached.** `run()` subscribes a `collect_usage` handler
105
105
  on `agent.events`; every `llm_end` event adds the call's token usage into a
106
106
  per-run `Usage` total. The subscription is removed in a `finally` block.
@@ -42,7 +42,9 @@ confinement root), a process environment map (the agent injects
42
42
  from `Agent.run` so tools cancel on caller abort **or** the executor deadline
43
43
  (`tool.timeout_ms`, else
44
44
  `DEFAULT_TOOL_TIMEOUT_MS` = 30000). `terminal` sets 300000, `run_tests` sets
45
- 600000, and registered MCP tools set 120000.
45
+ 600000, and registered MCP tools set 120000. Note: `LICH_TERMINAL_TIMEOUT_MS`
46
+ is injected from config `terminal_timeout_ms` but the `terminal` tool ignores
47
+ it today — use the tool's `timeout_ms` argument (default 60000).
46
48
 
47
49
  **Parameter schemas.** `parameters` is a `JsonSchemaObject`
48
50
  (`src/util/json_schema.ts`) passed through verbatim into provider requests.
@@ -10,7 +10,7 @@
10
10
 
11
11
  ```sh
12
12
  npm install -g @moikapy/lich
13
- lich --version # -> 0.7.0 (reads package.json)
13
+ lich --version # -> 0.8.0 (reads package.json)
14
14
  ```
15
15
 
16
16
  ## Choose a configuration path
@@ -23,11 +23,11 @@ Lich needs exactly one thing before it runs: a model. You can provide it three w
23
23
  # ollama — no api key needed
24
24
  LICH_PROVIDER_KIND=ollama LICH_MODEL=llama3.2 lich "Reply with ok"
25
25
 
26
- # openai-compatible (api.openai.com/v1 by default)
27
- LICH_PROVIDER_KIND=openai_compat LICH_MODEL=gpt-4.1-mini lich "Reply with ok"
26
+ # openai-compatible (api.openai.com/v1 by default; use a model id your provider accepts)
27
+ LICH_PROVIDER_KIND=openai_compat LICH_MODEL=gpt-4o-mini lich "Reply with ok"
28
28
 
29
29
  # anthropic
30
- LICH_PROVIDER_KIND=anthropic LICH_MODEL=claude-sonnet-4 lich "Reply with ok"
30
+ LICH_PROVIDER_KIND=anthropic LICH_MODEL=claude-sonnet-4-20250514 lich "Reply with ok"
31
31
  ```
32
32
 
33
33
  Defaults per kind when `LICH_BASE_URL`/`LICH_API_KEY_ENV` are unset: `openai_compat` uses `https://api.openai.com/v1` and reads `OPENAI_API_KEY`; `anthropic` uses `https://api.anthropic.com` and reads `ANTHROPIC_API_KEY`; `ollama` uses `http://localhost:11434` and needs no key.
@@ -76,7 +76,7 @@ lich # TUI; first run on a TTY opens the setup wizard
76
76
  lich tui # same TUI, no wizard
77
77
  ```
78
78
 
79
- Type a message and press Enter. The transcript shows your line, live tool-call rows while the agent works, and the reply; the status bar at the bottom tracks turns, tokens, and the session file path. Slash commands: `/help`, `/model`, `/usage`, `/clear`, `/sessions`, `/exit`. Details in [the TUI guide](user-guide/tui.md).
79
+ Type a message and press Enter. The transcript shows your line, live tool-call rows while the agent works, and the reply; the status bar at the bottom tracks turns, tokens, and the session file path. Slash commands: `/help`, `/model`, `/usage`, `/clear`, `/sessions`, `/resume`, `/exit`. Details in [the TUI guide](user-guide/tui.md).
80
80
 
81
81
  ## Your first gateway webhook
82
82
 
@@ -153,7 +153,7 @@ To hack on Lich itself, run the CLI straight from a clone instead of the npm pac
153
153
  ```sh
154
154
  git clone https://github.com/Moikapy/lich.git && cd lich
155
155
  bun install
156
- bun src/cli.ts --version # -> 0.7.0 (package.json)
156
+ bun src/cli.ts --version # -> 0.8.0 (package.json)
157
157
  ```
158
158
 
159
159
  `bun src/cli.ts` accepts the same arguments as the installed `lich` binary, so every command on this page works unchanged.