@moikapy/lich 0.5.1 → 0.6.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0 (unreleased)
4
+
5
+ - document a per-persona orchestrator example: one agent per NPC, the
6
+ webhook `POST /message` shape, and history serialization the game repo
7
+ copies. lich does not ship the service. see
8
+ examples/persona_orchestrator.
9
+ - document session JSONL as a combat log, with jq recipes for rationale,
10
+ ability use, vetoes, rejects, and `run_end` token totals. see
11
+ docs/user-guide/games.md.
12
+ - append a `run_end` meta record (`stopped_reason`, `usage`) on every
13
+ completed run so token spend is in the transcript. `budget_exhausted`
14
+ is still written when the stop reason is budget.
15
+ - add a display-only undead theme: frozen `lich` strings, `theme` / `--theme`, and `~/.lich/themes/<name>.json` that falls back to the built-in theme when missing or invalid.
16
+
3
17
  ## 0.5.1
4
18
 
5
19
  - fix the CLI so bun's global bin enters main (symlink argv no longer skips the entry).
package/README.md CHANGED
@@ -1,10 +1,29 @@
1
- # lich
1
+ # lich
2
2
 
3
3
  Lich is a TypeScript AI agent harness (library + CLI) that runs a
4
4
  Think-Act-Observe loop: an LLM plans, calls tools, observes results, and
5
5
  repeats until it produces a final answer. It ships with provider failover,
6
6
  tool guardrails, context compression, and JSONL session persistence.
7
7
 
8
+ ## Lore glossary
9
+
10
+ Lore names are prose only. Identifiers, config keys, event types, and tool
11
+ names do not change. The default system prompt is a myth-free behavior spec;
12
+ mythology lives in display strings only.
13
+
14
+ | Lore term | Actual concept | Where it appears |
15
+ | --- | --- | --- |
16
+ | **phylacteries** | JSONL session files in `.lich/sessions/` — conversations survive process death | This glossary; TUI `/sessions` listing label |
17
+ | **vessel-hopping** | Provider failover: 429/5xx retried with backoff, then the next provider takes over | This glossary |
18
+ | **the lair / wards** | `work_dir` confinement + `path_escape` guardrails | This glossary |
19
+ | **lair actions** | Plugin hooks that observe or veto tool calls | This glossary |
20
+ | **familiars** | Gateway adapters (webhook/telegram/discord/twitch) routing into one shared agent | This glossary |
21
+ | **spells** | Builtin tools in the registry | This glossary |
22
+ | **distillation** | Context compression: old turns summarized to fit the token budget | TUI compress notice |
23
+ | **the ritual is spent** | Turn-budget exhaustion | TUI + CLI budget notices |
24
+ | **dormant / deliberating / casting** | idle / thinking / tool phases | TUI status bar phase labels |
25
+ | **mortal** | The human user | TUI user transcript label |
26
+
8
27
  ## Documentation
9
28
 
10
29
  | Page | Contents |
@@ -15,6 +34,8 @@ tool guardrails, context compression, and JSONL session persistence.
15
34
  | [TUI guide](docs/user-guide/tui.md) | Launch, slash commands, status bar, memory semantics. |
16
35
  | [Gateway guide](docs/user-guide/gateway.md) | Webhook/Telegram/Discord/Twitch setup and the webhook API. |
17
36
  | [Library guide](docs/user-guide/library.md) | Embedding: `create_agent`, events, multi-turn history, errors. |
37
+ | [Games guide](docs/user-guide/games.md) | Session JSONL as a combat log, and jq recipes over it. |
38
+ | [Persona example](examples/persona_orchestrator/README.md) | Per-NPC agents the game repo copies. Not a second core. |
18
39
 
19
40
  ## Quick start (CLI)
20
41
 
@@ -184,6 +205,42 @@ lich tui
184
205
  Slash commands: `/help`, `/model`, `/usage`, `/clear`, `/sessions`,
185
206
  `/exit` (also `/quit`, `/q`). The transcript shows the newest 50 blocks.
186
207
 
