kaizen-agent 1.7.1 → 1.7.3

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 (39) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +657 -657
  3. package/agents/kaizen-builder.md +77 -77
  4. package/agents/kaizen-planner.md +250 -250
  5. package/agents/kaizen-reviewer.md +127 -127
  6. package/cli/dashboard.ts +1187 -1187
  7. package/cli/install.ts +947 -947
  8. package/cli/publish-gh.sh +27 -27
  9. package/cli/release.sh +45 -45
  10. package/cli/settings.ts +415 -415
  11. package/cli/state.test.ts +197 -197
  12. package/cli/state.ts +704 -704
  13. package/cli/web.ts +423 -423
  14. package/commands/kaizen-abort.md +5 -5
  15. package/commands/kaizen-approve.md +5 -5
  16. package/commands/kaizen-auto.md +6 -6
  17. package/commands/kaizen-backlog.md +5 -5
  18. package/commands/kaizen-config.md +5 -5
  19. package/commands/kaizen-full.md +5 -5
  20. package/commands/kaizen-init.md +5 -5
  21. package/commands/kaizen-install.md +5 -5
  22. package/commands/kaizen-lite.md +5 -5
  23. package/commands/kaizen-plan.md +6 -6
  24. package/commands/kaizen-reject.md +6 -6
  25. package/commands/kaizen-review.md +6 -6
  26. package/commands/kaizen-run.md +5 -5
  27. package/commands/kaizen-status.md +5 -5
  28. package/package.json +1 -1
  29. package/skills/kaizen/README.md +281 -281
  30. package/skills/kaizen/SKILL.md +440 -440
  31. package/skills/kaizen/adapters.md +77 -77
  32. package/skills/kaizen/config.default.yml +167 -167
  33. package/skills/kaizen/spec-build.md +119 -119
  34. package/skills/kaizen/spec-main.md +104 -104
  35. package/skills/kaizen/spec-plan.md +201 -201
  36. package/skills/kaizen/spec-review.md +121 -121
  37. package/skills/kaizen/spec.md +153 -153
  38. package/skills/kaizen-help/SKILL.md +129 -129
  39. package/web/board.html +17 -8
