@moikapy/lich 0.7.1 → 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 (40) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +38 -19
  3. package/dist/{chunk-CVX7LZWC.js → chunk-4N3S6ACI.js} +2 -2
  4. package/dist/{chunk-WNFBIX4E.js → chunk-KOEZQUIX.js} +1312 -600
  5. package/dist/chunk-KOEZQUIX.js.map +1 -0
  6. package/dist/{chunk-PZNYVGD4.js → chunk-P2YEHNM4.js} +2 -2
  7. package/dist/chunk-P2YEHNM4.js.map +1 -0
  8. package/dist/chunk-PL6MKRKE.js +75 -0
  9. package/dist/chunk-PL6MKRKE.js.map +1 -0
  10. package/dist/chunk-TYRKVBWQ.js +104 -0
  11. package/dist/chunk-TYRKVBWQ.js.map +1 -0
  12. package/dist/cli.d.ts +19 -1
  13. package/dist/cli.js +125 -47
  14. package/dist/cli.js.map +1 -1
  15. package/dist/{gateway-44QTIJTJ.js → gateway-OSB7254F.js} +209 -46
  16. package/dist/gateway-OSB7254F.js.map +1 -0
  17. package/dist/index.d.ts +122 -62
  18. package/dist/index.js +12 -4
  19. package/dist/resolve-H22TOVB5.js +7 -0
  20. package/dist/resolve-H22TOVB5.js.map +1 -0
  21. package/dist/store-YYLEB7JG.js +9 -0
  22. package/dist/store-YYLEB7JG.js.map +1 -0
  23. package/dist/{tui-MEJGEILU.js → tui-XXIU4W7K.js} +216 -25
  24. package/dist/tui-XXIU4W7K.js.map +1 -0
  25. package/docs/architecture/agent-loop.md +28 -9
  26. package/docs/architecture/overview.md +10 -9
  27. package/docs/architecture/tools.md +3 -1
  28. package/docs/getting-started.md +6 -6
  29. package/docs/index.md +1 -1
  30. package/docs/user-guide/cli.md +17 -8
  31. package/docs/user-guide/games.md +5 -4
  32. package/docs/user-guide/library.md +6 -3
  33. package/docs/user-guide/redot.md +2 -2
  34. package/docs/user-guide/tui.md +3 -2
  35. package/package.json +1 -1
  36. package/dist/chunk-PZNYVGD4.js.map +0 -1
  37. package/dist/chunk-WNFBIX4E.js.map +0 -1
  38. package/dist/gateway-44QTIJTJ.js.map +0 -1
  39. package/dist/tui-MEJGEILU.js.map +0 -1
  40. /package/dist/{chunk-CVX7LZWC.js.map → chunk-4N3S6ACI.js.map} +0 -0
package/docs/index.md CHANGED
@@ -68,4 +68,4 @@ Working from a clone of the repository? `bun install`, then run the same command
68
68
 
69
69
  ## Version compatibility
70
70
 
71
- The published npm package is **0.7.0**. `lich --version` reads `package.json`. Editor MCP (`mcp_servers`, `lich mcp`) ships in 0.7.0. Node >= 20 (`engines` in `package.json`); Bun is the recommended runtime for development from a clone (`bun src/cli.ts ...`). The TUI needs a TTY; the gateway and library run headless on both runtimes.
71
+ The published npm package is **0.8.0**. `lich --version` reads `package.json`. Editor MCP (`mcp_servers`, `lich mcp`) has shipped since 0.7.0. Node >= 20 (`engines` in `package.json`); Bun is the recommended runtime for development from a clone (`bun src/cli.ts ...`). The TUI needs a TTY; the gateway and library run headless on both runtimes.
@@ -19,7 +19,7 @@ lich mcp enable <name> # set enabled true
19
19
  lich mcp disable <name>
20
20
  lich mcp remove <name>
21
21
  lich --help # usage text