208
+ ## Themes
209
+
210
+ Display strings come from one active theme per process. Set `"theme": "lich"`
211
+ in `.lich/config.json`, or pass `--theme <name>`. The built-in `lich` theme
212
+ is frozen data: `~/.lich/themes/lich.json` is ignored. Any other name is read
213
+ from `~/.lich/themes/<name>.json`. A missing file, invalid JSON, or a file
214
+ that fails the theme schema logs one warning and falls back to `lich`. Themes
215
+ do not change the system prompt, tool descriptions, event types, or slash
216
+ command names.
217
+
218
+ The built-in tagline is `the agent that will not stay dead`, and it appears once, in the TUI banner (`welcome`). Greppable keywords
219
+ stay in place: budget notices still start with `budget exhausted`, and
220
+ compression notices still start with `context compressed`.
221
+
222
+ ```json
223
+ {
224
+ "name": "vampire",
225
+ "agent_name": "vampire",
226
+ "glyph": "🦇",
227
+ "tagline": "night's clerk, unpaid",
228
+ "welcome": "🦇 vampire v{version} — night's clerk, unpaid · {model} ({kind})",
229
+ "goodbye": "dawn approaches",
230
+ "response_label": "vampire",
231
+ "user_label": "mortal",
232
+ "phase_labels": { "idle": "sleeping", "thinking": "scheming", "tool": "feeding" },
233
+ "notices": {
234
+ "budget_exhausted": "budget exhausted — the blood bank is dry (turn cap reached)",
235
+ "compressed": "context compressed — memories enthralled (summary {chars} chars)",
236
+ "sessions": "coffins ({count}):"
237
+ }
238
+ }
239
+ ```
240
+
241
+ `welcome` substitutes `{version}`, `{model}`, and `{kind}`. `notices.compressed`
242
+ substitutes `{chars}`; `notices.sessions` substitutes `{count}`.
243
+
187
244
  ## Development
188
245
 
189
246
  ```sh
@@ -1930,7 +1930,7 @@ var provider_schema = z.object({
1930
1930
  fetch_fn: z.custom(() => true).optional()
1931
1931
  }).passthrough();
1932
1932
  var agent_config_schema = z.object({
1933
- /** Display name used by the TUI banner. */
1933
+ /** Wizard label. The TUI banner uses the active theme welcome string. */
1934
1934
  agent_name: z.string().min(1).default("lich"),
1935
1935
  system_prompt: z.string().optional(),
1936
1936
  max_turns: z.number().int().min(1).default(25),
@@ -1946,7 +1946,8 @@ var agent_config_schema = z.object({
1946
1946
  /** Plugin entry module specifiers, relative to work_dir or absolute. */
1947
1947
  plugins: z.array(z.string()).default([]),
1948
1948
  gateway: gateway_schema,
1949
- log_level: z.enum(["debug", "info", "warn", "error"]).default("info")
1949
+ log_level: z.enum(["debug", "info", "warn", "error"]).default("info"),
1950
+ theme: z.string().min(1).default("lich")
1950
1951
  }).transform((config) => {
1951
1952
  const work_dir = config.work_dir ?? process.cwd();
1952
1953
  return {
@@ -3640,6 +3641,17 @@ function collect_usage(total) {
3640
3641
  function append_meta(handle, meta) {
3641
3642
  return handle.append({ ts: (/* @__PURE__ */ new Date()).toISOString(), kind: "meta", meta });
3642
3643
  }
3644
+ function append_run_end(handle, stopped_reason, usage_total) {
3645
+ return append_meta(handle, {
3646
+ event: "run_end",
3647
+ stopped_reason,
3648
+ usage: {
3649
+ prompt_tokens: usage_total.prompt_tokens,
3650
+ completion_tokens: usage_total.completion_tokens,
3651
+ total_tokens: usage_total.total_tokens
3652
+ }
3653
+ });
3654
+ }
3643
3655
  function register_plugin_tools(registry, plugins) {
3644
3656
  for (const loaded of plugins) {
3645
3657
  for (const tool of loaded.plugin.tools ?? []) {
@@ -3716,7 +3728,7 @@ var Agent = class {
3716
3728
  await this.call_plugin_run_end(outcome);
3717
3729
  }
3718
3730
  }
3719
- const session_path = await this.persist_session(outcome, options);
3731
+ const session_path = await this.persist_session(outcome, options, usage_total);
3720
3732
  const full_messages = [...options.history ?? [], ...outcome.messages];
3721
3733
  return { outcome, messages: full_messages, usage_total, session_path };
3722
3734
  }
@@ -3750,7 +3762,7 @@ var Agent = class {
3750
3762
  );
3751
3763
  }
3752
3764
  /** Best-effort JSONL transcript: never fails the run, returns undefined path on error. */
3753
- async persist_session(outcome, options) {
3765
+ async persist_session(outcome, options, usage_total) {
3754
3766
  try {
3755
3767
  const handle = await open_session(this.config.session_dir, options.label);
3756
3768
  await append_meta(handle, { event: "run_start", input_chars: options.input.length, history_size: outcome.messages.length });
@@ -3760,6 +3772,7 @@ var Agent = class {
3760
3772
  if (outcome.stopped_reason === "budget") {
3761
3773
  await append_meta(handle, { event: "budget_exhausted" });
3762
3774
  }
3775
+ await append_run_end(handle, outcome.stopped_reason, usage_total);
3763
3776
  return handle.path;
3764
3777
  } catch (error) {
3765
3778
  logger.warn("session persistence failed; continuing without transcript", error);
@@ -3806,4 +3819,4 @@ export {
3806
3819
  create_agent_with_plugins,
3807
3820
  run_agent
3808
3821
  };
3809
- //# sourceMappingURL=chunk-CV2YH3FH.js.map
3822
+ //# sourceMappingURL=chunk-7HLVKVIG.js.map