@litfamily/litopencode 1.0.37 โ†’ 1.0.38

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
@@ -5,6 +5,16 @@ are tracked separately from this product history.
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 1.0.38 - 2026-11-02
9
+
10
+ - Dependency alerts cleared: zod 4.6.6 (the runtime dependency and the Remotion cover template), the audio runtime's msgpack 1.2.3, @hono/node-server 2.1.5 in the HyperFrames cover template, and toml 5.0.1 in the development tree.
11
+ - Releases are faster: the publish workflow no longer repeats the test suite and the QA probes. It checks with GitHub that CI already passed on the exact commit being released and stops if it did not. A publish from your own machine still runs the full suite first.
12
+ - On OpenCode 2 the footer line beside the prompt now steps down with the terminal width instead of being clipped by the host: the ignition mark with the whole stage chip, the mark with a smaller chip, the chip alone, `๐Ÿ”ฅ 1/3`, the bare flame, then nothing. Before, a narrow terminal cut the mark mid-word and mangled the host's own figures next to it.
13
+ - On OpenCode 2 the status card shows the OpenCode Go plan windows (`5h 42% ยท 1w 18%`) for sessions on the `opencode-go` provider, read from the host's usage source and asked at most once a minute. Other providers and signed-out hosts show nothing, because OpenCode 2 gives a plugin no other usage data.
14
+ - The automatic-handoff notices now reach the screen on OpenCode 2. The server plugin cannot raise a toast there, so it leaves each notice in `.litopencode/notices.jsonl` and the TUI plugin shows it.
15
+ - Automatic handoff now fires on OpenCode 2. The plugin waited for a `session.status` or `session.idle` event that OpenCode 2 never publishes, so a turn that crossed the percent was never followed by the handoff request. It now reads the end of a turn from `session.execution.succeeded`, `failed` and `interrupted` (and the start from `session.execution.started`), which also records the session as idle for the status card.
16
+ - A flaky test of the alias goal path now waits for the ledger event instead of only the goal in the fake host, so slow runners no longer fail it.
17
+
8
18
  ## 1.0.37 - 2026-11-01
9
19
 
10
20
  - `lit-typographic-motion` films can now carry real keyed footage, such as a reaction clip or a spinning-cat loop shot on a green screen. Key it outside the renderer into 8-bit RGBA PNG frames, put them in `stage/clips/<folder>/` and list them in `stage/clips/clips.json` (`id`, `dir`, `count`, `fps`). The page loads `/lit/clips.js`, registers a clip once with `LitStage.clip({ parent, id, box })` and shows it from `render(t)`; the frame is picked from film time, so a cue is plain arithmetic and the determinism replay holds. A declared clip is checked against its bytes (12 clips, 360 frames each and 1500 in all, 40 MB each and 128 MB in all, 720 px on the long side, up to 30 fps, real transparency) and is exempt from the flipbook rule; any frame sequence you did not declare is still refused with exit 17. A cover takes no clips. The flash audit and the text checks run on the final pixels, and the report lists each clip. `LitStage.clip.circle`, `wipe` and `iris` keep working.
package/README-Ko-KR.md CHANGED
@@ -1,13 +1,13 @@
1
- <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/assets/cover-motion.webp" width="100%" alt="LitFamily ๋ชจ์…˜ ์ปค๋ฒ„: ์ผ๊ณฑ ๋กœ๋ด‡ ํŒจ๋„์ด ์ฐจ๋ก€๋กœ ์ผœ์ง€๊ณ , LitOpenCode ๋กœ๋ด‡์˜ ๋ˆˆ๊ณผ ํ…Œ๋‘๋ฆฌ๊ฐ€ ๋น›๋‚œ ๋’ค LITFAMILY์™€ KEEP THE WORK LIT. ๋ฌธ๊ตฌ๊ฐ€ ๋ฐ์•„์ง€๋Š” ์˜์ƒ" /></picture></p>
1
+ <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/cover-motion.webp" width="100%" alt="LitFamily ๋ชจ์…˜ ์ปค๋ฒ„: ์ผ๊ณฑ ๋กœ๋ด‡ ํŒจ๋„์ด ์ฐจ๋ก€๋กœ ์ผœ์ง€๊ณ , LitOpenCode ๋กœ๋ด‡์˜ ๋ˆˆ๊ณผ ํ…Œ๋‘๋ฆฌ๊ฐ€ ๋น›๋‚œ ๋’ค LITFAMILY์™€ KEEP THE WORK LIT. ๋ฌธ๊ตฌ๊ฐ€ ๋ฐ์•„์ง€๋Š” ์˜์ƒ" /></picture></p>
2
2
 
3
3
  # LitOpenCode
4
4
 
5
5
  **LitOpenCode๋Š” OpenCode์šฉ ์›Œํฌํ”Œ๋กœ ํ”Œ๋Ÿฌ๊ทธ์ธ์ž…๋‹ˆ๋‹ค. ํ”„๋กฌํ”„ํŠธ์— `lit`๋งŒ ๋ถ™์ด๋ฉด ๊ณ„ํšํ•˜๊ณ , ๋งŒ๋“ค๊ณ , ํ™•์ธํ•˜๊ณ , ๊ธฐ๋ก๊นŒ์ง€ ๋‚จ๊ฒจ์„œ ๋‹ค์Œ ์„ธ์…˜์ด ์–ด๋””์„œ ๋ฉˆ์ท„๋Š”์ง€ ๋ฐ”๋กœ ์••๋‹ˆ๋‹ค.**
6
6
 
7
7
  <p align="center">
8
- <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/assets/readme/badge-version.svg" alt="1.0.37" /></a>
8
+ <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/readme/badge-version.svg" alt="1.0.38" /></a>
9
9
  <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://img.shields.io/npm/dm/@litfamily/litopencode?label=downloads&color=D7F75B" alt="์›”๊ฐ„ npm ๋‹ค์šด๋กœ๋“œ ์ˆ˜" /></a>
10
- <a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/assets/readme/badge-license.svg" alt="MIT ๋ผ์ด์„ ์Šค" /></a>
10
+ <a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/readme/badge-license.svg" alt="MIT ๋ผ์ด์„ ์Šค" /></a>
11
11
  <a href="https://github.com/wjgoarxiv/litopencode"><img src="https://img.shields.io/github/stars/wjgoarxiv/litopencode?style=social" alt="GitHub ์Šคํƒ€" /></a>
12
12
  </p>
13
13
 
@@ -57,9 +57,9 @@ npm uninstall -g @litfamily/litopencode
57
57
  ## ๋” ๋ณด๊ธฐ
58
58
 