22
- lich --version # package.json version (published package and this tree: 0.7.0)
22
+ lich --version # package.json version (published package and this tree: 0.8.0)
23
23
  ```
24
24
 
25
25
  - **Bare `lich`** opens the same TUI as `lich tui`. It does not print usage. On a TTY, if neither `.lich/config.json` nor `~/.config/lich/config.json` exists, a setup wizard runs first (name, provider, optional gateway env-var names, optional plugins) and writes `.lich/config.json` once. `LICH_MODEL` / `--model` prefills the model prompt; it does not skip the wizard. An existing config in that chain skips the wizard and is not replaced. Non-TTY stdin skips the wizard and prints guidance instead of hanging. `lich --help` still prints usage.
@@ -29,9 +29,9 @@ lich --version # package.json version (published package and this tree:
29
29
  - **TUI** launches the ink interface. See the [TUI guide](tui.md).
30
30
  - **Gateway** runs platform adapters (defaults to `webhook` when no platform is given). See the [Gateway guide](gateway.md). Unknown platform names are skipped with a warning; if none remain, the CLI exits `1`.
31
31
  - **Update** compares the installed version to the npm registry and, when a newer release exists, runs `npm install -g @moikapy/lich@latest`. Exit any running TUI or gateway first; npm cannot replace the package while those processes are running. A git clone is told to `git pull`. See [Updating](../getting-started.md#updating).
32
- - **MCP** ships in 0.7.0 (`lich mcp`). It edits only `mcp_servers` in `<work-dir>/.lich/config.json` through the same writer as `lich init` (`update` mode, so other keys stay). A missing file lists as empty; `add` creates the file if needed. New entries stay disabled until `enable`. Names must match `^[a-z][a-z0-9_]*$`. Catalog names use `optional-mcps/`; otherwise pass `--command` and repeatable `--arg`, or `--url` (loopback only), not both. Redot still needs `--project-path` for the catalog args, and the command basename must be `redot`. No prompts. See the [Redot guide](redot.md).
32
+ - **MCP** (`lich mcp`) edits only `mcp_servers` in `<work-dir>/.lich/config.json` through the same writer as `lich init` (`update` mode, so other keys stay). A missing file lists as empty; `add` creates the file if needed. New entries stay disabled until `enable`. Names must match `^[a-z][a-z0-9_]*$`. Catalog names use `optional-mcps/`; otherwise pass `--command` and repeatable `--arg`, or `--url` (loopback only), not both. Redot still needs `--project-path` for the catalog args, and the command basename must be `redot`. No prompts. See the [Redot guide](redot.md).
33
33
 
34
- A repository clone's `bun src/cli.ts` matches that checkout. The published 0.7.0 binary includes `lich mcp`.
34
+ A repository clone's `bun src/cli.ts` matches that checkout. The published 0.8.0 binary includes `lich mcp`.
35
35
 
36
36
  ## Flags
37
37
 
@@ -48,6 +48,7 @@ Flags work before or after the subcommand. Every value flag can also be set via
48
48
  | `--api-key-env <NAME>` | Env var holding the api key for `providers[0]`. | `LICH_API_KEY_ENV`, else per-kind default |
49
49
  | `--system-prompt <s>` | Replaces the default system prompt. | built-in concise-assistant prompt |
50
50
  | `--session-dir <path>` | Transcript directory. | `<work_dir>/.lich/sessions` |
51
+ | `--resume <id\|latest>` | TUI only: load an existing session transcript into history. Exact id, unique filename prefix, or `latest` (newest by mtime). | – |
51
52
  | `--log-level <level>` | `debug` \| `info` \| `warn` \| `error`. | `info` |
52
53
  | `--theme <name>` | Display theme loaded once at startup. `lich` is built-in; other names read `~/.lich/themes/<name>.json`. | `lich` |
53
54
  | `--command <bin>` | `lich mcp add` only: local stdio binary. | – |
@@ -55,7 +56,7 @@ Flags work before or after the subcommand. Every value flag can also be set via
55
56
  | `--url <url>` | `lich mcp add` only: loopback HTTP MCP URL. | – |
56
57
  | `--project-path <path>` | `lich mcp add` only: catalog `${project_path}` substitute. | – |
57
58
 
58
- Passing `--max-turns 0` or a non-integer fails with `--max-turns must be a positive integer`. Unknown flags fail with `unknown flag: --foo`. A flag missing its value fails with `<flag> requires a value`.
59
+ Passing `--max-turns 0` or a non-integer fails with `--max-turns must be a positive integer`. Unknown flags fail with `unknown flag: --foo`. A flag missing its value fails with `<flag> requires a value`. `--resume` outside the TUI (one-shot, `chat`, `gateway`) fails with `--resume is only supported in TUI mode (not <mode>)`.
59
60
 
60
61
  ## Provider resolution
61
62
 
@@ -84,7 +85,7 @@ Validated by zod (top-level unknown keys are silently stripped; extra keys insid
84
85
  {
85
86
  "kind": "openai_compat",
86
87
  "name": "openrouter",
87
- "model": "anthropic/claude-sonnet-4",
88
+ "model": "anthropic/claude-sonnet-4-20250514",
88
89
  "base_url": "https://openrouter.ai/api/v1",
89
90
  "api_key_env": "OPENROUTER_API_KEY"
90
91
  },
@@ -127,7 +128,7 @@ Validated by zod (top-level unknown keys are silently stripped; extra keys insid
127
128
  | `theme` | string | `lich` | Display theme name. See [Themes](https://github.com/Moikapy/lich/blob/main/README.md#themes). |
128
129
  | `gateway` | object | omitted | Optional. `platforms` (`webhook` \| `telegram` \| `discord` \| `twitch`) and `token_envs` (platform → env-var name). Secrets stay in the environment. |
129
130
  | `plugins` | string array | `[]` | Module paths relative to `work_dir` or absolute. Bare `lich`, one-shot, chat, tui, and gateway load them through `create_agent_with_plugins`. `run_agent` does too. `create_agent` does not. See the [plugins guide](plugins.md). |
130
- | `mcp_servers` | object | omitted | Optional. Closed record of named servers. Each entry is stdio `{command, args, env?}` or loopback http `{url}`. `enabled` defaults to false. Unknown keys are rejected. Ships in 0.7.0. See the [Redot guide](redot.md). |
131
+ | `mcp_servers` | object | omitted | Optional. Closed record of named servers. Each entry is stdio `{command, args, env?}` or loopback http `{url}`. `enabled` defaults to false. Unknown keys are rejected. See the [Redot guide](redot.md). |
131
132
  | `system_prompt` | string | built-in | Replaces the default system prompt. |
132
133
  | `max_turns` | int >= 1 | `25` | Turn budget per run. |
133
134
  | `work_dir` | string | cwd | Root for all file tools; paths outside are rejected. |
@@ -137,7 +138,7 @@ Validated by zod (top-level unknown keys are silently stripped; extra keys insid
137
138
  | `context_budget_tokens` | positive int | `100000` | Estimated budget before compression triggers. |
138
139
  | `compress_threshold` | 0.1–0.95 | `0.8` | Compress when usage >= this fraction of the budget. |
139
140
  | `session_dir` | string | `<work_dir>/.lich/sessions` | Transcript directory. |
140
- | `terminal_timeout_ms` | positive int | `60000` | Default timeout injected into the `terminal` tool. |
141
+ | `terminal_timeout_ms` | positive int | `60000` | Written into tool context as `LICH_TERMINAL_TIMEOUT_MS`. The `terminal` tool does **not** read it yet; pass `timeout_ms` on the tool call (default 60000, max 300000). |
141
142
  | `log_level` | enum | `info` | Logger verbosity. |
142
143
 
143
144
  Minimal per-provider examples:
@@ -147,7 +148,7 @@ Minimal per-provider examples:
147
148
  ```