@@ -1,129 +1,129 @@
1
- ---
2
- name: kaizen-help
3
- description: >
4
- Quick-reference card for the kaizen workflow: commands, modes, approvals,
5
- fix loop, config knobs, state layout, backlog/status rules. One-shot
6
- display, not a persistent mode. Trigger: /kaizen-help, "kaizen help",
7
- "what kaizen commands", "kaizen cheat sheet".
8
- ---
9
-
10
- # Kaizen Help
11
-
12
- **Print the content of this card into your reply.** The user cannot see this file —
13
- it loads into your context only, so summarizing it or saying "see above" shows them
14
- nothing. Output the tables themselves.
15
-
16
- One-shot: do NOT change kaizen's active mode, write to `state.json`, or persist
17
- anything.
18
-
19
- ## Commands
20
-
21
- | Invocation | Action |
22
- |---|---|
23
- | `/kaizen <request>` | Start a new run in the configured mode (default: `approve`) |
24
- | `/kaizen plan <request>` | Start a run and stop after the plan (`plan-only`) |
25
- | `/kaizen auto <request>` | Start a run in `auto` mode: every stage back to back, no approval stops |
26
- | `/kaizen lite <request>` | Run the stages in this session — cheap, reviewer has seen the work |
27
- | `/kaizen full <request>` | Dispatch each stage as its own cold agent |
28
- | `/kaizen run` | Resume the current run and execute the approved plan |
29
- | `/kaizen review [target]` | Review-only: audit existing code, no plan, no implementation |
30
- | `/kaizen status` | List every run grouped waiting-on-you / in flight / done / abandoned, plus open backlog count |
31
- | `/kaizen backlog` | Print open backlog items across all runs, grouped by source run, rescued items last under `Orphaned` |
32
- | `/kaizen approve` | Approve whatever the current run is waiting on |
33
- | `/kaizen reject <reason>` | Reject it; the reason decides revise (back to planner) vs kill (abandon) |
34
- | `/kaizen abort` | Mark the current run abandoned |
35
- | `/kaizen config` | Show this project's settings and change them, one picker per setting |
36
- | `/kaizen init` | Set up this project folder: `.kaizen/` with `spec.md`, config, and — in a git repo — the gitignore entry |
37
- | `/kaizen install` | Install the workflow itself globally or per-project |
38
-
39
- `/kaizen` with no argument: `status` if a run is in progress, else ask what to build.
40
-
41
- ## Modes
42
-
43
- Set by `mode` in config, overridable per invocation.
44
-
45
- | Mode | What it does |
46
- |---|---|
47
- | `plan-only` | Produce the plan, write it, stop. No code touched. |
48
- | `approve` (default) | Stop at every enabled approval, wait for an explicit decision. |
49
- | `auto` | Run every stage back to back, no approval, report at the end. Fix loop still respects `max_iterations`. |
50
- | `review-only` | Skip plan and implementation, run the reviewer against existing code or a diff. |
51
-
52
- Mode never overrides a refusal: destructive/irreversible actions still confirm even in `auto`.
53
-
54
- ## Approvals
55
-
56
- - `approvals.plan` — after the plan, before any code is written.
57
- - `approvals.review` — after the review passes, before the work is called done.
58
- - `approvals.each_file` — confirm each file edit individually. Off by default; slow.
59
-
60
- A rejection is written to the run directory, and is one of two things — the reason
61
- says which. **Revise** ("do it differently") goes back to the planner, which produces
62
- a new plan and returns to the same approval. **Kill** ("not doing this") means the
63
- request itself was wrong: set `stage: "abandoned"` and stop, nothing to revise toward.
64
-
65
- ## Fix loop
66
-
67
- - `auto_fix.enabled: false` — every finding stops the run, becomes a user decision.
68
- - `auto_fix.enabled: true` — builder fixes findings at or above `auto_fix.min_severity`,
69
- reviewer re-checks, up to `auto_fix.max_iterations` rounds. Findings below threshold,
70
- and anything still open when iterations run out, are reported to the user.
71
-
72
- Each iteration is written to `runs/<id>/05-iterations/`.
73
-
74
- ## Config knobs (`config.default.yml` defaults)
75
-
76
- | Key | Default | Meaning |
77
- |---|---|---|
78
- | `preset` | `medium` | Quick preset (`low`, `medium`, `ultra`, `custom`) |
79
- | `mode` | `approve` | Default run mode |
80
- | `runner` | `full` | `full`: each stage a cold subagent. `lite`: this session runs every stage |
81
- | `approvals.plan` | `true` | Stop after the plan |
82
- | `approvals.review` | `false` | Stop after the review |
83
- | `approvals.each_file` | `false` | Confirm every file edit |
84
- | `build.executor` | `subagent` | Who builds: `subagent`, `inline` (main thread), or `ask` at the approval |
85
- | `auto_fix.enabled` | `true` | Auto-fix findings vs. escalate everything |
86
- | `auto_fix.min_severity` | `high` | Threshold for auto-fix (`critical\|high\|medium\|low`) |
87
- | `auto_fix.max_iterations` | `2` | Fix/recheck rounds before escalating remaining findings |
88
- | `track.default` | `auto` (inferred per run) | Pin a track for a repo that is always one kind of work |
89
- | `review.write_memory` | `true` | Let the reviewer append lessons to `.kaizen/memory.md` |
90
- | `backlog.enabled` | `true` | Capture what a run deliberately did not do |
91
- | `backlog.file` | `06-backlog.md` | Per-run backlog file |
92
- | `backlog.orphan_file` | `backlog.md` | Where a pruned run's open items are rescued to |
93
- | `state.dir` | `.kaizen` | State directory |
94
- | `state.keep_runs` | `20` | Completed runs kept before pruning oldest (rescues open backlog first) |
95
- | `git.auto_commit` | `false` | Never commit/push/PR unless the user explicitly asks |
96
- | `git.branch_before_implement` | `true` | Branch before implement stage when on the default branch |
97
- | `subagents.model` | `inherit` | Model the stage subagents run on (opus/sonnet/haiku, Claude Code only) |
98
- | `subagents.effort` | `inherit` | Reasoning effort for the stage subagents (low/medium/high, Claude Code only) |
99
- | `agent.default` | `auto` | Coding agent to launch from dashboard backlog |
100
- | `agent.<cmd>.model`, `.effort` | — | Model/effort flags for the launched session, per tool (`claude`, `codex`, `agy`, `opencode`, `gemini`); model names are the tool's own |
101
-
102
- `.kaizen/config.yml` overrides only the keys it sets; everything else falls back to
103
- this skill's `config.default.yml`.
104
-
105
- ## State layout
106
-
107
- ```
108
- .kaizen/{spec.md, config.yml, memory.md, backlog.md (orphanage)}
109
- .kaizen/runs/<id>/{00-request,01-plan,02-approval,03-impl,04-review}.md,
110
- 05-iterations/, 06-backlog.md, state.json (id, mode, stage, awaiting, iteration, updated)
111
- ```
112
-
113
- ## Backlog and status
114
-
115
- Backlog items: `- <status>: <item text>`, `<status>` one of `open`, `done`, `rejected`
116
- (`rejected` carries a reason after `|`). `/kaizen backlog`/`status` are **read-only** —
117
- never edit `06-backlog.md` or `state.json`, even to fix a spotted inconsistency; report
118
- it and ask.
119
-
120
- `/kaizen status` groups every run by `state.json` at read time: **Waiting on you**
121
- (awaiting non-null, not abandoned), **In flight** (awaiting null, not done/abandoned),
122
- **Done**, **Abandoned**, plus `Backlog: <n> open` (all runs + orphanage; abandoned
123
- runs' items excluded).
124
-
125
- ## More
126
-
127
- Full walkthrough and rationale: `~/.claude/skills/kaizen/README.md`. Full stage
128
- contract: `~/.claude/skills/kaizen/spec.md`. Codex/Antigravity porting:
129
- `~/.claude/skills/kaizen/adapters.md`.
1
+ ---
2
+ name: kaizen-help
3
+ description: >
4
+ Quick-reference card for the kaizen workflow: commands, modes, approvals,
5
+ fix loop, config knobs, state layout, backlog/status rules. One-shot
6
+ display, not a persistent mode. Trigger: /kaizen-help, "kaizen help",
7
+ "what kaizen commands", "kaizen cheat sheet".
8
+ ---
9
+
10
+ # Kaizen Help
11
+
12
+ **Print the content of this card into your reply.** The user cannot see this file —
13
+ it loads into your context only, so summarizing it or saying "see above" shows them
14
+ nothing. Output the tables themselves.
15
+
16
+ One-shot: do NOT change kaizen's active mode, write to `state.json`, or persist
17
+ anything.
18
+
19
+ ## Commands
20
+
21
+ | Invocation | Action |
22
+ |---|---|
23
+ | `/kaizen <request>` | Start a new run in the configured mode (default: `approve`) |
24
+ | `/kaizen plan <request>` | Start a run and stop after the plan (`plan-only`) |
25
+ | `/kaizen auto <request>` | Start a run in `auto` mode: every stage back to back, no approval stops |
26
+ | `/kaizen lite <request>` | Run the stages in this session — cheap, reviewer has seen the work |
27
+ | `/kaizen full <request>` | Dispatch each stage as its own cold agent |
28
+ | `/kaizen run` | Resume the current run and execute the approved plan |
29
+ | `/kaizen review [target]` | Review-only: audit existing code, no plan, no implementation |
30
+ | `/kaizen status` | List every run grouped waiting-on-you / in flight / done / abandoned, plus open backlog count |
31
+ | `/kaizen backlog` | Print open backlog items across all runs, grouped by source run, rescued items last under `Orphaned` |
32
+ | `/kaizen approve` | Approve whatever the current run is waiting on |
33
+ | `/kaizen reject <reason>` | Reject it; the reason decides revise (back to planner) vs kill (abandon) |
34
+ | `/kaizen abort` | Mark the current run abandoned |
35
+ | `/kaizen config` | Show this project's settings and change them, one picker per setting |
36
+ | `/kaizen init` | Set up this project folder: `.kaizen/` with `spec.md`, config, and — in a git repo — the gitignore entry |
37
+ | `/kaizen install` | Install the workflow itself globally or per-project |
38
+
39
+ `/kaizen` with no argument: `status` if a run is in progress, else ask what to build.
40
+
41
+ ## Modes
42
+
43
+ Set by `mode` in config, overridable per invocation.
44
+
45
+ | Mode | What it does |
46
+ |---|---|
47
+ | `plan-only` | Produce the plan, write it, stop. No code touched. |
48
+ | `approve` (default) | Stop at every enabled approval, wait for an explicit decision. |
49
+ | `auto` | Run every stage back to back, no approval, report at the end. Fix loop still respects `max_iterations`. |
50
+ | `review-only` | Skip plan and implementation, run the reviewer against existing code or a diff. |
51
+
52
+ Mode never overrides a refusal: destructive/irreversible actions still confirm even in `auto`.
53
+
54
+ ## Approvals
55
+
56
+ - `approvals.plan` — after the plan, before any code is written.
57
+ - `approvals.review` — after the review passes, before the work is called done.
58
+ - `approvals.each_file` — confirm each file edit individually. Off by default; slow.
59
+
60
+ A rejection is written to the run directory, and is one of two things — the reason
61
+ says which. **Revise** ("do it differently") goes back to the planner, which produces
62
+ a new plan and returns to the same approval. **Kill** ("not doing this") means the
63
+ request itself was wrong: set `stage: "abandoned"` and stop, nothing to revise toward.
64
+
65
+ ## Fix loop
66
+
67
+ - `auto_fix.enabled: false` — every finding stops the run, becomes a user decision.
68
+ - `auto_fix.enabled: true` — builder fixes findings at or above `auto_fix.min_severity`,
69
+ reviewer re-checks, up to `auto_fix.max_iterations` rounds. Findings below threshold,
70
+ and anything still open when iterations run out, are reported to the user.
71
+
72
+ Each iteration is written to `runs/<id>/05-iterations/`.
73
+
74
+ ## Config knobs (`config.default.yml` defaults)
75
+
76
+ | Key | Default | Meaning |
77
+ |---|---|---|
78
+ | `preset` | `medium` | Quick preset (`low`, `medium`, `ultra`, `custom`) |
79
+ | `mode` | `approve` | Default run mode |
80
+ | `runner` | `full` | `full`: each stage a cold subagent. `lite`: this session runs every stage |
81
+ | `approvals.plan` | `true` | Stop after the plan |
82
+ | `approvals.review` | `false` | Stop after the review |
83
+ | `approvals.each_file` | `false` | Confirm every file edit |
84
+ | `build.executor` | `subagent` | Who builds: `subagent`, `inline` (main thread), or `ask` at the approval |
85
+ | `auto_fix.enabled` | `true` | Auto-fix findings vs. escalate everything |
86
+ | `auto_fix.min_severity` | `high` | Threshold for auto-fix (`critical\|high\|medium\|low`) |
87
+ | `auto_fix.max_iterations` | `2` | Fix/recheck rounds before escalating remaining findings |
88
+ | `track.default` | `auto` (inferred per run) | Pin a track for a repo that is always one kind of work |
89
+ | `review.write_memory` | `true` | Let the reviewer append lessons to `.kaizen/memory.md` |
90
+ | `backlog.enabled` | `true` | Capture what a run deliberately did not do |
91
+ | `backlog.file` | `06-backlog.md` | Per-run backlog file |
92
+ | `backlog.orphan_file` | `backlog.md` | Where a pruned run's open items are rescued to |
93
+ | `state.dir` | `.kaizen` | State directory |
94
+ | `state.keep_runs` | `20` | Completed runs kept before pruning oldest (rescues open backlog first) |
95
+ | `git.auto_commit` | `false` | Never commit/push/PR unless the user explicitly asks |
96
+ | `git.branch_before_implement` | `true` | Branch before implement stage when on the default branch |
97
+ | `subagents.model` | `inherit` | Model the stage subagents run on (opus/sonnet/haiku, Claude Code only) |
98
+ | `subagents.effort` | `inherit` | Reasoning effort for the stage subagents (low/medium/high, Claude Code only) |
99
+ | `agent.default` | `auto` | Coding agent to launch from dashboard backlog |
100
+ | `agent.<cmd>.model`, `.effort` | — | Model/effort flags for the launched session, per tool (`claude`, `codex`, `agy`, `opencode`, `gemini`); model names are the tool's own |
101
+
102
+ `.kaizen/config.yml` overrides only the keys it sets; everything else falls back to
103
+ this skill's `config.default.yml`.
104
+
105
+ ## State layout
106
+
107
+ ```
108
+ .kaizen/{spec.md, config.yml, memory.md, backlog.md (orphanage)}
109
+ .kaizen/runs/<id>/{00-request,01-plan,02-approval,03-impl,04-review}.md,
110
+ 05-iterations/, 06-backlog.md, state.json (id, mode, stage, awaiting, iteration, updated)
111
+ ```
112
+
113
+ ## Backlog and status
114
+
115
+ Backlog items: `- <status>: <item text>`, `<status>` one of `open`, `done`, `rejected`
116
+ (`rejected` carries a reason after `|`). `/kaizen backlog`/`status` are **read-only** —
117
+ never edit `06-backlog.md` or `state.json`, even to fix a spotted inconsistency; report
118
+ it and ask.
119
+
120
+ `/kaizen status` groups every run by `state.json` at read time: **Waiting on you**
121
+ (awaiting non-null, not abandoned), **In flight** (awaiting null, not done/abandoned),
122
+ **Done**, **Abandoned**, plus `Backlog: <n> open` (all runs + orphanage; abandoned
123
+ runs' items excluded).
124
+
125
+ ## More
126
+
127
+ Full walkthrough and rationale: `~/.claude/skills/kaizen/README.md`. Full stage
128
+ contract: `~/.claude/skills/kaizen/spec.md`. Codex/Antigravity porting:
129
+ `~/.claude/skills/kaizen/adapters.md`.
package/web/board.html CHANGED
@@ -27,6 +27,8 @@ pre.mark span:nth-child(1){color:var(--ink-hi)}pre.mark span:nth-child(2){color:
27
27
  .ib.pri{color:var(--accent)}
28
28
  .ib.sm{width:26px;height:26px}.ib.sm svg{width:15px;height:15px}
29
29
  /* dictation mic: accent while listening (pulsing) or still transcribing; digits while the model downloads */
30
+ /* the field being dictated into: accent border and a glow that swells with the voice level (--lvl, 0..1) */
31
+ .dictating{border-color:var(--accent)!important;box-shadow:0 0 0 calc(2px + 6px*var(--lvl,0)) color-mix(in srgb,var(--accent) 35%,transparent);transition:box-shadow .08s}
30
32
  .ib.mic.on{color:var(--accent);animation:pl 1.6s infinite}.ib.mic.busy{color:var(--accent)}.ib.mic i{font-style:normal;font-size:10px;font-variant-numeric:tabular-nums}.row .mic{margin-right:auto;align-self:center}
31
33
  .btn{border:1px solid var(--line);border-radius:var(--r);padding:5px 12px;color:var(--ink);background:var(--bg-2);min-height:30px;font-size:13px}
32
34
  .btn:hover{border-color:var(--dim)}
@@ -156,7 +158,7 @@ aside .foot:empty{display:none}
156
158
  .help.on{display:flex}.help div{background:var(--bg-2);border:1px solid var(--line);border-radius:var(--r);padding:20px 24px;line-height:2}
157
159
  .help kbd{display:inline-block;width:7ch;color:var(--accent)}
158
160
  @media(max-width:900px){main{grid-template-columns:1fr;padding-inline:16px;gap:8px}.hdr,.strip,.ftr{padding-inline:16px}section h2{position:sticky;top:0;background:var(--bg);z-index:1}.more{display:grid}.card:hover .age{display:inline}}
159
- @media(prefers-reduced-motion:reduce){aside,.toast,.mascot,.d-run .pulse,.ib.mic.on{transition:none;animation:none}}
161
+ @media(prefers-reduced-motion:reduce){aside,.toast,.mascot,.d-run .pulse,.ib.mic.on,.dictating{transition:none;animation:none}}
160
162
  </style>
161
163
  </head>
162
164
  <body>
@@ -229,7 +231,12 @@ const TFJS="https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.3.0",ASR_MO
229
231
  // Inference lives in a module Worker so a slow chunk never freezes the board.
230
232
  const WORKER_SRC=`import{pipeline}from"${TFJS}";let asr=null;
231
233
  onmessage=async e=>{const m=e.data;try{
232
- if(m.type==="load"){asr=await pipeline("automatic-speech-recognition","${ASR_MODEL}",{device:m.webgpu?"webgpu":"wasm",dtype:m.webgpu?{encoder_model:"fp16",decoder_model_merged:"q4"}:"q8",progress_callback:p=>{if(p.status==="progress")postMessage({type:"progress",file:p.file,pct:p.progress||0})}});postMessage({type:"ready"})}
234
+ if(m.type==="load"){const prog=p=>{if(p.status==="progress")postMessage({type:"progress",file:p.file,pct:p.progress||0})};
235
+ // WebGPU first, with the encoder in fp16 only where the adapter has shader-f16; any GPU failure falls back to the CPU.
236
+ const gpu=m.webgpu?{device:"webgpu",dtype:{encoder_model:m.f16?"fp16":"fp32",decoder_model_merged:"q4"}}:null,cpu={device:"wasm",dtype:"q8"};
237
+ try{if(!gpu)throw 0;asr=await pipeline("automatic-speech-recognition","${ASR_MODEL}",{...gpu,progress_callback:prog})}
238
+ catch(e){if(gpu)postMessage({type:"fallback",why:String(e&&e.message||e)});asr=await pipeline("automatic-speech-recognition","${ASR_MODEL}",{...cpu,progress_callback:prog})}
239
+ postMessage({type:"ready"})}
233
240
  if(m.type==="run"){const r=await asr(m.audio,{chunk_length_s:30,return_timestamps:false});postMessage({type:"text",sid:m.sid,seq:m.seq,text:(r.text||"").trim()})}
234
241
  }catch(err){postMessage({type:"error",sid:m.sid,seq:m.seq,error:String(err&&err.message||err)})}}`;
235
242
  // The worklet only forwards 128-frame blocks; chunking and silence detection stay on the main thread.
@@ -238,11 +245,13 @@ const HAS_MIC=!!(navigator.mediaDevices&&navigator.mediaDevices.getUserMedia&&wi
238
245
  // `active` is the one session listening; `sessions` also holds stopped ones still waiting on a chunk.
239
246
  let asrWorker=null,asrReady=null,asrProgress=()=>{},active=null,starting=false;const sessions=new Set();
240
247
  // One worker for the page; loading is awaited by whoever pressed first, later presses join.
241
- function loadAsr(){if(asrReady)return asrReady;asrReady=new Promise((res,rej)=>{
248
+ function loadAsr(){if(asrReady)return asrReady;asrReady=new Promise(async(res,rej)=>{
242
249
  asrWorker=new Worker(URL.createObjectURL(new Blob([WORKER_SRC],{type:"text/javascript"})),{type:"module"});
243
- const files={};asrWorker.onmessage=e=>{const m=e.data;if(m.type==="progress"){files[m.file]=m.pct;const v=Object.values(files);asrProgress(Math.round(v.reduce((a,b)=>a+b,0)/v.length))}else if(m.type==="ready")res();else if(m.type==="error"&&m.seq==null)rej(new Error(m.error));for(const s of sessions)s.onmsg(m)};
250
+ const files={};asrWorker.onmessage=e=>{const m=e.data;if(m.type==="progress"){files[m.file]=m.pct;const v=Object.values(files);asrProgress(Math.round(v.reduce((a,b)=>a+b,0)/v.length))}else if(m.type==="ready")res();else if(m.type==="fallback")toast("GPU could not run the model, using the CPU: "+m.why);else if(m.type==="error"&&m.seq==null)rej(new Error(m.error));for(const s of sessions)s.onmsg(m)};
244
251
  asrWorker.onerror=e=>rej(new Error(e.message||"worker failed"));
245
- const webgpu=!!navigator.gpu;if(!webgpu)toast("No WebGPU: dictation will run on the CPU and be slower");asrWorker.postMessage({type:"load",webgpu})});
252
+ // Ask the adapter what it can do: no adapter means the CPU path; no shader-f16 means an fp32 encoder.
253
+ let adapter=null;try{adapter=navigator.gpu&&await navigator.gpu.requestAdapter()}catch(e){}
254
+ if(!adapter)toast("No WebGPU: dictation will run on the CPU and be slower");asrWorker.postMessage({type:"load",webgpu:!!adapter,f16:!!(adapter&&adapter.features.has("shader-f16"))})});
246
255
  asrReady.catch(()=>{asrReady=null;try{asrWorker.terminate()}catch(e){}asrWorker=null});return asrReady}
247
256
  // After a re-render only a listener whose field is gone is stopped; closing the panel stops any.
248
257
  function stopDictation(all){if(active&&(all||!active.field.isConnected))active.stop()}
@@ -272,14 +281,14 @@ function mic(fields){
272
281
  const flush=()=>{if(!len||voiced<MIN){buf=[];len=0;voiced=0;quiet=0;return}const a=new Float32Array(len);let o=0;for(const c of buf){a.set(c,o);o+=c.length}buf=[];len=0;voiced=0;quiet=0;pending.set(seq,null);asrWorker.postMessage({type:"run",sid,seq,audio:a},[a.buffer]);seq++;b.classList.add("busy")};
273
282
  const drain=()=>{while(pending.has(done)&&pending.get(done)!==null){insert(pending.get(done));pending.delete(done);done++}if(!pending.size){b.classList.remove("busy");if(stopping){idle();sessions.delete(sess)}}};
274
283
  tap.port.onmessage=e=>{const c=e.data;buf.push(c);len+=c.length;let s=0;for(let i=0;i<c.length;i++)s+=c[i]*c[i];const rms=Math.sqrt(s/c.length);
275
- if(rms>0.01){voiced+=c.length;quiet=0}else quiet+=c.length;
284
+ if(rms>0.01){voiced+=c.length;quiet=0}else quiet+=c.length;f.style.setProperty("--lvl",Math.min(1,rms*8).toFixed(2));
276
285
  if(len>=MAX||(voiced>=MIN&&quiet>=SIL))flush()};
277
286
  // Chunk seqs are per session, so a result is matched by session id too.
278
287
  const sid=Math.random().toString(36).slice(2);
279
288
  const sess={btn:b,field:f,onmsg:m=>{if(m.sid!==sid)return;if(m.type==="text"){pending.set(m.seq,m.text);drain()}else if(m.type==="error"&&m.seq!=null){pending.set(m.seq,"");toast("Dictation failed: "+m.error);drain()}},
280
- stop:()=>{if(stopping)return;stopping=true;b.classList.remove("on");try{tap.port.onmessage=null;src.disconnect();tap.disconnect();stream.getTracks().forEach(t=>t.stop());ctx.close()}catch(e){}active=null;flush();drain()}};
289
+ stop:()=>{if(stopping)return;stopping=true;b.classList.remove("on");f.classList.remove("dictating");f.style.removeProperty("--lvl");try{tap.port.onmessage=null;src.disconnect();tap.disconnect();stream.getTracks().forEach(t=>t.stop());ctx.close()}catch(e){}active=null;flush();drain()}};
281
290
  sessions.add(sess);active=sess;starting=false;
282
- b.classList.remove("busy");b.classList.add("on");f.focus()};
291
+ b.classList.remove("busy");b.classList.add("on");f.classList.add("dictating");f.focus()};
283
292
  return b}
284
293
  const m=60e3,h=3600e3,d=86400e3;
285
294
  const age=(t,now)=>{const x=now-t;return x<h?Math.round(x/m)+"m":x<d?Math.round(x/h)+"h":Math.round(x/d)+"d"};