59
59
  - [GitHub์˜ ์‚ฌ์šฉ ์•ˆ๋‚ด](https://github.com/wjgoarxiv/litopencode/blob/main/docs/guide-Ko-KR.md): ์„ค์น˜ ์˜ต์…˜, ์ž๋™ ์ธ์ˆ˜์ธ๊ณ„, ๋ฌธ์ œ ํ•ด๊ฒฐ, ์ œ๊ฑฐ
60
- - [์›Œํฌํ”Œ๋กœ ์ƒ์„ธ ์•ˆ๋‚ด](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/reference-Ko-KR.md) ยท [English reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/reference.md)
61
- - [๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์•ˆ๋‚ด](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/migration.md) ยท [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/CHANGELOG.md) ยท [๊ฐœ์ธ์ •๋ณด์™€ ๋„คํŠธ์›Œํฌ ๋™์ž‘](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/privacy.md)
62
- - [LitFamily ํ—ˆ๋ธŒ](https://github.com/wjgoarxiv/litfamily) ยท [MIT ๋ผ์ด์„ ์Šค](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/LICENSE) ยท [์ƒํ‘œ ์ •์ฑ…](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/TRADEMARK.md) ยท [ASCII ๊ธ€๊ผด ๋ผ์ด์„ ์Šค](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/assets/readme/JetBrainsMono-OFL.txt)
60
+ - [์›Œํฌํ”Œ๋กœ ์ƒ์„ธ ์•ˆ๋‚ด](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/reference-Ko-KR.md) ยท [English reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/reference.md)
61
+ - [๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์•ˆ๋‚ด](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/migration.md) ยท [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/CHANGELOG.md) ยท [๊ฐœ์ธ์ •๋ณด์™€ ๋„คํŠธ์›Œํฌ ๋™์ž‘](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/privacy.md)
62
+ - [LitFamily ํ—ˆ๋ธŒ](https://github.com/wjgoarxiv/litfamily) ยท [MIT ๋ผ์ด์„ ์Šค](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/LICENSE) ยท [์ƒํ‘œ ์ •์ฑ…](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/TRADEMARK.md) ยท [ASCII ๊ธ€๊ผด ๋ผ์ด์„ ์Šค](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/readme/JetBrainsMono-OFL.txt)
63
63
 
64
64
  <details>
65
65
  <summary>ASCII ๋กœ๊ณ  ๋ณต์‚ฌ</summary>
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
- <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: seven armored robots power on one by one, the LitOpenCode robot wakes with glowing eyes and a lit frame, then LITFAMILY and KEEP THE WORK LIT. light up." /></picture></p>
1
+ <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: seven armored robots power on one by one, the LitOpenCode robot wakes with glowing eyes and a lit frame, then LITFAMILY and KEEP THE WORK LIT. light up." /></picture></p>
2
2
 
3
3
  # LitOpenCode
4
4
 
5
5
  **LitOpenCode is a workflow plugin for OpenCode. Add `lit` to a prompt and the work gets planned, built, checked and written down, so the next session knows where you stopped.**
6
6
 
7
7
  <p align="center">
8
- <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/assets/readme/badge-version.svg" alt="1.0.37" /></a>
8
+ <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/readme/badge-version.svg" alt="1.0.38" /></a>
9
9
  <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://img.shields.io/npm/dm/@litfamily/litopencode?label=downloads&color=D7F75B" alt="npm downloads per month" /></a>
10
- <a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
10
+ <a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
11
11
  <a href="https://github.com/wjgoarxiv/litopencode"><img src="https://img.shields.io/github/stars/wjgoarxiv/litopencode?style=social" alt="GitHub stars" /></a>
12
12
  </p>
13
13
 
@@ -57,9 +57,9 @@ npm uninstall -g @litfamily/litopencode
57
57
  ## More
58
58
 
59
59
  - [Guide on GitHub](https://github.com/wjgoarxiv/litopencode/blob/main/docs/guide.md): install options, automatic handoff, troubleshooting and removal.
60
- - [Workflow reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/reference.md)
61
- - [Migration notes](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/migration.md) ยท [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/CHANGELOG.md) ยท [Privacy and network behavior](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/privacy.md)
62
- - [LitFamily hub](https://github.com/wjgoarxiv/litfamily) ยท [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/LICENSE) ยท [Trademark policy](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/TRADEMARK.md) ยท [ASCII font license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.37/docs/assets/readme/JetBrainsMono-OFL.txt)
60
+ - [Workflow reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/reference.md)
61
+ - [Migration notes](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/migration.md) ยท [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/CHANGELOG.md) ยท [Privacy and network behavior](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/privacy.md)
62
+ - [LitFamily hub](https://github.com/wjgoarxiv/litfamily) ยท [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/LICENSE) ยท [Trademark policy](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/TRADEMARK.md) ยท [ASCII font license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/readme/JetBrainsMono-OFL.txt)
63
63
 
64
64
  <details>
65
65
  <summary>Copy ASCII logo</summary>
package/dist/state.d.ts CHANGED
@@ -18,6 +18,7 @@ export type RuntimePaths = {
18
18
  knowledgeDir: string;
19
19
  knowledgeClaimsFile: string;
20
20
  autoHandoffFile: string;
21
+ noticesFile: string;
21
22
  workflowStackDir: string;
22
23
  adhdDir: string;
23
24
  livenessDir: string;
package/dist/state.js CHANGED
@@ -34,6 +34,7 @@ export function createRuntimePaths(projectRoot) {
34
34
  knowledgeDir: path.join(runtimeDir, "knowledge"),
35
35
  knowledgeClaimsFile: path.join(runtimeDir, "knowledge", "claims.jsonl"),
36
36
  autoHandoffFile: path.join(runtimeDir, "auto-handoff.json"),
37
+ noticesFile: path.join(runtimeDir, "notices.jsonl"),
37
38
  workflowStackDir: path.join(runtimeDir, "workflow-stack"),
38
39
  adhdDir: path.join(runtimeDir, "adhd"),
39
40
  livenessDir: path.join(runtimeDir, "liveness"),
package/dist/tools.d.ts CHANGED
@@ -10,34 +10,44 @@ export declare const litOpenCodeTools: Readonly<{
10
10
  args: Readonly<{
11
11
  [k: string]: import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>;
12
12
  }>;
13
- execute(args: Record<string, unknown>, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
13
+ execute(args: {
14
+ [x: string]: unknown;
15
+ }, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
14
16
  };
15
17
  litwork: {
16
18
  description: string;
17
19
  args: Readonly<{
18
20
  [k: string]: import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>;
19
21
  }>;
20
- execute(args: Record<string, unknown>, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
22
+ execute(args: {
23
+ [x: string]: unknown;
24
+ }, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
21
25
  };
22
26
  "start-work": {
23
27
  description: string;
24
28
  args: Readonly<{
25
29
  [k: string]: import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>;
26
30
  }>;
27
- execute(args: Record<string, unknown>, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
31
+ execute(args: {
32
+ [x: string]: unknown;
33
+ }, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
28
34
  };
29
35
  "review-work": {
30
36
  description: string;
31
37
  args: Readonly<{
32
38
  [k: string]: import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>;
33
39
  }>;
34
- execute(args: Record<string, unknown>, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
40
+ execute(args: {
41
+ [x: string]: unknown;
42
+ }, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
35
43
  };
36
44
  wikify: {
37
45
  description: string;
38
46
  args: Readonly<{
39
47
  [k: string]: import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>;
40
48
  }>;
41
- execute(args: Record<string, unknown>, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
49
+ execute(args: {
50
+ [x: string]: unknown;
51
+ }, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
42
52
  };
43
53
  }>;
@@ -36,6 +36,13 @@ export type CardFigures = {
36
36
  readonly branch?: string;
37
37
  readonly inLane: boolean;
38
38
  readonly restartTo?: string;
39
+ readonly limits?: readonly CardLimit[];
40
+ };
41
+ /** A plan window the host reports (`5h 42%`), shown after the context figure; absent when the host reports none. */
42
+ export type CardLimit = {
43
+ readonly label: string;
44
+ readonly percent: number;
45
+ readonly limited: boolean;
39
46
  };
40
47
  export type CardInputs = {
41
48
  readonly view: MissionView;
@@ -150,6 +150,7 @@ function topLine(view, frame, palette, width, border, rest, mark) {
150
150
  }
151
151
  return fitLine([{ text: "โ•ญ" + "โ”€".repeat(Math.max(0, width)), fg: border }], width, palette);
152
152
  }
153
+ const limitWarnPercent = 90;
153
154
  /** The figures in the bottom border, most important first; the last ones go first when room runs out. */
154
155
  function figureParts(view, figures, palette) {
155
156
  const parts = [];
@@ -159,6 +160,9 @@ function figureParts(view, figures, palette) {
159
160
  const percent = Math.round(figures.contextPercent);
160
161
  parts.push({ text: `ctx ${percent}%`, fg: percent >= contextWarnPercent ? palette.warning : palette.muted });
161
162
  }
163
+ for (const limit of figures.limits ?? []) {
164
+ parts.push({ text: `${limit.label} ${limit.percent}%${limit.limited ? " limited" : ""}`, fg: limit.limited || limit.percent >= limitWarnPercent ? palette.warning : palette.muted });
165
+ }
162
166
  if (view.active)
163
167
  parts.push({ text: `โŸณ ${elapsedText(view.elapsedMinutes)}${figures.inLane ? " ยท in lane" : ""}`, fg: palette.muted });
164
168
  if (figures.branch !== undefined)
@@ -100,13 +100,13 @@ export declare function groupsWidth(groups: readonly ChipGroup[]): number;
100
100
  export declare function fade(segment: Segment, alpha: number, palette: MissionPalette): Segment;
101
101
  export declare const chipSparkWidth = 7;
102
102
  export declare function chipSparkCells(t: number, palette: MissionPalette): Segment[] | undefined;
103
- export declare function chipTier(view: MissionView, frame: MotionFrame, palette: MissionPalette, budget: number): ChipTier;
103
+ export declare function chipTier(view: MissionView, frame: MotionFrame, palette: MissionPalette, budget: number, largest?: ChipTier): ChipTier;
104
104
  /**
105
105
  * The map chip on the right of the prompt's agent/model row: `๐Ÿ”ฅ LIT plan โœ“ โ€บ WORK โ–ฐโ–ฐโ–ฐโ–ฑโ–ฑโ–ฑโ–ฑ 3/7 โ€บ review`,
106
106
  * then the live step when the full chip leaves room for it, shedding labels before values as `budget`
107
- * shrinks. Idle sessions render nothing.
107
+ * shrinks, and never larger than `largest`. Idle sessions render nothing.
108
108
  */
109
- export declare function chipLine(view: MissionView, frame: MotionFrame, palette: MissionPalette, budget: number): Line;
109
+ export declare function chipLine(view: MissionView, frame: MotionFrame, palette: MissionPalette, budget: number, largest?: ChipTier): Line;
110
110
  /** The header status: a glyph and words, or the COMPLETE badge that pops in and settles to green text. */
111
111
  export declare function statusSegments(view: MissionView, frame: MotionFrame, palette: MissionPalette): {
112
112
  readonly segments: Segment[];
@@ -396,8 +396,8 @@ export function chipSparkCells(t, palette) {
396
396
  }
397
397
  return cells;
398
398
  }
399
- export function chipTier(view, frame, palette, budget) {
400
- for (const tier of ["full", "medium"]) {
399
+ export function chipTier(view, frame, palette, budget, largest = "full") {
400
+ for (const tier of largest === "full" ? ["full", "medium"] : largest === "medium" ? ["medium"] : []) {
401
401
  if (groupsWidth(chipGroups(view, frame, palette, tier)) <= budget)
402
402
  return tier;
403
403
  }
@@ -406,12 +406,12 @@ export function chipTier(view, frame, palette, budget) {
406
406
  /**
407
407
  * The map chip on the right of the prompt's agent/model row: `๐Ÿ”ฅ LIT plan โœ“ โ€บ WORK โ–ฐโ–ฐโ–ฐโ–ฑโ–ฑโ–ฑโ–ฑ 3/7 โ€บ review`,
408
408
  * then the live step when the full chip leaves room for it, shedding labels before values as `budget`
409
- * shrinks. Idle sessions render nothing.
409
+ * shrinks, and never larger than `largest`. Idle sessions render nothing.
410
410
  */
411
- export function chipLine(view, frame, palette, budget) {
411
+ export function chipLine(view, frame, palette, budget, largest = "full") {
412
412
  if (!view.active)
413
413
  return [];
414
- const tier = chipTier(view, frame, palette, budget);
414
+ const tier = chipTier(view, frame, palette, budget, largest);
415
415
  const groups = chipGroups(view, frame, palette, tier);
416
416
  if (tier === "full") {
417
417
  const live = liveSegments(view, frame, palette, 24);
@@ -36,7 +36,7 @@ export type EventModelLike = {
36
36
  };
37
37
  export type V2EventOptions = {
38
38
  readonly projectRoot: string;
39
- readonly paths: Pick<RuntimePaths, "autoHandoffFile">;
39
+ readonly paths: Pick<RuntimePaths, "autoHandoffFile"> & Partial<Pick<RuntimePaths, "noticesFile">>;
40
40
  readonly autoHandoffConfig: AutoHandoffConfig;
41
41
  readonly boundedAuthority: BoundedAuthorityOptions;
42
42
  readonly getSession: SessionLookup;
package/dist/v2/events.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { createAutoHandoff } from "../auto-handoff-hooks.js";
2
2
  import { LifecycleConflictError, LifecycleSafetyError, createBoundedAuthorityLifecycle, parseFencedProgress } from "../bounded-authority.js";
3
3
  import { markLiveness } from "../session-liveness.js";
4
+ import { appendNotice } from "./notices.js";
4
5
  function isRecord(value) {
5
6
  return typeof value === "object" && value !== null && !Array.isArray(value);
6
7
  }
@@ -9,10 +10,20 @@ function str(value) {
9
10
  }
10
11
  /**
11
12
  * The calls the OpenCode 1 automatic handoff makes, answered by OpenCode 2. There is no server toast on
12
- * OpenCode 2, so the client has no `tui`; the handoff's own notices are not shown there.
13
+ * OpenCode 2, so `tui.showToast` leaves the notice in the project's notices file, which the TUI plugin
14
+ * reads and shows (notices.ts). Without a notices file the call is accepted and dropped.
13
15
  */
14
- function handoffClient(session, model, statuses) {
16
+ function handoffClient(session, model, statuses, noticesFile) {
15
17
  const client = {
18
+ tui: {
19
+ async showToast(input) {
20
+ const body = input.body;
21
+ if (noticesFile !== undefined && body !== undefined && typeof body.message === "string") {
22
+ appendNotice(noticesFile, { title: body.title ?? "๐Ÿ”ฅ LIT", message: body.message, variant: (body.variant ?? "info"), duration: body.duration ?? 8000 });
23
+ }
24
+ return { data: true };
25
+ }
26
+ },
16
27
  session: {
17
28
  async promptAsync(input) {
18
29
  const text = [input.body.system, ...(input.body.parts ?? []).map((part) => part.text)].filter((part) => typeof part === "string" && part !== "").join("\n\n");
@@ -50,7 +61,7 @@ export function createV2Events(options) {
50
61
  const autoHandoff = createAutoHandoff({
51
62
  projectRoot: options.projectRoot,
52
63
  paths: options.paths,
53
- client: handoffClient(options.session, options.model, statuses),
64
+ client: handoffClient(options.session, options.model, statuses, options.paths.noticesFile),
54
65
  config: options.autoHandoffConfig,
55
66
  getSession: options.getSession
56
67
  });
@@ -88,6 +99,11 @@ export function createV2Events(options) {
88
99
  throw error;
89
100
  }
90
101
  }
102
+ async function idle(sessionID) {
103
+ statuses.set(sessionID, { type: "idle" });
104
+ await markLiveness(options.projectRoot, sessionID, "idle");
105
+ await toHandoff("session.idle", { sessionID });
106
+ }
91
107
  async function handle(event) {
92
108
  const data = isRecord(event.data) ? event.data : {};
93
109
  const sessionID = str(data.sessionID);
@@ -135,13 +151,23 @@ export function createV2Events(options) {
135
151
  });
136
152
  return;
137
153
  }
154
+ // OpenCode 2.2.5 publishes no `session.status` or `session.idle`. A turn runs from
155
+ // `session.execution.started` to one of three endings, and the handoff reads them as busy and idle.
156
+ case "session.execution.started":
157
+ statuses.set(sessionID, { type: "busy" });
158
+ await toHandoff("session.status", { sessionID, status: { type: "busy" } });
159
+ return;
160
+ case "session.execution.succeeded":
161
+ case "session.execution.failed":
162
+ case "session.execution.interrupted":
163
+ await idle(sessionID);
164
+ return;
138
165
  case "session.status":
139
166
  statuses.set(sessionID, data.status);
140
167
  await toHandoff("session.status", { sessionID, status: data.status });
141
168
  return;
142
169
  case "session.idle":
143
- await markLiveness(options.projectRoot, sessionID, "idle");
144
- await toHandoff("session.idle", { sessionID });
170
+ await idle(sessionID);
145
171
  return;
146
172
  case "session.compaction.ended":
147
173
  await toHandoff("session.compacted", { sessionID });
@@ -0,0 +1,13 @@
1
+ export type NoticeVariant = "info" | "success" | "warning" | "error";
2
+ export type TuiNotice = {
3
+ readonly title: string;
4
+ readonly message: string;
5
+ readonly variant: NoticeVariant;
6
+ readonly duration: number;
7
+ };
8
+ /** Leaves one popup for the TUI. Best effort: a place that cannot be written loses the notice, never the caller. */
9
+ export declare function appendNotice(file: string, notice: TuiNotice): void;
10
+ /** Reads the notices that appear after it was made. Each call returns the new ones once, oldest first. */
11
+ export declare function createNoticeTail(file: string): {
12
+ readonly poll: () => readonly TuiNotice[];
13
+ };
@@ -0,0 +1,143 @@
1
+ // The way a server-side popup reaches the screen on OpenCode 2. The server plugin's context only subscribes to
2
+ // events, so it cannot raise a toast; the TUI plugin can, but it knows nothing of the server's state machines
3
+ // (the automatic handoff). The server therefore appends each popup it would have shown to a small file under
4
+ // the project's .litopencode, and the TUI reads the lines that appear after it started and toasts them. Both
5
+ // halves ship in this package; neither needs the other to be there. A symbolic link in place of the file is
6
+ // never followed, a torn line waits for its newline, and the file is rewritten to its newest lines past a cap.
7
+ import fs from "node:fs";
8
+ import path from "node:path";
9
+ const variants = new Set(["info", "success", "warning", "error"]);
10
+ const titleCells = 80;
11
+ const messageCells = 400;
12
+ const minDuration = 1000;
13
+ const maxDuration = 15_000;
14
+ const defaultDuration = 8000;
15
+ const capBytes = 64_000;
16
+ const keepBytes = 24_000;
17
+ const readCapBytes = 128_000;
18
+ const seenLimit = 64;
19
+ let counter = 0;
20
+ function clip(value, max) {
21
+ const chars = [...value];
22
+ return chars.length <= max ? value : `${chars.slice(0, max - 1).join("")}โ€ฆ`;
23
+ }
24
+ function clean(value) {
25
+ if (typeof value !== "object" || value === null)
26
+ return undefined;
27
+ const entry = value;
28
+ if (typeof entry.title !== "string" || typeof entry.message !== "string" || entry.message === "")
29
+ return undefined;
30
+ if (typeof entry.variant !== "string" || !variants.has(entry.variant))
31
+ return undefined;
32
+ const duration = typeof entry.duration === "number" && Number.isFinite(entry.duration) ? Math.min(maxDuration, Math.max(minDuration, entry.duration)) : defaultDuration;
33
+ return { title: clip(entry.title, titleCells), message: clip(entry.message, messageCells), variant: entry.variant, duration };
34
+ }
35
+ function plainFile(file) {
36
+ try {
37
+ const stat = fs.lstatSync(file);
38
+ return stat.isFile() ? stat : undefined;
39
+ }
40
+ catch {
41
+ return undefined;
42
+ }
43
+ }
44
+ function trimToNewest(file) {
45
+ const text = fs.readFileSync(file, "utf8");
46
+ const lines = text.split("\n").filter((line) => line !== "");
47
+ const kept = [];
48
+ let size = 0;
49
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
50
+ size += Buffer.byteLength(lines[index]) + 1;
51
+ if (size > keepBytes)
52
+ break;
53
+ kept.unshift(lines[index]);
54
+ }
55
+ const temp = `${file}.${process.pid}.tmp`;
56
+ fs.writeFileSync(temp, kept.length === 0 ? "" : `${kept.join("\n")}\n`);
57
+ fs.renameSync(temp, file);
58
+ }
59
+ /** Leaves one popup for the TUI. Best effort: a place that cannot be written loses the notice, never the caller. */
60
+ export function appendNotice(file, notice) {
61
+ const cleaned = clean(notice);
62
+ if (cleaned === undefined)
63
+ return;
64
+ try {
65
+ fs.mkdirSync(path.dirname(file), { recursive: true });
66
+ const existing = (() => {
67
+ try {
68
+ return fs.lstatSync(file);
69
+ }
70
+ catch {
71
+ return undefined;
72
+ }
73
+ })();
74
+ if (existing !== undefined && !existing.isFile())
75
+ return;
76
+ counter += 1;
77
+ const stored = { id: `${Date.now().toString(36)}-${process.pid}-${counter}`, at: Date.now(), ...cleaned };
78
+ fs.appendFileSync(file, `${JSON.stringify(stored)}\n`);
79
+ if ((plainFile(file)?.size ?? 0) > capBytes)
80
+ trimToNewest(file);
81
+ }
82
+ catch { // no-excuse-ok: catch -- a popup is best-effort feedback; the handoff itself must never stop for it.
83
+ }
84
+ }
85
+ /** Reads the notices that appear after it was made. Each call returns the new ones once, oldest first. */
86
+ export function createNoticeTail(file) {
87
+ let offset = plainFile(file)?.size ?? 0;
88
+ const seen = [];
89
+ return {
90
+ poll() {
91
+ const stat = plainFile(file);
92
+ if (stat === undefined)
93
+ return [];
94
+ // A smaller file was rewritten to its newest lines; read it again and let the ids drop what was shown.
95
+ if (stat.size < offset)
96
+ offset = 0;
97
+ if (stat.size === offset)
98
+ return [];
99
+ const length = Math.min(stat.size - offset, readCapBytes);
100
+ const buffer = Buffer.alloc(length);
101
+ let fd;
102
+ try {
103
+ fd = fs.openSync(file, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
104
+ fs.readSync(fd, buffer, 0, length, offset);
105
+ }
106
+ catch {
107
+ return [];
108
+ }
109
+ finally {
110
+ if (fd !== undefined)
111
+ fs.closeSync(fd);
112
+ }
113
+ const text = buffer.toString("utf8");
114
+ const end = text.lastIndexOf("\n");
115
+ if (end < 0)
116
+ return length === readCapBytes ? (offset += length, []) : [];
117
+ offset += Buffer.byteLength(text.slice(0, end + 1));
118
+ const out = [];
119
+ for (const line of text.slice(0, end).split("\n")) {
120
+ let parsed;
121
+ try {
122
+ parsed = JSON.parse(line);
123
+ }
124
+ catch {
125
+ continue;
126
+ }
127
+ const id = typeof parsed?.id === "string" ? parsed.id : undefined;
128
+ if (id !== undefined && seen.includes(id))
129
+ continue;
130
+ const entry = clean(parsed);
131
+ if (entry === undefined)
132
+ continue;
133
+ if (id !== undefined) {
134
+ seen.push(id);
135
+ if (seen.length > seenLimit)
136
+ seen.shift();
137
+ }
138
+ out.push(entry);
139
+ }
140
+ return out;
141
+ }
142
+ };
143
+ }
@@ -0,0 +1,20 @@
1
+ import type { MissionView } from "../tui/mission-model.ts";
2
+ import type { MotionFrame } from "../tui/mission-motion.ts";
3
+ import { type Line, type MissionPalette } from "../tui/mission-text.ts";
4
+ /** The session sidebar, which a wide terminal is taken to have open (the setting is not readable by a plugin). */
5
+ export declare const sidebarCells = 42;
6
+ export declare function footerBudget(input: {
7
+ readonly columns: number;
8
+ readonly sidebar: boolean;
9
+ readonly busy: boolean;
10
+ }): number;
11
+ export type FooterInput = {
12
+ /** The discipline the latest lit prompt started, as the server wrote it into the message. */
13
+ readonly name: string;
14
+ readonly view: MissionView | undefined;
15
+ readonly frame: MotionFrame;
16
+ readonly palette: MissionPalette;
17
+ readonly budget: number;
18
+ };
19
+ /** The footer line for `budget` cells, as segments; empty when not even the flame fits. */
20
+ export declare function footerStatus(input: FooterInput): Line;
@@ -0,0 +1,56 @@
1
+ // What OpenCode 2's prompt footer row holds besides our line, and how our line steps down to the room left.
2
+ // The row is the host's spinner and `esc interrupt` hint on the left and its token, goal and `ctrl+p commands`
3
+ // figures on the right; our line is squeezed between them, and the host clips it mid-word when it is too long.
4
+ // So the line is chosen by width: the ignition mark with the whole stage chip, the chip at a smaller size,
5
+ // the chip as `๐Ÿ”ฅ 1/3`, the bare flame, then nothing. The numbers were measured on opencode2 2.2.5.
6
+ import { activationDiscipline } from "../activation-probe.js";
7
+ import { cellWidth, chipLine, lineWidth } from "../tui/mission-text.js";
8
+ /** The session sidebar, which a wide terminal is taken to have open (the setting is not readable by a plugin). */
9
+ export const sidebarCells = 42;
10
+ // Right of our line: with a turn running `โ†“ 1 shell ยท โ—Ž goal ctrl+p commands`, idle `โ—Ž goal paused ยท 42.3K (8%) ยท $0.07 ctrl+p commands`.
11
+ const hostRightBusy = 38;
12
+ const hostRightIdle = 54;
13
+ // Left of it, the least the host keeps: the spinner and a shortened `esc โ€ฆrrupt` while running, a shortened path when idle.
14
+ const hostLeftBusy = 24;
15
+ const hostLeftIdle = 6;
16
+ const gap = 2;
17
+ export function footerBudget(input) {
18
+ const main = input.columns - (input.sidebar ? sidebarCells : 0);
19
+ const host = input.busy ? hostRightBusy + hostLeftBusy : hostRightIdle + hostLeftIdle;
20
+ return Math.max(0, main - host);
21
+ }
22
+ const flameWidth = 2;
23
+ /** The footer line for `budget` cells, as segments; empty when not even the flame fits. */
24
+ export function footerStatus(input) {
25
+ const { palette, budget } = input;
26
+ const discipline = activationDiscipline(input.name);
27
+ const mark = (text) => [{ text, fg: palette.primary, bold: true }];
28
+ const full = `๐Ÿ”ฅ LIT IGNITED ยท ${discipline}`;
29
+ const active = input.view !== undefined && input.view.active;
30
+ const chipFor = (room, largest) => {
31
+ if (!active || input.view === undefined || room < flameWidth)
32
+ return [];
33
+ const chip = chipLine(input.view, input.frame, palette, room, largest);
34
+ return lineWidth(chip) <= room ? chip : [];
35
+ };
36
+ if (!active) {
37
+ if (cellWidth(full) <= budget)
38
+ return mark(full);
39
+ }
40
+ else {
41
+ const beside = chipFor(budget - cellWidth(full) - gap, "full");
42
+ if (beside.length > 0)
43
+ return [...mark(full), { text: " ".repeat(gap), fg: palette.muted }, ...beside];
44
+ // The chip is worth more than the mark's words, so it stays alone when both do not fit. Alone it never
45
+ // grows past the middle size, so the line only gets smaller as the room does.
46
+ const alone = chipFor(budget, "medium");
47
+ if (alone.length > 0)
48
+ return alone;
49
+ }
50
+ const short = `๐Ÿ”ฅ ${discipline}`;
51
+ if (cellWidth(short) <= budget)
52
+ return mark(short);
53
+ if (budget >= flameWidth)
54
+ return mark("๐Ÿ”ฅ");
55
+ return [];
56
+ }
package/dist/v2/tui.d.ts CHANGED
@@ -38,6 +38,12 @@ export type TuiContextLike = {
38
38
  } | undefined;
39
39
  readonly theme: unknown;
40
40
  readonly themeMode: "dark" | "light";
41
+ /** The host's API client; only `usage.limits` is read, and a host without it simply shows no plan figures. */
42
+ readonly client?: {
43
+ readonly usage?: {
44
+ readonly limits?: () => Promise<unknown>;
45
+ };
46
+ };
41
47
  readonly attention?: {
42
48
  readonly notify: (options: {
43
49
  readonly title?: string;
package/dist/v2/tui.js CHANGED
@@ -13,15 +13,19 @@ import { micro, supportsMarkGlyphs } from "../lit-mark.js";
13
13
  import { readInstalledVersions, restartVersion } from "../restart-notice.js";
14
14
  import { buildMissionView, lastMission, leadingNotice, missionNotices } from "../tui/mission-model.js";
15
15
  import { idleMotion, motionTiming, planMotion, sampleMotion } from "../tui/mission-motion.js";
16
- import { latestLitMarkV2, missionStateFromV2 } from "../tui/mission-source-v2.js";
16
+ import { latestLitMarkV2, missionStateFromV2, sessionMessagesV2 } from "../tui/mission-source-v2.js";
17
17
  import { adhdSuggestDue, createLedgerReader, createWorkReader, earlierWorkWindowMs, hasEarlierWork, messageTimes, missionProjectRoot, readCardInput, readLatestResearchRun, readMissionInput, readThreadSources, wayBackDue } from "../tui/mission-source.js";
18
18
  import { chipShimmer, idleChipMotion, planChipMotion } from "../tui/mission-skills.js";
19
19
  import { bandCard, readBandOptions, readNotifyOptions, statusCard } from "../tui/mission-band.js";
20
20
  import { activeSkillLook, turnGlow, waitingOnYou } from "../tui/mission-looks.js";
21
21
  import { rainbowActive, rainbowAllowed, rainbowAt, rainbowShows, rainbowTickMs } from "../tui/mission-rainbow.js";
22
22
  import { threadLineCount, threadsOf } from "../tui/mission-threads.js";
23
- import { chipLine, cellWidth, homeLine, lineWidth, missionPanel, paletteFromTheme, panelWidth } from "../tui/mission-text.js";
23
+ import { homeLine, missionPanel, paletteFromTheme, panelWidth } from "../tui/mission-text.js";
24
+ import { createRuntimePaths } from "../state.js";
25
+ import { footerBudget, footerStatus, sidebarCells } from "./tui-footer.js";
26
+ import { createNoticeTail } from "./notices.js";
24
27
  import { v2StageNotices } from "./tui-notices.js";
28
+ import { createUsageLimits } from "./usage-limits.js";
25
29
  const refreshDelayMs = 80;
26
30
  const ledgerPollMs = 2000;
27
31
  const toastMs = 4000;
@@ -33,11 +37,12 @@ const homeLineWidth = 69;
33
37
  // A lit mark older than the moment a session is first drawn is history being loaded, not a new turn.
34
38
  const historySlackMs = 2000;
35
39
  const defaultWidth = 120;
36
- // What the footer row holds besides our line: the host's spinner and hint on the left, its token, cost,
37
- // goal and `ctrl+p commands` figures on the right (measured with a turn running, when they are widest).
38
- const hostFooterCells = 68;
39
- const sidebarCells = 42;
40
40
  const sidebarMinWidth = 120;
41
+ // The plan windows change slowly; asking the host more than once a minute would only add requests.
42
+ const limitsPollMs = 60_000;
43
+ // Only the OpenCode Go allowance has a source on OpenCode 2, so the windows belong to a Go session alone.
44
+ const goProviderID = "opencode-go";
45
+ const noticeBurst = 3;
41
46
  async function hostRuntime() {
42
47
  const [jsxRuntime, solid, opentui] = await Promise.all([import("@opentui/solid/jsx-runtime"), import("solid-js"), import("@opentui/solid")]);
43
48
  return {
@@ -95,6 +100,27 @@ export async function setupLitOpenCodeTui(context, runtime) {
95
100
  let liveTimer;
96
101
  let bandTimer;
97
102
  let restartTimer;
103
+ const readUsage = context.client?.usage?.limits;
104
+ const limits = createUsageLimits({
105
+ read: readUsage === undefined ? undefined : () => readUsage.call(context.client?.usage),
106
+ now: Date.now,
107
+ intervalMs: limitsPollMs,
108
+ onChange: () => setRevision(revision() + 1)
109
+ });
110
+ const noticeTail = createNoticeTail(root === undefined ? "" : createRuntimePaths(root).noticesFile);
111
+ // The provider of the session's latest assistant message decides whether the Go allowance applies to it.
112
+ function goSession(sessionID) {
113
+ const latest = sessionMessagesV2(context.data, sessionID).filter((message) => message.type === "assistant").at(-1);
114
+ return latest?.model?.providerID === goProviderID;
115
+ }
116
+ // Popups the server could not raise (the automatic handoff) arrive as lines in the notices file.
117
+ function showServerNotices() {
118
+ if (disposed)
119
+ return;
120
+ for (const notice of noticeTail.poll().slice(-noticeBurst)) {
121
+ context.ui.toast.show({ title: notice.title, message: notice.message, variant: notice.variant, duration: notice.duration });
122
+ }
123
+ }
98
124
  const palette = () => paletteFromV2Theme(context.theme, context.themeMode);
99
125
  function span(segment) {
100
126
  return ui.jsx("span", {
@@ -264,6 +290,8 @@ export async function setupLitOpenCodeTui(context, runtime) {
264
290
  if (mark !== undefined && mark.at >= (trackedAt.get(sessionID) ?? 0) - historySlackMs)
265
291
  ignitionToast(sessionID, mark.name);
266
292
  }
293
+ if (goSession(sessionID))
294
+ void limits.poll();
267
295
  const entry = sessions.get(sessionID);
268
296
  const { next, changed, notices } = build(sessionID, entry, now);
269
297
  if (!changed)
@@ -299,6 +327,7 @@ export async function setupLitOpenCodeTui(context, runtime) {
299
327
  return;
300
328
  for (const sessionID of tracked)
301
329
  refresh(sessionID);
330
+ showServerNotices();
302
331
  syncPoll();
303
332
  }
304
333
  function scheduleRefresh() {
@@ -379,6 +408,7 @@ export async function setupLitOpenCodeTui(context, runtime) {
379
408
  contextPercent: current.card.contextPercent,
380
409
  ...(current.card.branch === undefined ? {} : { branch: current.card.branch }),
381
410
  inLane,
411
+ ...(goSession(sessionID) && limits.windows().length > 0 ? { limits: limits.windows() } : {}),
382
412
  ...(restartTo() === undefined ? {} : { restartTo: restartTo() })
383
413
  },
384
414
  ...(last === undefined ? {} : { last })
@@ -415,19 +445,24 @@ export async function setupLitOpenCodeTui(context, runtime) {
415
445
  const mark = latestLitMarkV2(context.data, sessionID);
416
446
  if (mark === undefined)
417
447
  return null;
418
- const ignition = ignitionLine(mark.name);
419
448
  const current = snapshot(sessionID);
420
449
  // The sidebar setting is not readable here, so a wide terminal is taken to have it open.
421
- const sidebar = parentOf(sessionID) === undefined && columns() > sidebarMinWidth ? sidebarCells : 0;
422
- const budget = columns() - sidebar - hostFooterCells - cellWidth(ignition);
423
- const drawn = current === undefined || !current.view.active ? [] : chipLine(current.view, current.frame, palette(), budget);
424
- const chip = lineWidth(drawn) > budget ? [] : drawn;
450
+ const sidebar = parentOf(sessionID) === undefined && columns() > sidebarMinWidth;
451
+ const footer = footerStatus({
452
+ name: mark.name,
453
+ view: current?.view,
454
+ frame: current?.frame ?? sampleMotion(idleMotion, clock()),
455
+ palette: palette(),
456
+ budget: footerBudget({ columns: columns(), sidebar, busy: current?.card.busy === true })
457
+ });
458
+ if (footer.length === 0)
459
+ return null;
425
460
  return ui.jsx("box", {
426
461
  flexShrink: 1,
427
462
  minWidth: 0,
428
463
  overflow: "hidden",
429
464
  flexDirection: "row",
430
- children: [line([{ text: ignition, fg: palette().primary, bold: true }]), ...(chip.length === 0 ? [] : [line([{ text: " ", fg: palette().muted }]), line(chip)])]
465
+ children: line(footer)
431
466
  });
432
467
  };
433
468
  }
@@ -0,0 +1,19 @@
1
+ export type LimitWindow = {
2
+ readonly label: "5h" | "1w" | "1mo";
3
+ readonly percent: number;
4
+ readonly limited: boolean;
5
+ };
6
+ /** Reads the usage-limits reply (`{ data: Info | null }` or the bare Info) into the windows worth drawing. */
7
+ export declare function limitWindows(reply: unknown): readonly LimitWindow[];
8
+ export type UsageLimitsOptions = {
9
+ /** Asks the host; absent when the host has no usage source. */
10
+ readonly read: (() => Promise<unknown>) | undefined;
11
+ readonly now: () => number;
12
+ readonly intervalMs: number;
13
+ readonly onChange: () => void;
14
+ };
15
+ /** A throttled reader: it asks at most once per interval, and a failed or empty answer clears the figures. */
16
+ export declare function createUsageLimits(options: UsageLimitsOptions): {
17
+ readonly windows: () => readonly LimitWindow[];
18
+ readonly poll: () => Promise<void>;
19
+ };
@@ -0,0 +1,70 @@
1
+ // The plan windows OpenCode 2 can tell a plugin about. Its one usage source is client.usage.limits(): the
2
+ // OpenCode Go allowance of the Console workspace the location is signed in to, as a rolling window (five
3
+ // hours), a weekly one and a monthly one, each with a percent used; the reply is null when nobody is signed
4
+ // in. There are no provider response headers or rate-limit events for a plugin to read, so for any other
5
+ // provider this module has nothing to show, and the card draws no figure rather than a made-up one.
6
+ /** The monthly window is shown only when it is close to the cap, so the card keeps LitClaude's 5h and 1w pair. */
7
+ const monthlyShowPercent = 90;
8
+ function record(value) {
9
+ return typeof value === "object" && value !== null ? value : undefined;
10
+ }
11
+ function window(label, raw) {
12
+ const meter = record(raw);
13
+ const percent = meter?.percent;
14
+ if (typeof percent !== "number" || !Number.isFinite(percent))
15
+ return undefined;
16
+ return { label, percent: Math.round(Math.min(100, Math.max(0, percent))), limited: meter?.limited === true };
17
+ }
18
+ /** Reads the usage-limits reply (`{ data: Info | null }` or the bare Info) into the windows worth drawing. */
19
+ export function limitWindows(reply) {
20
+ const outer = record(reply);
21
+ const info = outer === undefined ? undefined : "data" in outer ? record(outer.data) : outer;
22
+ const allowance = record(info?.go);
23
+ if (allowance === undefined)
24
+ return [];
25
+ const rolling = window("5h", allowance.rolling);
26
+ const weekly = window("1w", allowance.weekly);
27
+ const monthly = window("1mo", allowance.monthly);
28
+ return [
29
+ ...(rolling === undefined ? [] : [rolling]),
30
+ ...(weekly === undefined ? [] : [weekly]),
31
+ ...(monthly !== undefined && (monthly.limited || monthly.percent >= monthlyShowPercent) ? [monthly] : [])
32
+ ];
33
+ }
34
+ /** A throttled reader: it asks at most once per interval, and a failed or empty answer clears the figures. */
35
+ export function createUsageLimits(options) {
36
+ let current = [];
37
+ let askedAt;
38
+ let pending;
39
+ async function ask(read) {
40
+ let next;
41
+ try {
42
+ next = limitWindows(await read());
43
+ }
44
+ catch { // no-excuse-ok: catch -- an unreachable or signed-out host means there is nothing to show, never an error on screen.
45
+ next = [];
46
+ }
47
+ if (JSON.stringify(next) === JSON.stringify(current))
48
+ return;
49
+ current = next;
50
+ options.onChange();
51
+ }
52
+ return {
53
+ windows: () => current,
54
+ poll() {
55
+ const read = options.read;
56
+ if (read === undefined)
57
+ return Promise.resolve();
58
+ if (pending !== undefined)
59
+ return pending;
60
+ const now = options.now();
61
+ if (askedAt !== undefined && now - askedAt < options.intervalMs)
62
+ return Promise.resolve();
63
+ askedAt = now;
64
+ pending = ask(read).finally(() => {
65
+ pending = undefined;
66
+ });
67
+ return pending;
68
+ }
69
+ };
70
+ }
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.37"><title>release: 1.0.37</title><g shape-rendering="crispEdges"><rect width="49" height="20" fill="#080d14"/><rect x="49" width="97" height="20" fill="#ff6337"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text x="255" y="140" textLength="390" transform="scale(.1)">release</text><text x="965" y="140" textLength="870" transform="scale(.1)" fill="#080D14">1.0.37</text></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.38"><title>release: 1.0.38</title><g shape-rendering="crispEdges"><rect width="49" height="20" fill="#080d14"/><rect x="49" width="97" height="20" fill="#ff6337"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text x="255" y="140" textLength="390" transform="scale(.1)">release</text><text x="965" y="140" textLength="870" transform="scale(.1)" fill="#080D14">1.0.38</text></g></svg>
@@ -384,14 +384,25 @@ Code Mode ๋ฐ–, ๋ชจ๋ธ์˜ ๋„๊ตฌ ๋ชฉ๋ก์— ๋ฐ”๋กœ ์žˆ์œผ๋ฏ€๋กœ `lit-plan`๋„ ๊ณ„
384
384
  ๋”ํ•ด OpenCode 2์—๋งŒ ์žˆ๋Š” ์„ธ ๊ฐ€์ง€๋ฅผ ๋„์›๋‹ˆ๋‹ค. ์ž‘์—… ๋‹จ๊ณ„๊ฐ€ ๋„˜์–ด๊ฐˆ ๋•Œ(`๐Ÿ”ฅ Stage`, `work 3/7`), ๊ทœ์œจ์ด ๋ฐ”๋€” ๋•Œ
385
385
  (`๐Ÿ”ฅ lit-loop โ†’ start-work`), ๋ ˆ์ธ์ด ์‹œ์ž‘๋˜๊ฑฐ๋‚˜ ๋๋‚  ๋•Œ์ž…๋‹ˆ๋‹ค. ํ•œ๊บผ๋ฒˆ์— ์ผ์–ด๋‚œ ์ผ์€ `+N more`๊ฐ€ ๋ถ™์€ ํ† ์ŠคํŠธ
386
386
  ํ•˜๋‚˜๋กœ ํ•ฉ์นฉ๋‹ˆ๋‹ค. ์žฌ์‹œ์ž‘ ์•ˆ๋‚ด, `lit adhd` ์ œ์•ˆ, ์ผœ ๋‘๋ฉด ๋‚˜์˜ค๋Š” ์™„๋ฃŒ ์•Œ๋ฆผ์€ OpenCode 1๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„๋งŒ ์•Œ๋˜
387
- ์ž๋™ ์ธ์ˆ˜์ธ๊ณ„ ์•Œ๋ฆผ๊ณผ ์Šคํ‚ฌ ํžŒํŠธ๋Š” ๊ทธ๊ณณ์— ๋‚˜์˜ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
387
+ ์ž๋™ ์ธ์ˆ˜์ธ๊ณ„ ์•Œ๋ฆผ์€ `.litopencode/notices.jsonl`์„ ๊ฑฐ์ณ ํ™”๋ฉด์— ๋‚˜์˜ต๋‹ˆ๋‹ค. OpenCode 2 ์„œ๋ฒ„ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์ด๋ฒคํŠธ๋ฅผ
388
+ ๊ตฌ๋…ํ•  ์ˆ˜๋งŒ ์žˆ๊ณ  ํ† ์ŠคํŠธ๋ฅผ ๋„์šธ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ, ์„œ๋ฒ„๊ฐ€ ์•Œ๋ฆผ์„ ๊ทธ ํŒŒ์ผ์— ํ•œ ์ค„์”ฉ ๋ง๋ถ™์ด๋ฉด TUI ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์‹œ์ž‘ํ•œ ๋’ค์—
389
+ ์ƒ๊ธด ์ค„์„ ํ† ์ŠคํŠธ๋กœ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค(ํŒŒ์ผ ํฌ๊ธฐ์— ์ƒํ•œ์ด ์žˆ๊ณ  ์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋Š” ๋”ฐ๋ผ๊ฐ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค). ์„œ๋ฒ„๊ฐ€ ๋„์šฐ๋˜ ๋‚˜๋จธ์ง€
390
+ ํ•˜๋‚˜์ธ ์ ํ™” ํ† ์ŠคํŠธ๋Š” ์ด๋ฏธ TUI๊ฐ€ ์ง์ ‘ ๋„์šฐ๋ฏ€๋กœ, ๋”ฐ๋กœ ์˜ฎ๊ธธ ์Šคํ‚ฌ ํžŒํŠธ๋Š” ๋‚จ์•„ ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
388
391
  - *TUI.* HUD๋Š” OpenCode 2๊ฐ€ ๊ณต๊ฐœํ•œ ์Šฌ๋กฏ์— ๋†“์ž…๋‹ˆ๋‹ค. ์ƒํƒœ ์นด๋“œ๋Š” `app` ์Šฌ๋กฏ์—์„œ ์„ธ์…˜๊ณผ ์‚ฌ์ด๋“œ๋ฐ” ์•„๋ž˜์— ๊ทธ๋ ค์ง€๊ณ ,
389
392
  ์‰ฌ๋“  ์ผํ•˜๋“  ๋„ค ์ค„์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ๊ณ„ ์นฉ์€ ์—์ด์ „ํŠธยท๋ชจ๋ธ ์ค„ ์˜ค๋ฅธ์ชฝ์— ์Šฌ๋กฏ์ด ์—†์–ด์„œ ์ ํ™” ์ค„๊ณผ ํ•จ๊ป˜
390
393
  `prompt.footer.status`์— ์žˆ๊ณ , ํ™ˆ ์ค„์€ `home.footer`, ๋ฏธ์…˜ ํŒจ๋„์€ `sidebar.content`์— ์žˆ์œผ๋ฏ€๋กœ ์‚ฌ์ด๋“œ๋ฐ”๋ฅผ ์—ด์–ด
391
394
  ๋‘๋ฉด ๋ณด์ž…๋‹ˆ๋‹ค. ์นด๋“œ, ์นฉ, ํŒจ๋„, ํ™ˆ ์ค„์€ OpenCode 1๊ณผ ๊ฐ™์€ ๊ธ€์ž ๋นŒ๋”๋ฅผ ์”๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ ํ•ญ๋ชฉ์˜ `band`, `notify`
392
- ์˜ต์…˜์€ OpenCode 2๊ฐ€ ๋„˜๊ฒจ ์ฃผ๋Š” ์˜ต์…˜์—์„œ ์ฝ์Šต๋‹ˆ๋‹ค. OpenCode 2๋Š” ํ•  ์ผ ๋ชฉ๋ก์„ ๋‘์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฏธ์…˜ ์ง„ํ–‰์€ ledger์™€
395
+ ์˜ต์…˜์€ OpenCode 2๊ฐ€ ๋„˜๊ฒจ ์ฃผ๋Š” ์˜ต์…˜์—์„œ ์ฝ์Šต๋‹ˆ๋‹ค. ํ‘ธํ„ฐ ์ค„์€ ํ˜ธ์ŠคํŠธ๊ฐ€ ์ž˜๋ผ ๋ฒ„๋ฆฌ๊ฒŒ ๋‘์ง€ ์•Š๊ณ  ํ„ฐ๋ฏธ๋„ ๋„ˆ๋น„์— ๋งž์ถฐ
396
+ ๋‹จ๊ณ„์ ์œผ๋กœ ์ค„์–ด๋“ญ๋‹ˆ๋‹ค. ํ‘œ์‹œ์™€ ์ „์ฒด ์นฉ, ํ‘œ์‹œ์™€ ์ž‘์€ ์นฉ, ์นฉ๋งŒ, `๐Ÿ”ฅ 1/3`, ๋ถˆ๊ฝƒ๋งŒ, ์•„๋ฌด๊ฒƒ๋„ ์—†์Œ ์ˆœ์„œ์ž…๋‹ˆ๋‹ค. ์“ธ ์ˆ˜ ์žˆ๋Š”
397
+ ๋„ˆ๋น„๋Š” ์ „์ฒด ๋„ˆ๋น„์—์„œ ์‚ฌ์ด๋“œ๋ฐ”(120์นธ์„ ๋„˜์œผ๋ฉด ์—ด๋ ค ์žˆ๋‹ค๊ณ  ๊ฐ€์ •)์™€ ํ˜ธ์ŠคํŠธ ์ž์‹ ์˜ ์Šคํ”ผ๋„ˆยท์ˆ˜์น˜๋ฅผ ๋บ€ ๊ฐ’์ž…๋‹ˆ๋‹ค.
398
+ OpenCode 2๋Š” ํ•  ์ผ ๋ชฉ๋ก์„ ๋‘์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฏธ์…˜ ์ง„ํ–‰์€ ledger์™€
393
399
  ์ž‘์—… ๊ธฐ๋ก๊ณผ ๊ณ„ํš์—์„œ ์ฝ์Šต๋‹ˆ๋‹ค. ๋„๊ตฌ ์ด๋ฆ„๋„ ๋‹ค๋ฆ…๋‹ˆ๋‹ค(`shell`, `subagent`, `id`๋ฅผ ๋ฐ›๋Š” `skill` ๋„๊ตฌ). ์นด๋“œ๋Š” ๊ทธ
394
- ์ด๋ฆ„์„ ์ฝ์Šต๋‹ˆ๋‹ค. OpenCode 2๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์— ์š”๊ธˆ์ œ ํ•œ๋„ ์ˆ˜์น˜๋ฅผ ์ฃผ์ง€ ์•Š์œผ๋ฏ€๋กœ ๊ทธ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
400
+ ์ด๋ฆ„์„ ์ฝ์Šต๋‹ˆ๋‹ค. OpenCode 2๊ฐ€ ํ”Œ๋Ÿฌ๊ทธ์ธ์— ์ฃผ๋Š” ์‚ฌ์šฉ๋Ÿ‰ ์ถœ์ฒ˜๋Š” `client.usage.limits()` ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ์œ„์น˜๊ฐ€ ๋กœ๊ทธ์ธํ•œ
401
+ OpenCode Console ์ž‘์—… ๊ณต๊ฐ„์˜ OpenCode Go ํ—ˆ์šฉ๋Ÿ‰(5์‹œ๊ฐ„ ๋กค๋ง, ์ฃผ๊ฐ„, ์›”๊ฐ„ ์ฐฝ)์ž…๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰ ๋‹ต๋ณ€์ด `opencode-go`
402
+ ์ œ๊ณต์ž์—์„œ ์˜จ ์„ธ์…˜์ด๋ฉด ์นด๋“œ ์•„๋ž˜ ํ…Œ๋‘๋ฆฌ์— ์ปจํ…์ŠคํŠธ ์ˆ˜์น˜ ๋’ค๋กœ `5h 42% ยท 1w 18%`๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค(์›”๊ฐ„ ์ฐฝ์€ 90% ์ด์ƒ์ผ
403
+ ๋•Œ๋งŒ, ํ˜ธ์ŠคํŠธ๊ฐ€ ์š”์ฒญ์„ ๊ฑฐ๋ถ€ ์ค‘์ด๋ผ๊ณ  ์•Œ๋ฆฌ๋Š” ์ฐฝ์€ `limited`๋กœ ํ‘œ์‹œ). ํ˜ธ์ถœ์€ 1๋ถ„์— ํ•œ ๋ฒˆ ์ดํ•˜์ž…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์ œ๊ณต์ž,
404
+ ๋กœ๊ทธ์•„์›ƒ ์ƒํƒœ(์‘๋‹ต์ด `null`), ์˜ค๋ฅ˜์ผ ๋•Œ๋Š” ์•„๋ฌด๊ฒƒ๋„ ๊ทธ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. OpenCode 2๋Š” ์ œ๊ณต์ž ์†๋„ ์ œํ•œ ํ—ค๋”๋‚˜ ์ด๋ฒคํŠธ๋ฅผ
405
+ ํ”Œ๋Ÿฌ๊ทธ์ธ์— ๋…ธ์ถœํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ AnthropicยทOpenAI ์š”๊ธˆ์ œ์˜ 5์‹œ๊ฐ„ยท์ฃผ๊ฐ„ ์ˆ˜์น˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.
395
406
  - *์—…๋ฐ์ดํŠธ.* OpenCode 2์—์„œ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ ์•ˆ์˜ ์ž๋™ ์—…๋ฐ์ดํŠธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. `opencode plugin update`๋ฅผ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜
396
407
  OpenCode 2์˜ ์—…๋ฐ์ดํŠธ ๋ฐฉ์‹์— ๋งก๊ธฐ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
397
408
 
package/docs/reference.md CHANGED
@@ -503,17 +503,29 @@ settings but does not send them yet.
503
503
  only OpenCode 2 has: the workflow stage moving (`๐Ÿ”ฅ Stage`, `work 3/7`), a switch of discipline
504
504
  (`๐Ÿ”ฅ lit-loop โ†’ start-work`) and a lane starting or finishing. A burst folds into one toast with a
505
505
  `+N more` tail. The restart notice, the `lit adhd` suggestion and the opt-in finish alert work as on
506
- OpenCode 1. The automatic-handoff notices and the skill hint, which only the server knew about, are
507
- not shown there.
506
+ OpenCode 1. The automatic-handoff notices, which only the server knew about, reach the screen through
507
+ `.litopencode/notices.jsonl`: the OpenCode 2 server plugin can only subscribe to events and has no way to
508
+ raise a toast, so it appends each notice there and the TUI plugin shows the lines that appear after it
509
+ started (the file is capped and never follows a symbolic link). The one popup the server raised besides
510
+ those, the ignition toast, is already the TUI's own, so there is no separate skill hint left to port.
508
511
  - *TUI.* The HUD is placed in the slots OpenCode 2 publishes: the status card is claimed in `app`, under
509
512
  the session and the sidebar, and keeps its four rows idle or working; the stage chip shares
510
513
  `prompt.footer.status` with the ignition line, because there is no slot right of the agent and model
511
514
  row; the home line is in `home.footer`; the mission panel sits in `sidebar.content`, so it shows when
512
515
  the sidebar is open. The card, chip, panel and home line use the same text builders as OpenCode 1. The
513
- `band` and `notify` options of the plugin entry are read from the options OpenCode 2 hands over. OpenCode 2
516
+ `band` and `notify` options of the plugin entry are read from the options OpenCode 2 hands over. The footer
517
+ line steps down with the terminal width instead of being clipped by the host: mark plus the whole chip,
518
+ mark plus a smaller chip, the chip alone, the chip as `๐Ÿ”ฅ 1/3`, the bare flame, then nothing; the room is the
519
+ width less the sidebar (assumed open above 120 columns) and the host's own spinner and figures. OpenCode 2
514
520
  keeps no todo list, so mission progress comes from the ledger, the work record and the plan. It names
515
521
  its tools differently (`shell`, `subagent`, a `skill` tool that takes `id`), and the card reads those
516
- names. OpenCode 2 gives plugins no rate-limit figures, so none are drawn.
522
+ names. OpenCode 2 gives plugins one usage source, `client.usage.limits()`: the OpenCode Go allowance of the
523
+ Console workspace the location is signed in to (a rolling five-hour window, a weekly one and a monthly
524
+ one). For a session whose latest answer came from the `opencode-go` provider the card's bottom border shows
525
+ `5h 42% ยท 1w 18%` after the context figure (the monthly window only from 90%, and a window the host reports
526
+ as refusing requests reads `limited`). It asks at most once a minute. For any other provider, a signed-out
527
+ host (the reply is `null`) or an error, nothing is drawn; OpenCode 2 exposes no provider rate-limit headers or
528
+ events to a plugin, so there is no 5-hour or weekly figure for the Anthropic or OpenAI plans.
517
529
  - *Updates.* There is no automatic self-update inside the plugin on OpenCode 2. Run
518
530
  `opencode plugin update`, or let OpenCode 2's own update policy handle it.
519
531
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litfamily/litopencode",
3
- "version": "1.0.37",
3
+ "version": "1.0.38",
4
4
  "description": "Workflow plugin for OpenCode: add lit to a prompt and the work is planned, built, checked and written down so the next session can pick it up.",
5
5
  "keywords": [
6
6
  "opencode",
@@ -70,7 +70,7 @@
70
70
  },
71
71
  "dependencies": {
72
72
  "yaml": "^2.9.0",
73
- "zod": "4.1.8"
73
+ "zod": "4.6.6"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@opencode-ai/plugin": "^1.17.6",
@@ -79,6 +79,10 @@
79
79
  "picomatch": "^4.0.5",
80
80
  "typescript": "^5.9.3"
81
81
  },
82
+ "overrides": {
83
+ "zod": "$zod",
84
+ "toml": "5.0.1"
85
+ },
82
86
  "publishConfig": {
83
87
  "access": "public"
84
88
  }
@@ -391,95 +391,98 @@ llvmlite==0.49.0 \
391
391
  --hash=sha256:ee81e96c15a6f870918f1eb60c913551c16aa23defb4f5f1acfa660d6a0aaac2 \
392
392
  --hash=sha256:f3f2ff0aeb17d34fcce9f79b99baac441cfd3efa41b83e233ca4530a72381f72
393
393
  # via numba
394
- msgpack==1.2.2 \
395
- --hash=sha256:06d95f61de7afe4f4ff908a6feebfcb070d0582ac87c9cf3cedf8551cf634516 \
396
- --hash=sha256:0708afbf6a9587f0bfe479a9825c141d14d91e2f6a5c8103cf28bc96f4edb5d9 \
397
- --hash=sha256:0883a1578168929fd1640fbbc4614773f1a130e419a8a817dc2918d9af1b651c \
398
- --hash=sha256:0a652ceeededf71d3fa40c303a02a149d42338d310162367b91c539d4bd6e0a3 \
399
- --hash=sha256:0dd9173c5ebaf5ecc5ca86e7ae1db92934e1d57b856f3dd90698941431f4fd77 \
400
- --hash=sha256:0e3315de5a4b2920ccef48d96b4448025e064a10d0f5a250f6584477d839c8d4 \
401
- --hash=sha256:0e91332144f69bc3018c91232fac26da580ef748fb8eaddd7914d4458001cc4f \
402
- --hash=sha256:0fbc1bed8a535389b41882cfae66376e248cd1680eaa94fd83193c73e1d24986 \
403
- --hash=sha256:11e8c421e117d1c36728b423d0402555cccbf0c6f53e288f0e75b6b12100d70f \
404
- --hash=sha256:1510f24612d4b983dff6935d9273e02c320cfd525727fbcb58836a75f589fdbc \
405
- --hash=sha256:1814f92306ae7862908e9ece7cfd90e0dc87ded3e89b6ae7ffdd1175d6376fdc \
406
- --hash=sha256:1e8cdd1f3e7cc52c751092a9bf740e81e6919ab109cd376ae2d965dad0bbae34 \
407
- --hash=sha256:1f3af0baafd184436501004828bb3df64eeb2fc49dfe9d89abcf604956094563 \
408
- --hash=sha256:1f6b6f8deb07d49090e1808c6ef9cb7d23ca17bef3aa6ed3e5e03df16606e60c \
409
- --hash=sha256:226a62ffe99fe54c5c61d910ec64c3449b7766c3280bd286bf6c94838dde239a \
410
- --hash=sha256:29cc2d5291711a52956a79a51f41c732329df39ad727c886bd8f0b5b9237a808 \
411
- --hash=sha256:336525cc2688e43ea77dfb1a4ce012c8cde561835913801dbfcfdcf4111d8abb \
412
- --hash=sha256:34e83e345194a2a51d8bd447dea9de2104f91e75b247f4735f14f04529f0746b \
413
- --hash=sha256:352ed831042549cca8be23780e1fe7c9177e65ff02bf183509c4b4d33f671782 \
414
- --hash=sha256:3e915d390d7068b257ca8b62f3fc59fad135c8631d1017ab03b0b924b07c5367 \
415
- --hash=sha256:419a45c67a5c04213172a14b1864657e014665b77d7081b107a51707923dd39e \
416
- --hash=sha256:42fd9260416885b4815caca5bdd14dfd5dda6cdade732d6c09104ef8f6228761 \
417
- --hash=sha256:46ec851571d8f1b6e29794ebb9dd36f785008da6d14f57c702e60781d6caf648 \
418
- --hash=sha256:4710d881d8fb047deed2485707409116722af2b992d3fefd73c7667c4e350839 \
419
- --hash=sha256:4955accbd87f27beebef5f3ecc27503aa74cb016fb4f640868e749fd93194a35 \
420
- --hash=sha256:4a4348705be86e029d04e741cf9ed0dfe03e942d7d3b92e838fa80d3aa2c3ebc \
421
- --hash=sha256:4b554d8164ebb526892194f71dcd96ef1fefe0c250087498785d3ffc04a80be3 \
422
- --hash=sha256:4d9a562aec0a92fe536da2e533d313b3d2a6b929157b1dec7ff623446dc0a8ab \
423
- --hash=sha256:51dd39d23cfdea0400ed3ff2d29d1e83bd951d3aea79dc89be5b701a09edfe23 \
424
- --hash=sha256:53679573c75cce5f82359e0bd4e6a97809a6b9a9b7a48fd1ba592f4a82cddc84 \
425
- --hash=sha256:55faa6f8395e23b848c535ad5dcb96b3462f37f5e7f4ac500d500434f7345da7 \
426
- --hash=sha256:58ce37a4a54577115922385d37201d9a44d66d0167dfbbf4770a2e9bf8ea7ba3 \
427
- --hash=sha256:59d5b93efa45fd09f620d0c9ba81cde339a2c9937af3eea42ee9653094ce6640 \
428
- --hash=sha256:6195257a107bf25872ef84aab7295078271eea3ac6413f0506b631f6c9586ed5 \
429
- --hash=sha256:652d1bf13d01bac8fd569def0fe76745e55bcda01e30aa6332d5947ea3788839 \
430
- --hash=sha256:682804bf31e43d46e51a9a33bd575b51e839d715ce6bd5612c055f7b28ad637b \
431
- --hash=sha256:68df2947921d449f6dcfeafd86cb2cdde13327a8b447534bbe4ee5aaf32a5695 \
432
- --hash=sha256:6f53285f20d592ed309ee19e509cc4c77a3bda1db02ad67e8a0949bb227a5a6d \
433
- --hash=sha256:73b0e05c32c3cfc3cd84994908e57430c0ebc6813abf905d3f18ff115d54df3f \
434
- --hash=sha256:77c2e018417dc1d66f235e383877ee885b60ade9d29e494dd581e08af2cb1923 \
435
- --hash=sha256:7826f16edc763e768404f55605ef85dfcf5857e729c1ed29e0d7c180be4fe6d8 \
436
- --hash=sha256:7afa5431f6f3487c584187ca6c8e2a34e9b106529893b3e720eabb068f6ac970 \
437
- --hash=sha256:7d095df2627e5dd59ac7b0c5ad627a671c76e6020171e03cbe4621a61f0562c3 \
438
- --hash=sha256:7fe374ba76eb0ecca13a1703daa8fa85825a6ddddbb52d4c1a732fa524194683 \
439
- --hash=sha256:82b1bdf293267afaadcc608b125e7fc6576bb0785a60c4fa7d07c7ab76ed76ec \
440
- --hash=sha256:86f173a584f72f6164801f31866d22a581f60c991572cf922aed9ab8eb422b77 \
441
- --hash=sha256:8b1415d02e9bf722672af8a90f90813265a0cd0b14163187261e54a5592bc949 \
442
- --hash=sha256:8b2a281b556f120a43e591ea39915741b7ad54d4727b9c4350a0a11692252533 \
443
- --hash=sha256:8c6321a414f8b4a8dc43976b2fa8349156434ca9adedd9a187b796f7e1d3d3fc \
444
- --hash=sha256:8dc4487097571f7311188c3eca2a3e86cd1f1db4c37c7a017bcc3fd38486cbfe \
445
- --hash=sha256:90986cc9aab9d7d1d8f38bcbf65d3f7ac83bdd90c35765db7d691b4829698cba \
446
- --hash=sha256:9352e6cdb510a7b1a5d3ccaccec730e82e50cf3484a3af7bdaab19e23b9589ff \
447
- --hash=sha256:935b1cfad9b908b0fa845010f4271df4c2f04e1cd26e3f18acd61a45f93c9e36 \
448
- --hash=sha256:9b659d77f8726fa5e7038967dda6b68d53cf34472c094cfa5b845454713b90d5 \
449
- --hash=sha256:9bd3d1557c3fe1a095068210708a03e3e4795973392af6f4047060e70abd9a6c \
450
- --hash=sha256:9bf452ff4d4981f25a18e9476e002bcc9263e7928024aa4d7148e25f7be3f929 \
451
- --hash=sha256:9d7fb25b4442fae0cb2590272d06ab4f6caa526ee36a994edb81e946b874813e \
452
- --hash=sha256:9db1ba1c1e6a84245a9dd866265b56b8a1e9461549cc72ed296d8cbfbd32961b \
453
- --hash=sha256:9eb0b0e602064527a045ea28c4f174ed69383587e29cebe28947e3b84106eb2a \
454
- --hash=sha256:9fd7f32e2f0fb334e7ecc5adb5cf0458785bd3a9d9d86f950e1715f101cebce5 \
455
- --hash=sha256:a378e12ccc06d76efde115caf4073b7e5ff3cc18291d1341f9e65fb882e3f754 \
456
- --hash=sha256:a4161eee7799863aee237c35c90427861f7b994416dd81ae829f560b0a81bdcd \
457
- --hash=sha256:a9b4cf3685a135666d27d0d7a73fece74e2fad01d9b508fded89e843512f0e90 \
458
- --hash=sha256:aa1120c653b76d8eafa50423b5eba06b5c9737f8692c74fa3afe03e84b8978ea \
459
- --hash=sha256:b07c03f0da7e5279170df7745ddc732d526c8a198208936ec1a95c11ed2b2d5f \
460
- --hash=sha256:b13b59e66f107cca1ba708dd5307179870ca1b15b19fcee7ccf722e5308d9212 \
461
- --hash=sha256:b542ffc0a5c531eedc40419f291f1bd659aa8d4223408a5b51c88a2796083fd3 \
462
- --hash=sha256:b5c696ae7cd7166b3657261adb855b461ff31f07823fdbae9de8bf80adfccc21 \
463
- --hash=sha256:b68614fba0570349833b7dd999ff0aed4e5cc8d9eb6e3a7d4527be33c65e33d3 \
464
- --hash=sha256:b8dd6c71d20c28d2d0eb0c51e7cccf3584afde3b1364f6629596186c9025bd54 \
465
- --hash=sha256:b9b0c1f2aa7b0026b4bd50718100e8b04175e4f36e160aa852502377b5e572e7 \
466
- --hash=sha256:c522420d78db2431887d45b518e304d86e27b9ad0b30f24e3806a6ad5d8bdbfc \
467
- --hash=sha256:ccfd880988f8438d1c91c77d7edc58e70f4d2012e999167bc154c64c6f06ea6b \
468
- --hash=sha256:cdb6cc6e1127d15879c47a8b3270716243da82d3e7feab1f5946872c75b3d60f \
469
- --hash=sha256:cf66fb38703e61a486b01b56d43bb1f50698fbe99b6bd90feba10f24fab60b3b \
470
- --hash=sha256:d13d07efbf655f9ae7a2352b630c52727b359005b21ba08a507585c9ac8c0896 \
471
- --hash=sha256:d242f3c4ccf55b056e6cf901720dccde58f1df117898f2bbf3bcd6e38ec7c248 \
472
- --hash=sha256:d24b38a825bcca41bb956de50eb98451ef291304a8607fad99e619043d3e79b9 \
473
- --hash=sha256:d3c247d457ae9079974c7ce3c665396754a6d2baff7eaa51332212a8a5a3f13b \
474
- --hash=sha256:d886baa46b2532135e7320067e6a44edb09ba5883a6096b0f9c044533984b8a8 \
475
- --hash=sha256:e05a94a0442de86818a30281c6cc2cb9cc7aa148386fd3541c4d4774b73cb3a9 \
476
- --hash=sha256:e1b99ad34613d5f8477fa5cf99bc4eaeaf27965588007c102370cd9a78fe9de5 \
477
- --hash=sha256:e2eb7ea0ac3911a7aac9d8aaa36d40f216d99455b3274cd3fac38181bcd910cf \
478
- --hash=sha256:e497ee34e8a3342bbde51b27c22d8db05a651df3361dd3daef5b3ab0d66f3e04 \
479
- --hash=sha256:f11e09f10210a91c169e39c7a5a1f9090eaa73ad75555fafad5023c3053c47ba \
480
- --hash=sha256:f466049b8e1ec0854287bbe9a074316826fe0e08dcf707245f98b1ae49e92650 \
481
- --hash=sha256:f80361592c13d7226b4379c8941529b63fe1a9d0e05d2de8f3306b70e522b53f \
482
- --hash=sha256:ffdd2f4950daf7815490f23087963e3420175b9609520b7ff5df64d351159c22
394
+ msgpack==1.2.3 \
395
+ --hash=sha256:07c9733089d1b176c3dd2f7fa268452f9d5d784d076473499d754a58e8d1fbbb \
396
+ --hash=sha256:0955b9000725573d1457c1676944b370dd9643c8d18f25bda5ac72913f850949 \
397
+ --hash=sha256:0c91762c48cd686dc9cf2b142c0bc544083952de32f5853d6624c956e54b85e5 \
398
+ --hash=sha256:0ed5823c4efc20fe87d3530665f40ec18a002be003114814c21235cc8d256207 \
399
+ --hash=sha256:13221a6c81ebb8e43ea63a7251c35d54e4175cea37ebf3a62e911bdf42562a3c \
400
+ --hash=sha256:186e6c602b8a9968b8e864c67d622a69279f7d1e55ae25f40e3bff7e815b2b62 \
401
+ --hash=sha256:18a6ed513023001b28dcd3ba54966f6bb90a38274ba8d2640464bcab3a1b81d4 \
402
+ --hash=sha256:1d6bcec3dbbdb89ca385d3a73e63ceae7b841fa0d7ca7c676f1a7bfe7fb2cdb8 \
403
+ --hash=sha256:1f4ae8bd4ad9ba085fde95e95d055a896d19210238a4199a771a3cf36dceed49 \
404
+ --hash=sha256:1f585407f740a9eac04a3bb82c61d68a0ea78f90e29e670bfb086b9ce3a518dd \
405
+ --hash=sha256:21bfa4d2aa0b04c1806ef778a1199e9e53ea2441bcbf284420a32083896320b8 \
406
+ --hash=sha256:2487453ca1b6104442c6442f9a1a8fee1fe8f428a70d99d4cba799108b304150 \
407
+ --hash=sha256:2574ef81c1c8c38b10e330f3f9406fd09198a776b002030fafcf8e7647e9e06e \
408
+ --hash=sha256:30e1522e4173230dca4d9ad896f038f73c0da6c1edd42f4dbad88ac583cf5d46 \
409
+ --hash=sha256:32edb81a2b5eb7cd7c9d941b2bfbbb082fd2cd09e0e725930316af6b708db186 \
410
+ --hash=sha256:3372475211a9ce1a23acefe512cb3e121d18c95dc74ed56cb1819ef40836ebf4 \
411
+ --hash=sha256:382b219de3d436de3baba0f4b0c6d4336e8f5858d0eb047918b13b69a71c6c55 \
412
+ --hash=sha256:382bc88fe90f29f5ac8a0b65c7046ff255356f2f2f3186c30e370215736fa1dc \
413
+ --hash=sha256:39b6986c19e1f2dfa549d185dba6ccf1de2e4c0ba10d8cfc0048935b1c5f9109 \
414
+ --hash=sha256:3a31905206722103a84c1f72633fe30692cff6732c9d262e09a27dbc468797c8 \
415
+ --hash=sha256:3d4c807ed050fe3ddbea5ba7e9f63d7136871ce42861be1f50ff739f0e91047a \
416
+ --hash=sha256:3ec409b0d6aa8e9eec6eaf881b893caa215dbe68c5319ca96e8a271d81bb111d \
417
+ --hash=sha256:471e12a6a42498a31490c206e0069e343b6a7c35db540be73a879eb06f5be047 \
418
+ --hash=sha256:4c0780095871ecc49a58b2ff6b1b43b25214704da67646557ca287a3f49fb2dd \
419
+ --hash=sha256:59612b4ed48a04cf024584218e813562f3b30a3bafa5f55abe300b15da314751 \
420
+ --hash=sha256:5bd5f91ea75c45cafcc5433ba8fae59b708b736ec178d2441c40c499e9e079db \
421
+ --hash=sha256:5bf390259cb25a6a1cd197c65810999b811f64cd38683251538bcc5a1e41f7d3 \
422
+ --hash=sha256:5c1efdd9181cb1b719ee46865f368a927f1c0c65d577798340b1194545b7515a \
423
+ --hash=sha256:5e0d7950ca3c1bbae291d0552dd3bb2792fc680629c4c0d44e47e5bab969f3ca \
424
+ --hash=sha256:5f304123b90e8b2e49867981b7f6061612c39f50cca51ee88de007c084cf68d3 \
425
+ --hash=sha256:62cc1a4ef0e553bac32c8342e1f04834aca7de276b92744eb7307db77759b890 \
426
+ --hash=sha256:63bb7448a1e9111319ae2430c09a5596140c160422830d6271bc75730ff2ff9a \
427
+ --hash=sha256:6576f348ed6cc4f31db6fd915a8e94245f042f50eae08d48732425e70638ea37 \
428
+ --hash=sha256:666ef5601ab0e6e345e47febc96aa81143cc932201543480cbb9499164f05ffb \
429
+ --hash=sha256:6707d2fa2aa1bb5424ea0b05f44ffc989b15ab41a73ff5855bff4944fec7c8ac \
430
+ --hash=sha256:69ad12cedb674c73527bed869cddb42b742cac79a207a614202a4abaa24ea173 \
431
+ --hash=sha256:6a834097144aabe948b8ca9020a833e8026f7d0abbd0ec54bc7e50f45a8ce012 \
432
+ --hash=sha256:6df430419f2338cb71e4a34d6e64f83c88ccd321f91f40ba4513400b36d864ec \
433
+ --hash=sha256:700bc0fc9e968a292b9137ee70e7a012f7e115bf0107ce45e3a88202788dfc1e \
434
+ --hash=sha256:7013534a7163aa4f213c4d9864f1a8a7555daac6fcd48f699a198e29b436bfab \
435
+ --hash=sha256:7995a7c6a62a1d6e7df211b4a16de513bd99fd053525050a319f80f44fb8015e \
436
+ --hash=sha256:79dfa38faf92f804aa61beec140d70b18418e1dde1778dbb77a87a4cce85aa8a \
437
+ --hash=sha256:7a003b02c6ee2eea6dfe0bb08818631e3597e69f0131f2a8250488a1cc553290 \
438
+ --hash=sha256:7c047250096f9fc19dba26e3d1639b5e7a84114003605c94def667149a70ced1 \
439
+ --hash=sha256:84a6616d396ec1bc18a1e83e67c96a393ec35dfe5e17434a5be7b9aa0fe988ab \
440
+ --hash=sha256:87cf2ef05ff2f2493ba29fcdaef27e960ca64dacfd13460ae29e6f92e0ed05bb \
441
+ --hash=sha256:89c930aece4e972b208ba589c8410b4167b05e411a5ea2cb25fd96f8bc47ee43 \
442
+ --hash=sha256:8ca67f77938ea6a3663aa9bd22b3e031f6da84d665be850abab910ee90728dfd \
443
+ --hash=sha256:8e51eca14fbb65c4e0a5a9657346962bd3dca78c08e04e3d4dee70ef48687d30 \
444
+ --hash=sha256:8ec7a1d49ca6c2569d722ab5ec86e90089b0713900aa31905b47b4c4d9e78ce0 \
445
+ --hash=sha256:902f3490db0e07a7d40b48536a85c9b28fbf1397e7e1658a45a55f958e303620 \
446
+ --hash=sha256:905a189853d6bdb204c7ae5f4ab77fb857448abfff574d3d93c62e2815b24b4f \
447
+ --hash=sha256:9276ba88891338f2617044429dfd080ae008c9868a25f6f1a7d004a35dc9ac0a \
448
+ --hash=sha256:9324c54995641c3d1f92a9d55093c8cde0ffa2fbc87a467a688ef60428393220 \
449
+ --hash=sha256:968583e956d0427878050b371308c5f8647088732ef3e66a117dbe1192ec91e0 \
450
+ --hash=sha256:9d7e9cbb0998bbfd363fd9a09c330520d5e9cb323c05b5a1a05865d23ccf2226 \
451
+ --hash=sha256:a393e428f6ffb0dcb73308c1fff5593041c16ff42da66e5bac8a83a6107a54b0 \
452
+ --hash=sha256:a6b63917d60d6df451f328bd6afba8565e33c4afe1f62ec4ad758b78731c827b \
453
+ --hash=sha256:b1631e12fe572e181cd77e831f69335d6cd5278eac22e3db3f33cf264ac2ac18 \
454
+ --hash=sha256:b774ff994d844e541439ac5d2d49a14def4104830c3465e9394c153f86200ffb \
455
+ --hash=sha256:b949cc25e4a09252cbcc54e66e507de914d0e94a3a7039bd54c299bf7037c098 \
456
+ --hash=sha256:bb89b5dc30469c84bbf8684826eb851d82412ca95690e111b9ac5e8fb343961a \
457
+ --hash=sha256:bfe7d5b62cbe7aa664f0b3e2c49077f10fcdd06183d3014f8271ff3c5edbfbf9 \
458
+ --hash=sha256:c309a7abae1d14ba29a8bd0ddbd704a5e469d8e9bd9c3dee0e4ff53d7ae01d56 \
459
+ --hash=sha256:c77e27790ad72989db783d5303825fba0b71550f00a490efba35cde7dc4b719f \
460
+ --hash=sha256:c942c21a93f36b3a69e828c8945bb72c94dc2ffe488a2086950c812f3edf046c \
461
+ --hash=sha256:ccea05b5542f6d283fef3f0a8e93a7f0be90af0ddeeef84c25c0216ba76dcae1 \
462
+ --hash=sha256:cd5a9f9f86a52c24713679aa2631956835f3842512964ff93f736ff76f1f530d \
463
+ --hash=sha256:d0238cd05dec9ffbe0de1071df685ba63e30a36ac155285b1a094e727c38cbe9 \
464
+ --hash=sha256:d1c1e8989a855b7f1f2a64ec4a80b23a631822903952770813857b2e4f460471 \
465
+ --hash=sha256:d2f9c4f85e47a44d26d5baf3b041eef23436e224d44eed273f01bd8a12048d9f \
466
+ --hash=sha256:d31864ba3933a589b6a00249f89c0eb422197f49128fc10da550e57e9cb0f377 \
467
+ --hash=sha256:d8ef3a66e4b52d2d7fdd90df2984670124b2ff7546d76bb25dcf68ef47f7df58 \
468
+ --hash=sha256:db84203b13aecc222f465061397fdd5b53b7ae73d2c95ffc1c8dc5be0153a709 \
469
+ --hash=sha256:db9fb67a3a2e75247bae569d34ebb5ff61c0448a4f0d6dbf991dae68af39b007 \
470
+ --hash=sha256:e0bd394e999949c814f7912284243298de1b5a17b6a3dcb6cc8a79b156ffc4fa \
471
+ --hash=sha256:e15f70588f4db8cd10df0930145b186de70feb9db51710cd378b1399009655bd \
472
+ --hash=sha256:e54394b7dbe2e12ab032d9d21feef7bb61a90a150a2623633ba3781ba69dcb1f \
473
+ --hash=sha256:eaf7e82249837e3aa97297b34a0bb9ff562027381631e057cea6e1367f10b438 \
474
+ --hash=sha256:ec0030361cc861ac699b2ef1c695b741fa145c88f8667fa3d7e3f73deeb648a3 \
475
+ --hash=sha256:ec90a9ae3e1169fa1171147340f0e97d941aa19fcd3b34e8339a55933ed042af \
476
+ --hash=sha256:ed899d73a22f286a72bd9528d63f2ab3030dbad8bf1527fc249319a50d61fb9d \
477
+ --hash=sha256:ede33b2892ceb976283e009ad12fa1834cfdf1f9c43ee9c97849fc588d00a618 \
478
+ --hash=sha256:f24a43b3560e20f825b807fe1e874bd73d53abaf8bbdcf258a6eb152cddbc1f5 \
479
+ --hash=sha256:f3d7b3d0018746b5997dd6b14a1870b07cc4c327d9101145d94a1fc264a51a06 \
480
+ --hash=sha256:f41ca154b7737b11893cdce3c78c61d703398a1cd54d4297bdad908392338a8e \
481
+ --hash=sha256:f42f146752eedb6765f07dcc04d72dab0a25779ec8d4a88c0085263ce114f22c \
482
+ --hash=sha256:f56fba61b2516be7917cb00151f0d060b5b21184e3499bb57f0f7d9259bea124 \
483
+ --hash=sha256:f9ddd28d3e9bbc602a9dced1591882c7fb9ab776eef8837da2c326fde19e2853 \
484
+ --hash=sha256:fafc3b8898b432b841d30a61082c599fa7f4d06885f9dc58ad72259e12059fa6 \
485
+ --hash=sha256:fcc6800daac4922960f6eeb7a0dda3dd4105e0bf7bce0e83ebc465a78cb7bdba
483
486
  # via librosa
484
487
  narwhals==2.26.0 \
485
488
  --hash=sha256:29326d74f107c347fd1009bd58e38d9f7c7c5b51e6de97bc93dbc325d9038b54 \
@@ -3270,7 +3270,7 @@
3270
3270
  },
3271
3271
  {
3272
3272
  "path": "templates/hyperframes/package-lock.json",
3273
- "sha256": "9c8fb05ae772defbd885e032a3ee04caf28bea3869e115a748c23e9808288a3c"
3273
+ "sha256": "115478d6f9ea0194666aa076559cd063955ba0d3fdc75782c2c8204e2a1c6599"
3274
3274
  },
3275
3275
  {
3276
3276
  "path": "templates/hyperframes/package.json",
@@ -3282,11 +3282,11 @@
3282
3282
  },
3283
3283
  {
3284
3284
  "path": "templates/remotion/package-lock.json",
3285
- "sha256": "8fb3fa7aba1a99516ca19f225b61bb7e881987ddfcbb6e206e6093692b0d6d21"
3285
+ "sha256": "10beadf1d53049adcb83c75e8af35bb50d83907bb96d70b1deb1faad5fe8e6e6"
3286
3286
  },
3287
3287
  {
3288
3288
  "path": "templates/remotion/package.json",
3289
- "sha256": "d152b6fcf21fa733e0a136f1607bddb4a10801ee59330a4e4c164da62ba7a03f"
3289
+ "sha256": "84b1bd1a2624889e44dad6df00a79ac6d6ea51a3bda99050f7cebae8b588f5b9"
3290
3290
  },
3291
3291
  {
3292
3292
  "path": "templates/remotion/src/index.tsx",
@@ -3789,7 +3789,7 @@
3789
3789
  },
3790
3790
  {
3791
3791
  "path": "requirements-audio.txt",
3792
- "sha256": "771ab581292545d597b5a5e0f47efa399ecc5f1c5e710dd042a55e72702a7775"
3792
+ "sha256": "55bc85467f4e416db2d6069d23e94f8ad7f3b515432a166797c4a682e4451503"
3793
3793
  },
3794
3794
  {
3795
3795
  "path": "runtime.mjs",
@@ -463,16 +463,16 @@
463
463
  }
464
464
  },
465
465
  "node_modules/@hono/node-server": {
466
- "version": "2.1.1",
467
- "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.1.tgz",
468
- "integrity": "sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==",
466
+ "version": "2.1.5",
467
+ "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.5.tgz",
468
+ "integrity": "sha512-MgP/I06GdvsilDspMk/qmRzpk8LOUStP6jFAbSRi40W09DhoyUn+Mj7eyKICPqeYKybOjVFLVIAQzvu1bfIa7Q==",
469
469
  "dev": true,
470
470
  "license": "MIT",
471
471
  "engines": {
472
472
  "node": ">=20"
473
473
  },
474
474
  "peerDependencies": {
475
- "hono": "^4"
475
+ "hono": "^4 || ^5.0.0-0"
476
476
  }
477
477
  },
478
478
  "node_modules/@img/colour": {
@@ -3771,9 +3771,9 @@
3771
3771
  "license": "ISC"
3772
3772
  },
3773
3773
  "node_modules/zod": {
3774
- "version": "4.5.4",
3775
- "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz",
3776
- "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==",
3774
+ "version": "4.6.6",
3775
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.6.tgz",
3776
+ "integrity": "sha512-4UmzQUF5fxvfcYHlbjBmejU+w+hbDnWFx4aS8kBx7nPBHcO2SqPbpAG8snbdg+Xse4pRefR98hgjFgIMCI/UhA==",
3777
3777
  "license": "MIT",
3778
3778
  "funding": {
3779
3779
  "url": "https://github.com/sponsors/colinhacks"
@@ -1 +1 @@
1
- {"name":"readme-studio-motion","private":true,"type":"module","scripts":{"compositions":"remotion compositions src/index.tsx"},"dependencies":{"@remotion/cli":"4.0.526","remotion":"4.0.526","react":"19.2.0","react-dom":"19.2.0"}}
1
+ {"name":"readme-studio-motion","private":true,"type":"module","scripts":{"compositions":"remotion compositions src/index.tsx"},"dependencies":{"@remotion/cli":"4.0.526","remotion":"4.0.526","react":"19.2.0","react-dom":"19.2.0"},"overrides":{"zod":"4.6.6"}}