148
149
 
149
150
  ```json
150
- { "providers": [{ "kind": "anthropic", "name": "main", "model": "claude-sonnet-4", "api_key_env": "ANTHROPIC_API_KEY" }] }
151
+ { "providers": [{ "kind": "anthropic", "name": "main", "model": "claude-sonnet-4-20250514", "api_key_env": "ANTHROPIC_API_KEY" }] }
151
152
  ```
152
153
 
153
154
  Listed providers form a failover chain: the router walks them in order, retrying `rate_limit`/`network` errors (bounded backoff) on the current provider before moving on, and failing over immediately on `auth`, `overflow`, and `bad_request`.
@@ -162,6 +163,8 @@ never accepted as a config passthrough.
162
163
  | `LICH_ALLOW_SELF_COMMIT` | Set to `1` to allow one gated `git_commit` per run. Unset or any other value is fail-closed. Read at agent construction. |
163
164
  | `LICH_ALLOW_PRIVATE_URLS` | Set to exactly `1` to let `fetch_url` / `http_request` reach private or loopback URLs. Unset or any other value is fail-closed (they are blocked). |
164
165
  | `LICH_TEST_COMMAND` | Command `run_tests` runs in `work_dir` (default `node node_modules/vitest/vitest.mjs run`). An optional `filter` argument is appended. |
