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.
- package/LICENSE +21 -21
- package/README.md +657 -657
- package/agents/kaizen-builder.md +77 -77
- package/agents/kaizen-planner.md +250 -250
- package/agents/kaizen-reviewer.md +127 -127
- package/cli/dashboard.ts +1187 -1187
- package/cli/install.ts +947 -947
- package/cli/publish-gh.sh +27 -27
- package/cli/release.sh +45 -45
- package/cli/settings.ts +415 -415
- package/cli/state.test.ts +197 -197
- package/cli/state.ts +704 -704
- package/cli/web.ts +423 -423
- package/commands/kaizen-abort.md +5 -5
- package/commands/kaizen-approve.md +5 -5
- package/commands/kaizen-auto.md +6 -6
- package/commands/kaizen-backlog.md +5 -5
- package/commands/kaizen-config.md +5 -5
- package/commands/kaizen-full.md +5 -5
- package/commands/kaizen-init.md +5 -5
- package/commands/kaizen-install.md +5 -5
- package/commands/kaizen-lite.md +5 -5
- package/commands/kaizen-plan.md +6 -6
- package/commands/kaizen-reject.md +6 -6
- package/commands/kaizen-review.md +6 -6
- package/commands/kaizen-run.md +5 -5
- package/commands/kaizen-status.md +5 -5
- package/package.json +1 -1
- package/skills/kaizen/README.md +281 -281
- package/skills/kaizen/SKILL.md +440 -440
- package/skills/kaizen/adapters.md +77 -77
- package/skills/kaizen/config.default.yml +167 -167
- package/skills/kaizen/spec-build.md +119 -119
- package/skills/kaizen/spec-main.md +104 -104
- package/skills/kaizen/spec-plan.md +201 -201
- package/skills/kaizen/spec-review.md +121 -121
- package/skills/kaizen/spec.md +153 -153
- package/skills/kaizen-help/SKILL.md +129 -129
- 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"){
|
|
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
|
-
|
|
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"};
|