166
+ | `LICH_DOCS_DIR` | Optional docs root for `docs_read` / `docs_search` (dir with `index.md`, or a parent containing `docs/`). Else `<work_dir>/docs` or package docs. |
167
+ | `LICH_TERMINAL_TIMEOUT_MS` | Injected from config `terminal_timeout_ms` into tool context. Unused by `terminal` today — use the tool's `timeout_ms` arg. |
165
168
 
166
169
  Veto reasons, the terminal git denylist, skills, and `MEMORY.md` are in the
167
170
  [plugins guide](plugins.md#self-improvement-loop).
@@ -174,6 +177,12 @@ Each run writes `.lich/sessions/<timestamp36>-<counter>[-label].jsonl` where the
174
177
  # follow the newest session
175
178
  ls -t .lich/sessions/*.jsonl | head -1
176
179
 
180
+ # resume that session in the TUI (loads history; new turns append to one
181
+ # per-launch transcript via incremental persistence).
182
+ # Inside a running TUI, `/resume <id|latest>` uses the same load path (Phase 3).
183
+ lich --resume latest
184
+ lich tui --resume m1abc-1-tui
185
+
177
186
  # print the conversation
178
187
  jq -r 'select(.kind=="message") | "\(.message.role): \(.message.content // "(tool call)")"' .lich/sessions/<file>.jsonl
179
188
  ```
@@ -8,7 +8,7 @@ Tool shapes live in [`examples/game_bridge/README.md`](https://github.com/Moikap
8
8
 
9
9
  ## Session files as combat logs
10
10
 
11
- Each `Agent.run` appends one `.jsonl` file under `session_dir` (default `<work_dir>/.lich/sessions`). Records are `{ts, kind: "message"|"meta", message?, meta?}`.
11
+ Each `Agent.run` without a shared `session` handle appends one `.jsonl` file under `session_dir` (default `<work_dir>/.lich/sessions`). The TUI passes one handle per launch so N turns share one file. Records are `{ts, kind: "message"|"meta", message?, meta?}`.
12
12
 
13
13
  | What you want | Where it is |
14
14
  | --- | --- |
@@ -60,7 +60,8 @@ jq -r 'select(.kind=="message") | select(.message.role=="tool" and .message.is_e
60
60
  ```
61
61
 
62
62
  ```bash
63
- # (5) pacing
63
+ # (5) pacing (`.ts` is append/write time for each JSONL record — useful with
64
+ # incremental persistence; not a model-side event clock)
64
65
  jq -r 'select(.kind=="message") | select(.message.role=="user" or .message.role=="assistant")
65
66
  | [.ts, .message.role] | @tsv' .lich/sessions/<run>.jsonl
66
67
  ```
@@ -75,9 +76,9 @@ Recipe 2 counts assistant tool-call arguments, including orders a hook later vet
75
76
 
76
77
  ## Do not glob a playthrough blindly
77
78
 
78
- A gateway conversation of N posts writes N files. `Agent.run` seeds from `history`, and `persist_session` appends all of `outcome.messages`, so each file is a superset of the previous exchange. Globbing `*.jsonl` double-counts. Take the newest file per label (names sort by timestamp prefix), or dedupe on `tool_call.id`, which stays stable when the same call is replayed into the next file.
79
+ A gateway conversation of N posts writes N files. `Agent.run` seeds from `history`, and without a shared `session` handle each run opens a new file that includes prior history plus the new exchange, so each file is a superset of the previous. Globbing `*.jsonl` double-counts. Take the newest file per label (names sort by timestamp prefix), or dedupe on `tool_call.id`, which stays stable when the same call is replayed into the next file. TUI launches avoid this by sharing one handle.
79
80
 
80
- Compression can rewrite a long run in place: when estimated tokens cross `compress_threshold` of `context_budget_tokens`, older messages become one summary and the 8 most recent non-system messages stay verbatim. Early rounds may survive only as that summary. The recipes see the file on disk, not the pre-compression transcript.
81
+ Compression can rewrite in-memory history: when estimated tokens cross `compress_threshold` of `context_budget_tokens`, older messages become one summary and the 8 most recent non-system messages stay verbatim. The transcript keeps raw pre-compress messages plus a `compress_end` meta marker; on resume those raw messages replay and compression may run again. Early rounds may survive only as the in-memory summary for the live agent. Offline recipes see the file on disk.
81
82
 
82
83
  ## Player modeling
83
84
 
@@ -62,7 +62,8 @@ const result = await run_agent(
62
62
  | `input` | `string` | Required user message for this run. |
63
63
  | `history` | `Message[]` | Prior conversation to continue (multi-turn). |
64
64
  | `signal` | `AbortSignal` | Cooperative cancellation; the loop returns `outcome.stopped_reason: "aborted"`. |
65
- | `label` | `string` | Origin tag for the session filename (e.g. `"tui"`, `"gw:webhook:default"`). |
65
+ | `label` | `string` | Origin tag for the session filename (e.g. `"tui"`, `"gw:webhook:default"`). Unused when `session` is set. |
66
+ | `session` | `SessionHandle` | Optional shared transcript handle. When set, `Agent.run` reuses it instead of opening a new file (TUI: one handle per launch). |
66
67
 
67
68
  `AgentRunResult`:
68
69
 
@@ -147,7 +148,7 @@ Listed providers form a failover chain tried in order: `rate_limit`/`network` er
147
148
 
148
149
  `tools_enabled` accepts `"all"` (default) or an array of tool names to register; everything else stays unregistered and invisible to the model. MCP tools, when a named server is `enabled`, use the same allowlist and stay off when the list is `[]` (that empty list does not connect). The filter does not apply to plugin tools: they register afterward, including the gatekeeper's `git_commit` (fail-closed unless `LICH_ALLOW_SELF_COMMIT=1`). `[]` strips every builtin and every MCP tool and does not throw.
149
150
 
150
- `mcp_servers` and `catalog_client_entry` ship in 0.7.0. See the [Redot guide](redot.md).
151
+ `mcp_servers` and `catalog_client_entry` ship in this package (since 0.7.0). See the [Redot guide](redot.md).
151
152
 
152
153
  ```ts
153
154
  const agent = create_agent({
@@ -190,4 +191,6 @@ A Godot client does not embed the library. A game backend that does is still one
190
191
 
191
192
  ## Session access
192
193
 
193
- Each `run()` appends a transcript line-by-line under `config.session_dir` (default `<work_dir>/.lich/sessions`); `result.session_path` gives the exact file. Records carry `{ts, kind: "message"|"meta", message?, meta?}`. A completed run (`final`, `budget`, or `aborted` returned by the loop) closes with `{event: "run_end", stopped_reason, usage}` where `usage` equals `usage_total`. A budget stop also writes `{event: "budget_exhausted"}` before that. Provider throws do not persist. Read transcripts with `jq` (see the [games guide](games.md)) or `read_session_messages(path)` from a source checkout — it is not a package export. Persistence is best-effort: a write failure logs a warning, returns `session_path: undefined`, and never fails the run.
194
+ Each `run()` appends to a JSONL transcript under `config.session_dir` (default `<work_dir>/.lich/sessions`) while the loop runs: `run_start`, seeded system/user (plus history when the handle is new), then assistant/tool messages on `llm_end` / `tool_call_end`, optional `budget_exhausted` / `compress_end` meta, and `run_end` (`stopped_reason`, `usage` matching `usage_total`) when the run completes. Pass `session` to reuse one handle across runs (the TUI does this so one launch = one file). Omit it for per-run files (one-shot, chat, gateway). `result.session_path` is the handle path when recording started. Records carry `{ts, kind: "message"|"meta", message?, meta?}`. Provider throws leave whatever was already appended; they do not write `run_end`. `read_session_messages` drops a trailing user message so resume does not start with two consecutive user turns. Read transcripts with `jq` (see the [games guide](games.md)) or `read_session_messages(path)` from a source checkout — it is not a package export. Persistence is best-effort: a write failure logs a warning and never fails the run.
195
+
196
+ **CLI resume:** `lich --resume <id|latest>` (TUI only) resolves a transcript via `src/session/resolve.ts`, loads messages with `read_session_messages`, and seeds the TUI history. New turns append into the launch's shared handle (history is written once on first seed).
@@ -22,13 +22,13 @@ The client is general, the same shape as Hermes and Claude: a named list you ext
22
22
  }
23
23
  ```
24
24
 
25
- stdio is a local binary you named, plus `args`, plus optional `env`. The process is spawned with those argv, never a shell. `npx`, `npm`, `bunx`, `uvx`, `curl`, `wget`, a URL, and shell metacharacters are refused. lich does not run `npx -y` or fetch an addon. Values in `env` are passed to the process and are never logged. `lich mcp add` has no flag for `env`; set that key in the file if you need it. On Bun, the spawned child is not `unref`'d, so one-shot and other short-lived runs wait for the MCP answer instead of exiting while the child is still working; `Agent.close()` (every CLI mode calls it) kills the child.
25
+ stdio is a local binary you named, plus `args`, plus optional `env`. The process is spawned with those argv, never a shell. Config validation refuses common downloader basenames (`npx`, `npm`, `bunx`, `uvx`, `curl`, `wget`), a URL in the command, and shell metacharacters — a footgun guard, not a sandbox (wrappers like `bash -c` or `env npx` are not covered). Treat an enabled server as trusted code. lich does not run `npx -y` or fetch an addon. Values in `env` are passed to the process and are never logged. `lich mcp add` has no flag for `env`; set that key in the file if you need it. On Bun, the spawned child is not `unref`'d, so one-shot and other short-lived runs wait for the MCP answer instead of exiting while the child is still working; `Agent.close()` (every CLI mode calls it) kills the child.
26
26
 
27
27
  HTTP is `{ "url": "http://127.0.0.1:9/mcp" }`. The host must be `127.0.0.1` or `localhost`. `0.0.0.0` and any other host are refused. There is no remote MCP in v1.
28
28
 
29
29
  On connect the client sends `initialize`, then `notifications/initialized`, then `tools/list`. `tools/call` runs only when the model invokes a registered tool. Registered names are `mcp_<server>_<tool>`, so two servers cannot collide. They appear only when that server is `enabled` and `tools_enabled` is `"all"` or lists the prefixed name. `tools_enabled: []` drops them even when the server is enabled, and does not connect. Plugin tools still register, including the gatekeeper's `git_commit`. The commander persona keeps `tools_enabled: []` and the `game_bridge` plugin only — `persona_config` does not copy `mcp_servers`.
30
30
 
31
- This client ships in 0.7.0 (`lich mcp`, `mcp_servers`). From a clone, use `bun src/cli.ts mcp ...`.
31
+ This client has shipped since 0.7.0 (`lich mcp`, `mcp_servers`; current package 0.8.0). From a clone, use `bun src/cli.ts mcp ...`.
32
32
 
33
33
  ## Add a server
34
34
 
@@ -9,12 +9,12 @@ lich # front door: TUI, plus a first-run setup wizard when no config exi
9
9
  lich tui # same TUI, no wizard. From a clone: bun src/cli.ts tui
10
10
  ```
11
11
 
12
- The TUI needs a TTY and a resolvable provider (same resolution as every mode). On startup it prints a dim header from the active theme welcome string, e.g. `⚱ lich v0.7.0 — the agent that will not stay dead · llama3.2 (ollama)`. `{version}` is `LICH_VERSION` from `package.json`. That banner is the only tagline placement. Quit with `/exit`, `/quit`, `/q`, or Ctrl+C.
12
+ The TUI needs a TTY and a resolvable provider (same resolution as every mode). On startup it prints a dim header from the active theme welcome string, e.g. `⚱ lich v0.8.0 — the agent that will not stay dead · llama3.2 (ollama)`. `{version}` is `LICH_VERSION` from `package.json`. That banner is the only tagline placement. Quit with `/exit`, `/quit`, `/q`, or Ctrl+C.
13
13
 
14
14
  ## Anatomy
15
15
 
16
16
  ```
17
- ⚱ lich v0.7.0 — the agent that will not stay dead · llama3.2 (ollama)
17
+ ⚱ lich v0.8.0 — the agent that will not stay dead · llama3.2 (ollama)
18
18
  mortal › list the files here <- your input, echoed into the transcript
19
19
  ⏺ list_dir({}) <- live tool-call row (name + args preview)
20
20
  ⏷ list_dir: ok (d src/ d test/ ...) <- result row (ok/error + output preview)
@@ -37,6 +37,7 @@ model llama3.2 · turns 2 · tokens 1,204 · [dormant] · /path/.lich/sessions/.
37
37
  | `/usage` | Show tokens used this session (cumulative across turns). |
38
38
  | `/clear` | Wipe the on-screen transcript. Does **not** reset agent memory — the next message still sees prior turns. |
39
39
  | `/sessions` | List the 10 newest `.jsonl` files in `session_dir` with sizes. The heading uses the theme sessions label (`phylacteries (n):` by default). |
40
+ | `/resume <id\|latest>` | Load a stored transcript into agent history (exact id, unique prefix, or `latest` by mtime — same resolver as `--resume`). Resets the on-screen transcript with a `· resumed <id> (n messages)` notice and updates the header banner. Missing/ambiguous ids print an error notice. |
40
41
  | `/exit`, `/quit`, `/q` | Exit the TUI. |
41
42
 
42
43
  Unknown commands print `· unknown command: /x (try /help)`. Slash commands are handled client-side and never invoke the model.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moikapy/lich",
3
- "version": "0.7.1",
3
+ "version": "0.9.0",
4
4
  "description": "Lich — a TypeScript AI agent harness (library + CLI) inspired by Hermes",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts","../src/mcp/mcp_catalog_entry.ts"],"sourcesContent":["/**\n * Public surface of the lich agent harness. Pure re-exports, plus the package version.\n */\nimport { readFileSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\n\nfunction read_package_version(): string {\n const pkg_path = fileURLToPath(new URL(\"../package.json\", import.meta.url));\n const pkg = JSON.parse(readFileSync(pkg_path, \"utf8\")) as { version?: unknown };\n if (typeof pkg.version !== \"string\" || pkg.version.length === 0) {\n throw new Error(\"package.json is missing version\");\n }\n return pkg.version;\n}\n\nexport const LICH_VERSION = read_package_version();\n\nexport { Agent, create_agent, run_agent, create_agent_with_plugins } from \"./agent/agent.js\";\nexport type { AgentRunOptions, AgentRunResult } from \"./agent/agent.js\";\nexport type { AgentConfig } from \"./agent/config.js\";\nexport { parse_agent_config } from \"./agent/config.js\";\nexport { catalog_client_entry } from \"./mcp/mcp_catalog_entry.js\";\nexport { AgentEmitter } from \"./agent/events.js\";\nexport type { AgentEvent, AgentEventHandler, AgentEvents } from \"./agent/events.js\";\nexport type { LoopOutcome, LoopParams } from \"./agent/loop.js\";\nexport type {\n ChatOptions,\n ChatResult,\n Message,\n ProviderConfig,\n ToolCall,\n ToolDefinition,\n Usage,\n} from \"./providers/types.js\";\nexport { ProviderError } from \"./providers/types.js\";\nexport { ToolExecutor } from \"./tools/executor.js\";\nexport { ToolRegistry } from \"./tools/registry.js\";\nexport type { Tool, ToolContext, ToolResult } from \"./tools/types.js\";\nexport { register_builtin_tools } from \"./tools/builtin/index.js\";\nexport { HookedToolRunner } from \"./plugins/hooks.js\";\nexport { load_plugins, plugin_errors_summary } from \"./plugins/loader.js\";\nexport type { LoadedPlugin, PluginLoadError } from \"./plugins/loader.js\";\nexport type {\n AfterToolCallInfo,\n BeforeToolCallInfo,\n BeforeToolCallResult,\n HookContext,\n Plugin,\n PluginHooks,\n RunEndInfo,\n} from \"./plugins/types.js\";","/**\n * Install shape: turn a catalog manifest into a disabled mcp_servers entry.\n * Does not spawn, download, or copy secrets.\n */\nimport { catalog_by_name } from \"./mcp_catalog.js\";\n\nconst PLACEHOLDER = /\\$\\{([a-z_]+)\\}/g;\n\nfunction expand(value: string, substitutes: Record<string, string>): string | undefined {\n const tokens = value.match(PLACEHOLDER) ?? [];\n let next = value;\n for (const token of tokens) {\n const key = token.slice(2, -1);\n const found = substitutes[key];\n if (found === undefined || found.length === 0) {\n return undefined;\n }\n next = next.replace(token, found);\n }\n return next;\n}\n\n/** Write the client entry. The user sets enabled. Godot has nothing to write. */\nexport function catalog_client_entry(\n name: string,\n substitutes: Record<string, string> = {},\n): Record<string, unknown> | string {\n const manifest = catalog_by_name(name);\n if (manifest === undefined) {\n return `unknown mcp catalog entry '${name}'`;\n }\n if (manifest.transport !== \"stdio\" || manifest.command === undefined) {\n return manifest.note ?? `${name} has no official mcp server to enable`;\n }\n const args: string[] = [];\n for (const arg of manifest.args ?? []) {\n const expanded = expand(arg, substitutes);\n if (expanded === undefined) {\n return `mcp catalog '${name}' needs a substitute for ${arg}`;\n }\n args.push(expanded);\n }\n return { enabled: false, command: manifest.command, args };\n}\n"],"mappings":";;;;;AAGA,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;;;ACE9B,IAAM,cAAc;AAEpB,SAAS,OAAO,OAAe,aAAyD;AACtF,QAAM,SAAS,MAAM,MAAM,WAAW,KAAK,CAAC;AAC5C,MAAI,OAAO;AACX,aAAW,SAAS,QAAQ;AAC1B,UAAM,MAAM,MAAM,MAAM,GAAG,EAAE;AAC7B,UAAM,QAAQ,YAAY,GAAG;AAC7B,QAAI,UAAU,UAAa,MAAM,WAAW,GAAG;AAC7C,aAAO;AAAA,IACT;AACA,WAAO,KAAK,QAAQ,OAAO,KAAK;AAAA,EAClC;AACA,SAAO;AACT;AAGO,SAAS,qBACd,MACA,cAAsC,CAAC,GACL;AAClC,QAAM,WAAW,gBAAgB,IAAI;AACrC,MAAI,aAAa,QAAW;AAC1B,WAAO,8BAA8B,IAAI;AAAA,EAC3C;AACA,MAAI,SAAS,cAAc,WAAW,SAAS,YAAY,QAAW;AACpE,WAAO,SAAS,QAAQ,GAAG,IAAI;AAAA,EACjC;AACA,QAAM,OAAiB,CAAC;AACxB,aAAW,OAAO,SAAS,QAAQ,CAAC,GAAG;AACrC,UAAM,WAAW,OAAO,KAAK,WAAW;AACxC,QAAI,aAAa,QAAW;AAC1B,aAAO,gBAAgB,IAAI,4BAA4B,GAAG;AAAA,IAC5D;AACA,SAAK,KAAK,QAAQ;AAAA,EACpB;AACA,SAAO,EAAE,SAAS,OAAO,SAAS,SAAS,SAAS,KAAK;AAC3D;;;ADrCA,SAAS,uBAA+B;AACtC,QAAM,WAAW,cAAc,IAAI,IAAI,mBAAmB,YAAY,GAAG,CAAC;AAC1E,QAAM,MAAM,KAAK,MAAM,aAAa,UAAU,MAAM,CAAC;AACrD,MAAI,OAAO,IAAI,YAAY,YAAY,IAAI,QAAQ,WAAW,GAAG;AAC/D,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACnD;AACA,SAAO,IAAI;AACb;AAEO,IAAM,eAAe,qBAAqB;","names":[]}