agent-dag 3.22.1 → 3.22.4

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 (70) hide show
  1. package/README.md +6 -477
  2. package/package.json +14 -48
  3. package/shim.js +107 -0
  4. package/LICENSE +0 -661
  5. package/LICENSING.md +0 -82
  6. package/THIRD_PARTY_NOTICES.md +0 -395
  7. package/bin/agent-dag.js +0 -626
  8. package/bin/deck.js +0 -1805
  9. package/dist/web/assets/index-CJYsv0lr.css +0 -1
  10. package/dist/web/assets/index-Ifm23DDC.js +0 -270
  11. package/dist/web/index.html +0 -49
  12. package/hook/hook.js +0 -542
  13. package/release-notes.json +0 -398
  14. package/src/server/activity.mjs +0 -52
  15. package/src/server/agent-activity.mjs +0 -522
  16. package/src/server/args.mjs +0 -183
  17. package/src/server/auto-update.mjs +0 -79
  18. package/src/server/block-notify.mjs +0 -173
  19. package/src/server/boot-deadline.mjs +0 -127
  20. package/src/server/brand.mjs +0 -16
  21. package/src/server/browser-history.mjs +0 -497
  22. package/src/server/browser-presence.mjs +0 -211
  23. package/src/server/browser-profiles.mjs +0 -279
  24. package/src/server/browser-react.mjs +0 -284
  25. package/src/server/browser-watch-store.mjs +0 -350
  26. package/src/server/browser-watch.mjs +0 -905
  27. package/src/server/ccusage.mjs +0 -1168
  28. package/src/server/claude-accounts.mjs +0 -951
  29. package/src/server/claude-dir.mjs +0 -213
  30. package/src/server/codex-auth.mjs +0 -388
  31. package/src/server/codex-dir.mjs +0 -171
  32. package/src/server/codex-quota.mjs +0 -449
  33. package/src/server/codex-usage.mjs +0 -512
  34. package/src/server/cswap-admin.mjs +0 -1562
  35. package/src/server/cswap-auto.mjs +0 -658
  36. package/src/server/cswap-install.mjs +0 -641
  37. package/src/server/deck-home.mjs +0 -243
  38. package/src/server/deck-prefs.mjs +0 -301
  39. package/src/server/deck-probe.mjs +0 -111
  40. package/src/server/detach.mjs +0 -244
  41. package/src/server/exec.mjs +0 -996
  42. package/src/server/global-install.mjs +0 -67
  43. package/src/server/hwmonitor.mjs +0 -56
  44. package/src/server/index.mjs +0 -6043
  45. package/src/server/installer.mjs +0 -912
  46. package/src/server/invoked-as.mjs +0 -144
  47. package/src/server/lan-about.mjs +0 -119
  48. package/src/server/lan-engine.mjs +0 -952
  49. package/src/server/lan-reach.mjs +0 -256
  50. package/src/server/lan-socket.mjs +0 -682
  51. package/src/server/lan-sync.mjs +0 -941
  52. package/src/server/lhm-parse.mjs +0 -91
  53. package/src/server/log-tail.mjs +0 -139
  54. package/src/server/log-writer.mjs +0 -322
  55. package/src/server/login-service.mjs +0 -473
  56. package/src/server/macmon.mjs +0 -310
  57. package/src/server/npx.mjs +0 -264
  58. package/src/server/open-url.mjs +0 -242
  59. package/src/server/presence.mjs +0 -40
  60. package/src/server/quota.mjs +0 -792
  61. package/src/server/relay-guard.mjs +0 -507
  62. package/src/server/reset-label.mjs +0 -78
  63. package/src/server/retire-sound-hook.mjs +0 -349
  64. package/src/server/running-deck.mjs +0 -234
  65. package/src/server/self-update.mjs +0 -1380
  66. package/src/server/stop-deck.mjs +0 -171
  67. package/src/server/supervisor.mjs +0 -392
  68. package/src/server/system-metrics.mjs +0 -1825
  69. package/src/server/term.mjs +0 -686
  70. package/src/server/uv-bootstrap.mjs +0 -337
@@ -1,398 +0,0 @@
1
- {
2
- "//": [
3
- "What a user would NOTICE. One key per version that has something to say,",
4
- "and no key at all for a version that has nothing — which is most of them.",
5
- "",
6
- "The rule this file exists to serve: before every release, ask 'what would a",
7
- "user notice?' and write the answer here. 'Nothing user-visible' is a valid",
8
- "and expected answer, and it is spelled by adding no key. A release with no",
9
- "key shows no modal, and that silence is what keeps the modal worth reading",
10
- "on the day it is not silent.",
11
- "",
12
- "What belongs here: something that changed on the user's machine or under",
13
- "their hands — a hook written to or removed from settings.json, a default",
14
- "that flipped, a control that moved or went away, a stored preference that",
15
- "changed meaning. What does not: bounds, races, assertions that could not",
16
- "fire, anything that is invisible when it is correct.",
17
- "",
18
- "Write it for the person running the deck, not for the person reading the",
19
- "diff. `title` is the sentence they scan; `body` is what they need in order",
20
- "to act on it, or to stop worrying about it.",
21
- "",
22
- "Keys are versions exactly as package.json spells them. A key that is not a",
23
- "version, or an entry missing either field, fails the suite rather than",
24
- "being quietly dropped — see src/web/__tests__/release-notes-file.test.ts.",
25
- "Ordering here is for humans; the deck sorts by version itself.",
26
- "",
27
- "A title may open with an emoji, and if it does, put exactly ONE space after",
28
- "it. Not two: the dialog gives a leading emoji its own optical gap, because",
29
- "a colour glyph is drawn edge to edge inside its advance and one word space",
30
- "after one reads as none. A second space here would be a lie in the data",
31
- "about a defect in the type, and the suite refuses both it and no space at",
32
- "all."
33
- ],
34
- "3.21.0": [
35
- {
36
- "title": "🖼️ The deck explains itself in pictures",
37
- "body": "🎬 First run opens an eight-picture tour instead of the changelog — who is waiting on you, the tree, a node's inside, cost and quota, this machine, accounts, local network, how to start. `Take the tour` on an empty canvas brings it back.\n\n📡 **Local network** has a guide: a card while it is off, `How it works` under an empty list, four pictures ending on `Turn it on`. A machine's row opens its own dialog — address, version and system, the logins it offers, and a name of your own for it. Setup ends on Done.\n\n🔄 The deck updates itself when nobody is looking: no tab focused, no agent mid-turn, thirty seconds quiet. The banner's `auto when idle` switch governs it.\n\n🔑 An account whose numbers were paused resumes on its own, and the panel is titled **Claude accounts** — a Codex login is not in it."
38
- }
39
- ],
40
- "3.20.0": [
41
- {
42
- "title": "🟢 The deck keeps running after you close the terminal",
43
- "body": "`ccdeck` prints the same boot report and gives you the prompt back. The deck stays up, and starts again when you log in.\n\n🛑 **`ccdeck --stop` is the off switch now** — Ctrl+C only cancels a start that is still printing. `--status` says what is running, `--logs` shows what the deck wrote while you were away, and `--foreground` holds the terminal the way every version before this one did.\n\n🔁 Typing `ccdeck` beside a deck that is already up opens **that deck's tab** instead of building a second one. `ccdeck --stop --all` clears any older deck still running from before this version. An `npx` run cannot start at login — `npx ccdeck --install` puts it on your PATH and sets that up.\n\n🔑 An account that will not collect now says which of three things is wrong: **no stored login**, a **login that expired**, or a **keychain the deck cannot open** — with the fix beside it. One with no stored login is filled over the local network by a paired deck that has it.\n\n📡 And decks find each other again on a machine with a VPN up: the beacon was going to an address that never left the machine, so this deck could hear colleagues and none of them could hear it."
44
- }
45
- ],
46
- "3.19.0": [
47
- {
48
- "title": "🟢 Local network: fixes and improvements",
49
- "body": "The list of decks is quiet now. A deck that is **on**, with nothing to repair, is a name and a green mark — no sentence under it. Every healthy row used to carry the same words, so the one row worth reading looked like all the others. A failure, a login that arrived, a machine that is away, still say so, and they are now the only rows with anything written on them.\n\n`unpair` arrives when you point at a row rather than sitting on every one of them, two machines that share a name show their addresses, and the ↻ turns while a round is out.\n\n**Add a deck** is two ways in — by address, or with an invite — with `make one to send` beside the invite heading instead of under the paste field, where it read as that field's button. A refused address now says which mistake it was, and an invite that will not work says whether it ran out, was not an invite, or reached nothing.\n\nSwitching **Local network** on opens your name and shared logins, every time, so nothing goes on the network without you having just seen what goes with it."
50
- }
51
- ],
52
- "3.18.3": [
53
- {
54
- "title": "🟢 The list shows what is on, and folds the rest",
55
- "body": "A machine that is off, or nearby and unpaired, or one you said no to, is context rather than an answer to \"who can I use right now\" — so the decks that are **on** lead the list and everything else is one line: `2 more`. Nothing worrying is hidden by it: a deck that cannot be reached is counted in that line, in the warning colour, so the fold says whether it is worth opening.\n\nThe round moved to a **↻ beside the switch**, where the accounts header has kept the same act since it was written, and the line under the state now says when it last ran — `checked 2m ago`, or `not checked yet`. A list that refreshes itself and a list that has stopped used to look identical, and pressing the button to find out was the only way to tell."
56
- }
57
- ],
58
- "3.18.1": [
59
- {
60
- "title": "🔁 A deck stops asking to pair over and over",
61
- "body": "Several decks on one computer share a config directory, so they start with one key. Each read the others' beacons as somebody wearing its name, took a fresh key, and wrote it to the file the others read — a loop that never settles. Every new key is a **new deck** to everyone else on the network, so one machine sent a fresh pairing request every few seconds and accepting one accomplished nothing: the deck that asked no longer existed by the time the answer arrived.\n\nDecks on one computer now recognise each other as the same machine and leave the key alone. The clash still worth healing — a `~/.claude` copied to a second machine — is unchanged.\n\nThis also collapses the rows: a computer that has run the deck a few times appeared several times on everybody else's panel and is one row now. **Both machines need this version** for that to take effect on the other one's screen.\n\nA deck that answered is no longer counted as unreachable, the list refreshes itself in seconds while somebody is deciding rather than at the next minute, and a failed round says what happened — `not listening`, `no route to it` — with the address and the raw error on the hover."
62
- }
63
- ],
64
- "3.18.0": [
65
- {
66
- "title": "🟢 A deck that is switched off no longer reads as on",
67
- "body": "A paired deck this one has no address for was drawn **live** on the strength of being paired — a machine closed an hour ago still sat under `1 deck ready`. Every row now leads with whether that machine is actually there, and when it last was: `online · 1 login arrived`, `no answer · last online 20m ago`, `never reached`. The mark beside the name is a traffic light — green for a deck that answered, amber for one that cannot be reached, grey for one nothing has heard from.\n\n**+ add a deck** opens a dialog now instead of unfolding inside the panel. An address, an invite and the firewall note never fitted a 288px column, and unfolded they turned a list of machines into a form."
68
- }
69
- ],
70
- "3.17.0": [
71
- {
72
- "title": "🔔 Pairing is two presses",
73
- "body": "When another deck asks to pair, the question now arrives **over the deck** rather than in a row three sections down a panel: its name, its address, the fingerprint to compare, **Accept** or **Decline**. Escape means later — the request stays in the panel until you answer it.\n\nThe rest is one list. Every deck you are paired with, every deck on the network, and every one you turned away, each a row with its state under the name and one verb at the end — and a deck that cannot be reached now reads differently from one that is fine. Saying no also reaches the other machine now, instead of leaving it waiting forever; you can take it back.\n\nThe dialog behind **setup** is down to the two things it is really about: what this deck is called, and which logins it offers."
74
- }
75
- ],
76
- "3.15.0": [
77
- {
78
- "title": "🔗 Decks on one network heal each other's dead logins",
79
- "body": "A login dies on the machine that has not used it while the same account keeps working on the machine that has. The fix was a blob copied out of one deck and pasted into another. Now: **Accounts ▸ Local network**, the same passphrase on each deck — it offers you six words — and they find each other and repair themselves.\n\nA deck only **adds** an account it does not have, or **replaces one it has proven dead** — proven by Anthropic refusing the token, not guessed. A credential that works is never touched. The passphrase is the only gate; a stranger on the same wifi sees that a deck exists and nothing else."
80
- },
81
- {
82
- "title": "⚠️ What sharing a login actually means",
83
- "body": "**A login you share is a live one, and it cannot be taken back.** Turning the switch off stops what has not happened yet; a credential that has left your machine is gone, and the only real revocation is signing in again at Anthropic — which ends the session everywhere at once.\n\nSharing a Claude account may also be against Anthropic's terms. Your call; the deck says it once and does not nag."
84
- }
85
- ],
86
- "3.14.0": [
87
- {
88
- "title": "📊 The machine comes back beside the list that covers it",
89
- "body": "**Busiest processes** opens over the machine panel and hides the very readings that sent you looking. They are back beside the list — cores, memory, swap, queued work, temperature — with up to an hour behind each: a column down the side on a wide screen, a band under the table on a narrow one.\n\nEach is drawn against the line it is measured to — memory and swap at 90%, load at your core count, the GPU at 75° and again at 90° — so swap sitting *on* its line is something you see rather than work out. The processor readings get no line on purpose: a CPU at 90% is your machine doing what you asked for. Throttling appears only once your machine has actually been held back, and the band says how much history it really has rather than claiming an hour it has not had."
90
- },
91
- {
92
- "title": "📋 The process dialog's footnote moved onto the columns it was about",
93
- "body": "A band ran along the bottom of **Busiest processes** saying three things. Two were worth keeping and are now on the header of the column they describe — hover `memory` to read what that number measures, `process` to read that the command-line redaction is a filter rather than a promise.\n\nThe third said the list is the busiest by processor and memory, refreshed every four seconds. The heading, the count beside it and the moving numbers were already saying that."
94
- },
95
- {
96
- "title": "📝 These notes render what they were written in",
97
- "body": "Every note in this dialog is written in Markdown — **bold** for the sentence that carries the change, backticks for a flag or a path — and the dialog printed the string raw. So the first window you see after an upgrade has been showing you `**this**`, backticks and all, since the feature shipped: 25 of 52 notes, 52 bold spans, 54 code spans.\n\nWhat you are reading is the first version that does not.\n\nNo Markdown library came along for it. Counted in the file: bold 52, code 54, links 0, headings 0, lists 0 — and the smallest library that reads all of Markdown is 40KB on a bundle the deck fetches while you wait for it to start."
98
- },
99
- {
100
- "title": "⎋ Esc leaves the machine panel where it is",
101
- "body": "The × on **This machine** read `Close (Esc)`, so Escape put the panel away. Escape belongs to the canvas — it deselects and hands the keyboard back — and pressing it while you are reading your instruments should not close them.\n\nThe panel now closes the two ways it opens: its ×, and the meter in the topbar. The × says `Close`, and names no key it does not answer."
102
- },
103
- {
104
- "title": "🖥 The machine moved out of the topbar and into a button",
105
- "body": "The small CPU sparkline and memory bar in the topbar are gone. A chip glyph in the icon row beside `$` and the accounts silhouette opens the same **This machine** panel — cores, memory, swap, load, temperature, busiest processes — and the bar stops moving in the corner of your eye.\n\nIt also stops asking. The deck read your machine every three seconds for the life of every tab to feed that meter; it now reads only while the panel is open, and the history behind the readings is kept by the deck itself, so a panel opened an hour from now still has the hour behind it."
106
- },
107
- {
108
- "title": "🔎 The panel's eight process rows became one way in",
109
- "body": "The machine panel drew eight processes in a 280px box: three columns, names cut off mid-word, and a `ps` run every four seconds to keep them moving. **Busiest processes** is a single row you press now, and the dialog behind it is what it always was — every candidate the deck sees, its memory in bytes, threads, user, pid, and the command line that tells you which `node` this one is.\n\nWith that dialog closed, the deck reads no process list at all."
110
- },
111
- {
112
- "title": "⏱ The restart banner says whether the press is safe, and shows the fuse",
113
- "body": "The button read `Restart now` whether the machine was idle or five agents were mid-turn — and the events fired while the server is down are lost for good. It reads **`Restart anyway`** now when something is running, with the count beside it, and `Restart now` with *nothing is running* when the machine is quiet.\n\nThe automatic restart said only `auto when idle`, while a 30-second clock ran where nobody could see it. It counts — **`auto in 18s`** — which is also how you stop it, since the switch is the label. The amber dot pulses only while that clock runs.\n\nThe switch also says `auto-restart off` in words rather than in a colour, both its label and the button are readable in the light theme now, and a tab you are not looking at no longer restarts the deck under the tab you are."
114
- }
115
- ],
116
- "3.13.0": [
117
- {
118
- "title": "📊 Usage history stops jumping out from under you",
119
- "body": "Pressing **H** put `running ccusage…` in a box 186px tall. Two to four seconds later the chart, the totals and the legend arrived and the box became 429px — and because the dialog is centred, that growth moved the whole thing **121px upward**, under the cursor that had just pressed something.\n\nThe chart's place is now reserved from the first frame: the 168px the chart is tall plus the 16px it keeps for its day labels, both taken from the chart's own rule so the placeholder and the thing it holds room for cannot drift apart. The jump goes from 243px to 156px. It deliberately stops there — the totals and the legend are content, the legend's height depends on how many models ran, and reserving those would have meant drawing four plausible empty cards for a reading that had not arrived.\n\nWhat is drawn in the reserved space is **the chart's baseline and nothing else**: one line, in the same colour, at the height the columns will land on. Not a skeleton of grey bars — a skeleton stands in for content, an axis is furniture, and furniture is honest before the data exists. The light on it travels rather than fills, because the deck knows a subprocess is running and does not know how far through your transcripts it has got.\n\nThen the days rise out of that line, **oldest first**. The stagger is divided rather than fixed — 200ms of spread whether the range is 7 days or 90 — so the longest range is not also the slowest to appear.\n\nWith reduced motion both loops and both entrances are off. The reserved space is layout, not animation, and stays outside that rule: asking for less movement gets you the same dialog that does not jump."
120
- },
121
- {
122
- "title": "⏳ The period mark says it is being read",
123
- "body": "The first read of **today**, **month** or **all** takes two to three seconds — it walks the transcripts on this machine, and the cost is the walk, not the size of the range you asked for. Until now the panel said nothing for the whole of it, so pressing `month` looked like pressing nothing, and the honest response was to press it again.\n\nThe mark under the period now pulses while that read is in flight, and it **pulses rather than fills**: nothing measures how far through the transcripts ccusage has got, so a bar that filled would be inventing a number. Hovering the pending period says which one is being read, and screen readers are told the same thing through a live region rather than being left with a silent button."
124
- }
125
- ],
126
- "3.12.0": [
127
- {
128
- "title": "🔍 Busiest processes tells you which one",
129
- "body": "The list could say what was eating the machine and not **which** of nine identical `Google Chrome Helper` rows was the headless Chrome you left running. It now shows the arguments after the name — `--type=renderer` beside one, `serve admin-portal --port 44440` beside a node — and four columns it did not have: **memory in MB and GB** instead of a percentage, **threads**, **how long it has been up**, and **user**. Every column sorts; press its heading.\n\n**Command lines have anything secret-shaped removed before they leave your machine** — the value of any flag whose name says token, password, key or secret, the password in a URL, and the shapes that identify themselves. It is a filter and not a guarantee: a secret that looks like an ordinary word looks like an ordinary word. The reading happens only while this modal is open, so a deck whose process list you never open never reads a command line at all.\n\n**Memory is resident set size** — what `ps` and Windows Task Manager report. On macOS this is *not* the number Activity Monitor shows in its Memory column; that one is the physical footprint, and on the machine this was built on the two disagree by up to fourteen times on the same process. No unprivileged command reports the footprint quickly, so the deck shows the figure that means the same thing on all three platforms and says which one it is.\n\n**There is no Ports column**, and the reason is the same kind of honesty: Mach ports do not exist on Linux, are not what Windows calls a handle, and on macOS the only way to count them takes four seconds — against a list that refreshes every four."
130
- },
131
- {
132
- "title": "🔢 A token count that stays a whole number",
133
- "body": "Switching the Usage panel from **month** to **today** counted down smoothly and then, for a fraction of a second, showed something like `in 594.3268260610639` where a token count belongs.\n\nThe count was never wrong; the printing was. The figures animate between two readings, so the formatter is handed a fraction on most of its frames — every size above a thousand was already rounded and the one below it was not."
134
- }
135
- ],
136
- "3.11.0": [
137
- {
138
- "title": "🗂 The session list waits to be asked for",
139
- "body": "**BY SESSION in the Usage panel now starts collapsed.** It is the one block in that panel with no ceiling — every other section is two or three rows, or a model table bounded by the models that exist — and it was the reason the panel scrolled at all. Shut, the whole panel is one screen: quota, period, money, models. Press the heading to open it, and it stays open; ccdeck remembers your answer from then on.\n\nThe count of what is behind it is on the heading's tooltip, so collapsing it does not hide how much there is.\n\n**Scrollbars are also gone until you point at the thing that scrolls.** Nothing moves when they appear — the track keeps its width in both states, so no text shifts under the pointer as it arrives."
140
- },
141
- {
142
- "title": "📊 today · month · all, as tabs that remember",
143
- "body": "**The Usage panel's period switcher is a full-width row of tabs** rather than a pill floating over the column, with a thin cyan mark under the word it belongs to. It governs the figures beneath it, so it now looks like it does.\n\n**Your chosen period survives a reload.** It was the one preference in that panel that did not.\n\n**It costs one Tab stop instead of three.** Tab into the strip, then move with ←/→ or Home/End, and press Enter to choose. Arrowing past a period no longer starts reading it — moving and choosing are separate now, which matters on `all`.\n\nAnd **`month` says which month.** Hover it: this is the calendar month, from the 1st — not the last 30 days. `today` and `all` say what they cover too."
144
- },
145
- {
146
- "title": "🔔 Sounds and Notifications are two switches, and Browser notifications is a channel",
147
- "body": "**The sound menu has been rebuilt.** Sounds and Notifications are real switches now instead of words that read like status. Under them, **Browser notifications** is named as what it is — one of the two ways ccdeck can reach you — rather than sitting there as a third setting that seemed to contradict the one above it.\n\nThere are two notifiers and they never both fire: the page raises one when the deck is open but hidden, and the server raises one when no page is open at all. Only the first needs a browser permission, which is why the permission belongs to a channel and not to the feature. **Turning Notifications on now asks the browser on that same press** — no second control to hunt for.\n\n`M` mutes and unmutes sounds from anywhere. **Hear it** plays the tone even when Sounds is off, so you can pick one before turning them back on."
148
- },
149
- {
150
- "title": "🧹 No agent, no empty panel",
151
- "body": "**The detail panel no longer appears when nothing is selected.** It used to sit there empty, and a tab on the edge of the canvas existed to bring it back after you closed it — both are gone. Select an agent and the panel is there; press **D** to toggle it yourself.\n\nThe canvas is that much wider whenever you are not reading one agent."
152
- }
153
- ],
154
- "3.10.0": [
155
- {
156
- "title": "🔌 Who else can drive your browser, and where to see it",
157
- "body": "**Browser Watch has a Remote control section.** The Claude in Chrome extension holds a connection open to Anthropic's relay, and that connection is registered against your **account**, not against this machine — so any Claude Code session signed in to the same account, running anywhere, can enumerate and drive this browser, and nothing prompts you on the machine being driven.\n\nThe panel now says whether that is the case here, and what the extension is actually allowed to do in each profile — enabled or switched off, every site or only the ones it was granted, and which of the powerful APIs it holds. That last part is not what `chrome://extensions` shows you: the per-site list there is enforced by the extension, on itself, while the grant underneath stays exactly as wide as it was.\n\nIf you want it closed, the section hands you the one command that closes it — as text, to paste in your own terminal. **ccdeck never runs it and never asks for your password**, and it never will: no part of this deck elevates, and a dashboard that could raise a password box on request would be a gift to anything else running on the machine. If the block is already in place, the section offers the command that takes it back out. And if something else in your hosts file already points that name somewhere, it says so and leaves the line alone — ccdeck only removes the line ccdeck wrote.\n\nAll of this is two file reads and no network call. It was written some releases ago and, until now, was not connected to any screen.\n\n**Also:** the README said `share` produces a `ccdeck1:` blob. It produces `ccdeck2:` — `ccdeck1:` has been read-only legacy for a while."
158
- }
159
- ],
160
- "3.9.0": [
161
- {
162
- "title": "🎛️ Five switches that were not where the thing they control is",
163
- "body": "**The session list has a button again.** It lost one a while back and only the `L` key was left — and `L` is written down in one place, a shortcuts sheet behind a small `?` in the corner of the canvas. If you use a mouse there was no way to open the list at all. The `☰` is back at the left of the top bar.\n\n**The Notifications switch now tells you whether the browser will actually show one.** It said `on` while this browser had never been asked for permission, which is not the same thing. It now says so on that row and offers the ask right there. Before, the only place to grant it was a button that appears solely while a session is stuck waiting — so on a machine where that rarely happens you could not switch notifications on at all.\n\n**Started with `--workspace` or `--scope` and seeing nothing?** The empty screen used to tell everyone to \"run `claude` or `codex` in any folder\", which is the one thing that cannot work for you. It now names the folder the deck is actually watching.\n\n**Browser Watch says when a setting did not save.** Flipping the switch and having it fail showed a red line reading \"Could not read\" — about reading history, not about your press — and ten seconds later the panel wiped it and looked perfectly healthy with the setting unchanged. A failed save now says so, in its own words, and stays until you dismiss it. The \"nobody browsing for\" menu also follows the stored value again: change it in a second tab and this one never caught up.\n\n**The deck no longer restarts itself when nothing on screen offers to stop it.** Thirty seconds after the last agent went quiet, with a new version on disk, the server would exit, come back and reload the page. The `auto when idle` switch that controls that lives inside the update banner — so if you had closed the banner with its `×`, it happened with no visible setting anywhere. Closing the banner now also stands down the restart; clicking the version chip brings back both together."
164
- }
165
- ],
166
- "3.8.9": [
167
- {
168
- "title": "🖥️ Four things that only went wrong on somebody else's operating system",
169
- "body": "**Ubuntu:** the deck knew where Chromium and Brave keep their profiles when installed as a snap or a flatpak — which on a default Ubuntu is the only place Chromium is — but not what those programs are called. So it reported them as not running while they were, and refused to quit them. Both now work.\n\n**Windows:** the deck asked the system whether it could write to the folder an update installs into, and Windows always answers yes for a folder whatever the permissions say. It offered you an update that then died inside npm with a permission error. It now finds out by trying, and declines up front with the command to run yourself — which is what every other platform already got.\n\n**Linux:** if `XDG_DATA_HOME` was set to something starting with `~`, as it is from a systemd unit or a Dockerfile, the deck and claude-swap looked in two different places for your accounts. The panel said you had none while `cswap list` showed them all.\n\n**Plain terminals:** six messages used a typographic dash the old Windows console and the Linux text console cannot draw, so a box appeared mid-sentence — including in the line that explains why no events are arriving. They use the plain form on those terminals now, like everything else already did."
170
- }
171
- ],
172
- "3.8.8": [
173
- {
174
- "title": "📉 A false 99% on the memory chart, and three other quiet failures",
175
- "body": "**The machine meter no longer invents a reading.** When the deck could not measure memory — a subprocess that would not start under load, or one that took too long — it substituted a number that means something else, and on a Mac that reads as about 99% used. The meter went red for half a minute, and because the chart keeps the highest value in each minute, that false peak stayed on it for a full day. A poll that cannot measure now keeps the last reading and records nothing: a gap in the chart is honest.\n\n**A failed ccusage repair no longer disables the repair.** If the deck found a half-installed copy it removes it and starts again. On Windows that removal can fail for a few seconds because the program it just ran still has the folder open — and the deck then treated the attempt as spent and never tried again for the rest of the session, leaving the panel permanently unable to read your usage. It now only counts a repair that happened.\n\n**Changing the auto-switch interval takes effect even during startup.** Set it in the first seconds after launch and the timer kept the old value for the life of the process, while the panel showed the new one.\n\n**Codex sessions with very large histories no longer cost hundreds of megabytes to read.** The deck read the whole file to find four values that live at its two ends — measured at 185 MB of memory and 95ms of frozen interface for one 44 MB session. It now reads only the ends."
176
- }
177
- ],
178
- "3.8.7": [
179
- {
180
- "title": "🧠 Four things in the panel that behaved wrongly",
181
- "body": "**Today's figure no longer overshoots.** Once a minute, when a fresh reading landed, the headline briefly counted past the true number and back down — it was measuring the last minute of spend from a starting point one moment out of date. The reading and its starting point are now recorded together, so there is no moment where they can disagree.\n\n**Browser Watch marks findings read when you close it, not when you open it.** It was clearing the badge immediately, and stamping every new finding as read the moment it arrived — so anything that turned up while you were reading was never badged at all. It was also writing to storage four times a second the whole time the panel was open.\n\n**A hidden tool category can always be brought back.** Hide one, leave the deck running, and once the canvas had turned over the row of chips disappeared while the filter stayed on — no bubbles, and nothing to press. Only reloading fixed it. The chips now stay for as long as anything is hidden.\n\n**Clicking a session's name no longer switches auto-fit off.** The deck was reading its own camera move as you grabbing the canvas, and remembered it across restarts, with nothing said."
182
- }
183
- ],
184
- "3.8.6": [
185
- {
186
- "title": "🗂️ Two decks can no longer both claim the same log",
187
- "body": "When several decks share one events log they elect a single writer for it, so nothing is recorded twice. That election compared the path as typed — so two decks reaching the SAME file by different spellings each thought they were alone, both wrote every event, and each believed the log was theirs to clear.\n\nDifferent spellings are ordinary, not exotic: a shortened Windows home directory, a mapped or substituted drive, a junction, or on macOS `/tmp` against `/private/tmp`. The visible symptoms were duplicated tool calls after a restart, and Clear wiping history belonging to another deck.\n\nThe path is now canonicalised the same way the watched folder already was. It works on a first run too, before the log file exists."
188
- }
189
- ],
190
- "3.8.5": [
191
- {
192
- "title": "⛔ Cancelling a sign-in no longer adds the account anyway",
193
- "body": "Press Escape while adding a Claude account and the deck killed the sign-in — and then, sometimes, registered the account regardless, switched to it, and told you it had worked.\n\nThe deck asks the tools it runs whether they finished cleanly. A program that is well behaved about being cancelled tidies up and exits with a success code, and the deck was reading that as \"it finished\" rather than \"I stopped it\". `claude auth login` is exactly that kind of program.\n\nA cancelled run now reports as cancelled. Everything that was not cancelled is unaffected."
194
- }
195
- ],
196
- "3.8.4": [
197
- {
198
- "title": "🧷 Your own hooks can no longer be eaten by two decks starting together",
199
- "body": "If you keep hooks of your own in `settings.json`, the deck parks them while it installs and puts them back afterwards. Two decks starting at the same moment could interleave inside that, and the loser wrote a version of the file computed before the restore — so your hook was gone from `settings.json` and gone from the park, which was its only other copy.\n\nThere was already a check for this, and it had a hole: if the last-moment re-read of the file failed, it read that as \"nothing changed\" and wrote anyway. A failed read is the one moment that check was most needed, because the usual reason a file cannot be read for a few milliseconds is that something else has just written it.\n\n`ccdeck --uninstall` had no such check at all and could overwrite a deck that was starting, then exit 0 saying it had succeeded. It has one now, and when it declines it says so and tells you to run it again rather than reporting that there was nothing to remove."
200
- }
201
- ],
202
- "3.8.3": [
203
- {
204
- "title": "🪟 Windows: two settings files that could refuse to save",
205
- "body": "On Windows a file cannot be moved into place while anything else has it open, and Defender and the search indexer open a file the instant it is written. The deck has had a retry for that for a long time, and two files written in the last few days were not using it: the notifications preference, and Browser Watch's episode archive.\n\nWhen it bit, the notifications switch silently did not stick, and Browser Watch answered an error and showed an empty panel. Both now retry the way everything else in the deck already did.\n\nNothing changes on macOS or Linux, where the operating system never refused the move in the first place — which is why this got past a green test suite on both."
206
- }
207
- ],
208
- "3.8.2": [
209
- {
210
- "title": "⌨️ A closed-behind-your-back dialog no longer kills every keyboard shortcut",
211
- "body": "Open the context ring on a session's card and leave it open. The session finishes, and a couple of minutes later the canvas evicts it — or you press Clear. The dialog vanished, and from that moment every shortcut in the deck stopped working: Space, C, R, F, L, H, U, A, J, K, T, M, and the trash button too. Only reloading the page brought them back.\n\nThe deck was still counting the dialog as open, because it remembered which session you had opened it for rather than whether anything was on screen. It now closes the dialog when its session leaves the board, which is what should have happened in the first place. The end-of-session recap had the same problem and is fixed with it."
212
- }
213
- ],
214
- "3.8.1": [
215
- {
216
- "title": "🔒 Security: update if the deck runs on a Mac",
217
- "body": "A local process able to reach the deck's port — no token, no browser, no tab — could get a string of its choosing compiled as AppleScript when the deck raised a desktop notification with no page open. On macOS that is code running as you.\n\nThe strings were already passed as arguments rather than pasted into the script, which is normally the whole defence. It is not enough for `osascript`: its option parser reads any argument starting with a dash as an option, so a name beginning with `-e` was taken as more script instead of as text. Every place the deck hands `osascript` or `notify-send` something from outside now carries an explicit end-of-options marker, so nothing from outside can be read as an instruction again.\n\nNothing to change on your side beyond updating. If you would rather keep the deck off the desktop entirely, the Notifications switch in the sound menu does that, and `AGENTS_DECK_NO_NOTIFY=1` does it at launch — but the fix is the thing to have."
218
- }
219
- ],
220
- "3.8.0": [
221
- {
222
- "title": "🔕 Notifications now have an off switch",
223
- "body": "3.7.0 gave you a \"notify me\" button and no way back. Turning them off meant digging into your browser's site settings — which no page can do for you — and that still left the other notifier, the one the deck raises when no page is open at all. Silencing that one meant quitting the deck and starting it again with `AGENTS_DECK_NO_NOTIFY=1` in front. The person who wanted quiet was the one doing the work.\n\nThere is a **Notifications** switch in the sound menu now, beside the one for sound — that menu is already \"how loudly does this deck interrupt me\". One switch governs both notifiers, it takes effect immediately without a restart, and it is remembered across restarts.\n\nIt is kept by the deck rather than by your browser, because the second notifier runs when no page exists and a setting living in a page could never reach it. `AGENTS_DECK_NO_NOTIFY=1` still wins over the switch — a machine told at launch to stay off the desktop stays off it — and when that is the case the menu says so instead of showing an \"on\" that will not happen."
224
- }
225
- ],
226
- "3.7.1": [
227
- {
228
- "title": "🔔 The notifications now fire on the thing that actually stops your agent",
229
- "body": "3.7.0 could tell you a session was waiting — but only when Claude Code asked permission to run a tool. If you work with permissions bypassed, that never happens, and the whole feature was silent for you. Measured on one real log: 1683 events, every one of them in bypassPermissions, and a single permission prompt in the entire history.\n\nWhat fires constantly on that setup is the other kind: the agent asking you a question and stopping for the answer. The deck was throwing those away — no amber chip, no row in the sidebar, no notification, and no \"notify me\" button, because the button only appears while something is actually blocked.\n\nNow they count. The card carries the question itself rather than a generic \"Your turn\" — \"paycore needs your input: merge both branches to main, or just one?\" — and so does the notification, on the desktop, whether the tab is behind something or not open at all.\n\nAn idle prompt still stays quiet: a turn that ended is not a session that is stuck, and a channel that cries wolf is one you switch off."
230
- }
231
- ],
232
- "3.7.0": [
233
- {
234
- "title": "🔔 The deck can reach you when you are not looking at it",
235
- "body": "The deck's whole point is telling you which agent is waiting on you, and until now it could only say so on screen — the amber chip, the tab title, the favicon. Every one of those is drawn inside a page, so the one moment it matters, when you have walked away and four agents have gone quiet, the deck was silent.\n\nNow it reaches the desktop, two ways that never overlap. With the deck open behind something, the page raises the notification itself: a \"notify me\" button appears in the top bar the first time a session blocks, and the browser asks you — nothing is requested before there is something to notify you about. With no page open at all, the server raises it, because it can see that nobody is connected and therefore that no other surface is saying anything.\n\nOne notification per session, replaced rather than stacked if a second deck raises it too. Clicking it brings the tab forward. `AGENTS_DECK_NO_NOTIFY=1` turns the desktop side off entirely, and if you refuse the browser prompt the deck says so plainly instead of leaving you thinking you switched something on — after a refusal only your browser's site settings can undo it, which no page is allowed to touch."
236
- },
237
- {
238
- "title": "🧩 A blocked session now says what it is asking for",
239
- "body": "\"Waiting on you\" told you to go and look. The session row and the card now carry the tool the prompt is most likely about, and the tooltip carries the one line of its input that a person actually reads — `rm -rf node_modules` rather than `{\"command\":\"rm -rf node_modules\"}`.\n\nIt is a guess and it is worded as one: the deck sees the tool calls, not the permission dialog, so it says \"likely on\". The deck still cannot allow, deny or answer anything for your agent, and it never will."
240
- },
241
- {
242
- "title": "🧮 Today's figure was counting some sessions twice",
243
- "body": "The panel reads your usage from ccusage once a minute and fills the gap in between from what the deck is watching. The filling-in was wrong: any session that reached the canvas after the reading had landed was added in full, and since the canvas is still loading when the first reading arrives, that was most of them.\n\nMeasured on a deck four seconds old: ccusage said $450 for today and the panel said $1,006. The table underneath it said $450 the whole time, which is the disagreement to trust.\n\nThe deck now adds only work it has actually watched happen. A session that starts while you are looking shows up at the next reading instead of instantly — up to a minute — which is how the panel behaved before the live figure existed at all. Everything already on screen keeps moving as it did."
244
- }
245
- ],
246
- "3.6.0": [
247
- {
248
- "title": "✂️ The two figures at the end of the top bar are gone",
249
- "body": "The bar used to end with a token count and a dollar figure. Both were sums over what is on the canvas at that moment, and the canvas drops finished sessions a couple of minutes after they end — so both numbers fell on their own, with nothing on screen to explain the fall. They carried the word \"board\" and a three-line tooltip for exactly that reason.\n\nThe usage panel answers the same question without the apology: it reads the logs on disk, it covers sessions this deck never watched, and it does not forget. Press U for the panel, or H for the full history.\n\nWhat is left in the bar is what is true right now — whether the stream is alive, and what your machine is doing."
250
- }
251
- ],
252
- "3.5.0": [
253
- {
254
- "title": "⚡ The figures move when the work happens",
255
- "body": "Reading your usage off the logs is not free — measured here, one reading is 7.8 seconds of CPU, because ccusage walks every transcript on the machine whatever period you asked for. Once a minute is affordable; once every ten seconds would be three quarters of a core, permanently, for numbers that move slowly.\n\nSo the reading stays on a minute and the deck fills the gap with what it already knows. Every hook event carries its session's running total, so the panel adds whatever has been spent since the last reading and counts up to it. The headline now moves when a turn finishes rather than on the clock, and it costs nothing extra.\n\nIt only ever adds. A session leaving the canvas cannot pull the figure down, and the next reading corrects whatever the estimate got wrong — the tokens are exact, and the dollars in between are priced by the deck's own table.\n\nTwo smaller things with it: pressing today, month or all now counts between the two figures instead of swapping them, and coming back to the tab only re-reads if what is on screen is actually stale."
256
- }
257
- ],
258
- "3.4.0": [
259
- {
260
- "title": "🔢 The figures count to their new value, once a minute",
261
- "body": "The Usage panel re-reads its numbers every minute now rather than every five, and it is a real minute — the server used to hold a reading for two, so a shorter poll would have handed the same figure back.\n\nAnd they no longer teleport. Replaced in a single frame, $170 → $269 says only \"this is different now\": no direction, no sense of how much, and nothing at all if your eye was a few pixels away. Counted over a fifth of a second, the same change says up, and roughly how far.\n\nWhat deliberately does not count: the first paint, a change of period — \"today\" and \"all time\" are different quantities rather than one that moved — the tables, a change too small to read, and anything at all if you have reduced motion on or the tab is in the background."
262
- },
263
- {
264
- "title": "🧹 The board's own figure is out of the Usage panel",
265
- "body": "3.3.0 put the canvas's live spend on its own labelled line, under whichever period you had chosen. In front of a panel that answers today, this month and all time from the logs on disk, that second money figure answers a question nobody asked at that moment — and it invited a comparison it could never win, $7,385 on the board beneath $170 for today.\n\nIt is gone. The board's tokens and cost are still on the topbar, with the sentence that explains why they fall on their own, and the panel still shows them when ccusage cannot answer at all."
266
- }
267
- ],
268
- "3.3.0": [
269
- {
270
- "title": "📊 The Usage panel answers for a period, not just for the board",
271
- "body": "It used to add up the sessions drawn on the canvas — which is an honest number with a scope nobody reads it as having: finished sessions are evicted a couple of minutes after they end, so the figure fell on its own while nothing had happened and nothing was refunded.\n\nIt now reads ccusage, the same source the H modal uses, and offers today, this month and all time. BY MODEL and BY SESSION come from there too, and each session is named by the project the board knows it as rather than by a uuid.\n\nThe live figure is still there, on its own line and labelled — what the sessions currently on the board have spent — because that is a different question from what the month cost.\n\nOne thing worth knowing about the source: ccusage reports a session's LIFETIME total, so the rows under \"active today\" can add up to more than the day above them. The heading says so."
272
- },
273
- {
274
- "title": "🪟 Windows: a deck started from an elevated terminal was being treated as dead",
275
- "body": "The hook decides whether a deck is still running by trying to signal it. On POSIX a process you may not signal answers EPERM; on Windows it answers EACCES, and only EPERM was accepted — so a deck started from an elevated terminal, or under another account, read as gone. Its registration was deleted on every event and rewritten five seconds later, while its banner went on saying it was connected and almost nothing arrived.\n\nThree more Windows-only faults went with it: a read-only settings.json made every hook install fail for good (the atomic write could not replace the file); the machine panel answered 500 every four seconds whenever the process read failed; and a bare command name was looked for in the working directory before PATH, which is where `npx ccdeck` was run."
276
- },
277
- {
278
- "title": "👁 Browser Watch reads nothing while it is switched off",
279
- "body": "The switch used to control only what was KEPT. The read happened anyway — every five minutes, on every deck, the panel's badge poll discovered each Chromium profile and copied its whole History database to a temp file to query it. A switch that says off while that continues is not a switch.\n\nWith the watch off, nothing touches a browser now. Opening the panel still reads live, because that is you looking, and a watch that is ON still records in the background — that is the feature. The trade is that the topbar badge no longer counts findings while the watch is off; open the panel to see them.\n\nThe README has a Browser Watch section now: what is read, when, where the copy goes, and that only visits after the deck started are ever considered."
280
- },
281
- {
282
- "title": "🔒 The deck's own data is no longer readable by anything on the machine",
283
- "body": "Changing an account needed the deck's credential; reading the event ring did not. Anything running on your machine could `curl` the events — prompt text, the commands an agent ran, the paths and contents it touched — plus the account list and the browsing episodes.\n\nThose reads now answer to the same rule the mutations do: the deck's own page, or its token. Nothing changes for you in the browser. A script of yours that reads those routes needs the token from `~/.claude/agent-dag/<pid>.json`, sent as `x-ccdeck-token`."
284
- },
285
- {
286
- "title": "💳 An API-key, Bedrock or Vertex install is told there is no quota window",
287
- "body": "Those installs are billed per token and have no five-hour window. The CLI ran, printed no quota lines, and the deck read that as a genuine \"under 1%\" — drawing two empty bars for a measurement nobody had taken, or telling you to run /usage, which cannot help there.\n\nIt now says what it is, in one line, the way the Codex half already did."
288
- },
289
- {
290
- "title": "🎨 The model chip is coloured by its model again",
291
- "body": "A session that switched model — Sonnet, then Opus — drew a chip reading \"Opus 5 +1\" in Sonnet blue, because the colour was picked from the tooltip and the tooltip lists every model the card's spend covers. Add a Fable turn and it went yellow.\n\nAlso fixed on the way past: an account import the CLI rejected could take the whole deck down with it, and a corrupt compressed Codex rollout leaked a file handle on every poll."
292
- }
293
- ],
294
- "3.2.1": [
295
- {
296
- "title": "💵 Sonnet 5 was being costed fifty per cent high",
297
- "body": "Sonnet 5 launched at $2/$10 per million tokens as introductory pricing, with a rise to $3/$15 announced for September 1. The deck implemented that schedule, and on September 1 it started charging the higher rate.\n\nThe increase was cancelled. Anthropic's pricing page now says the $2/$10 rate is simply the price and the rise \"will not occur\" — so every Sonnet 5 session since the 1st has been shown to you at half again what it cost.\n\nIt is corrected, and there is no date logic left in that rate to go stale a second time. Numbers already on your board are recomputed from the log the next time the deck starts; nothing was billed by us, only displayed.\n\nFable 5.1 and Mythos 5.1 are also priced now, including the cache read that is a quarter of Fable 5's — which is most of what a long session pays for."
298
- }
299
- ],
300
- "3.2.0": [
301
- {
302
- "title": "🌡️ Windows shows a temperature where it can find one",
303
- "body": "On most Windows laptops there is nothing to show, and that is Windows rather than this deck: modern machines declare no ACPI thermal zone, and the sensors that do exist sit behind an interface only an administrator may read. Measured on real hardware, elevated and not.\n\nOne thing does work, and it costs you nothing to have. If LibreHardwareMonitor happens to be running with its web server on, the deck now reads its numbers over localhost — which needs no privileges at all.\n\nIt is only ever a read. Nothing is installed, nothing is asked of you, and a machine without it shows no section, exactly as before."
304
- }
305
- ],
306
- "3.1.0": [
307
- {
308
- "title": "🚀 The first run stops looking like it hung",
309
- "body": "`npx ccdeck` on a machine that has never run it used to sit on a spinner at \"checking claude-swap\" and say nothing else. Measured on a Windows box: seventeen and a half seconds before it told you where to point a browser. It is now one and a half.\n\nThe deck was waiting for an install nobody asked for during that boot — a Python toolchain being fetched so the accounts panel would work. The port had been open the whole time; there was just nothing on screen to say so.\n\nIt no longer waits. Anything still working past the boot says so on its own line and keeps going in the background, and the deck comes up without it."
310
- },
311
- {
312
- "title": "⚡ Every start is quicker, and stays quicker",
313
- "body": "The event log was read from the beginning on every boot to fill a buffer that only keeps the last two thousand events — so four fifths of that reading existed to be thrown away, and it got slower the longer you had been using the deck. It reads from the end now and stops when the buffer is full: 258ms down to 20ms here, on a 34 MB log, and it no longer grows.\n\nThe accounts tool is also asked for its version once instead of twice, which on this machine was the single largest thing in an ordinary boot."
314
- },
315
- {
316
- "title": "📉 Ten fewer packages to install",
317
- "body": "Opening your browser used to cost a dependency that brought nine more with it — ten downloads on a cold `npx ccdeck` before the deck's own files were unpacked. It is now done with the commands the operating system already has.\n\nThe package has no runtime dependencies at all."
318
- },
319
- {
320
- "title": "🌡️ Temperature, on the machines that could not show it",
321
- "body": "On Windows the deck was asking somewhere it is not allowed to look: that reading lives behind a namespace that needs administrator, so it failed for everybody and the section never appeared. It reads a performance counter now, which needs nothing.\n\nOn Apple Silicon there is no command that ships with macOS which prints a temperature at all — the sensors are behind an interface only native code can reach. The deck fetches a small signed tool for it, verified against the checksum its release publishes, in the background and only on the Macs that need it. You do not have to install anything. `AGENTS_DECK_NO_DOWNLOAD=1` turns that off.\n\nA machine with no sensor still shows no section, which includes every virtual machine. Nothing is drawn that was not measured."
322
- },
323
- {
324
- "title": "😌 The terminal goes quiet when there is nothing happening",
325
- "body": "The dot at the bottom blinked every 800ms for as long as the deck ran, which is what a terminal does while it is working — so a deck that had finished in a second looked like one that never finished.\n\nIt now moves only while something actually is: a job still running, or hooks that cannot find the deck. Otherwise it is drawn once and left alone, and it says what the job is rather than just blinking. A step that takes more than three seconds shows how long it has been going, which is the whole of \"is this stuck\".\n\nAnd a machine with no Claude Code installed stops being told about it every few seconds — that one is said once now, on its own line instead of across the status line."
326
- }
327
- ],
328
- "3.0.0": [
329
- {
330
- "title": "📈 The machine panel now shows what it DID, not just what it is",
331
- "body": "Click any section of the machine panel — Cores, Memory, Load average, Thermal — and it opens a chart of what that reading has done since the deck started.\n\nThe panel could always tell you the machine is busy now. It could not tell you whether it got hot while that three-hour build was running, or how deep the queue went while you were away, and those are the questions you actually have afterwards.\n\nThermal is new as well. Where the machine will give degrees, it gives degrees; on a Mac it also reports whether the CPU is being held back to cool down, which is the consequence a temperature has to be interpreted into anyway. Nothing is drawn that was not measured — a machine with no sensor gets no section at all, rather than a zero."
332
- },
333
- {
334
- "title": "🔍 The whole process list, not just the busiest eight",
335
- "body": "\"more\" beside Busiest processes opens the full list the deck is watching — around fifty processes rather than eight — with the pid and the untruncated name.\n\nThis closes a gap. The panel's eight rows are the busiest, so sorting them by name only reorders those eight: \"is my test runner still going\" could not be answered if the test runner was not already on screen. Now it can.\n\nIt is deliberately not a task manager. Nothing here ends a process; Activity Monitor and Task Manager are better at that and it is not a thing this deck should own."
336
- },
337
- {
338
- "title": "🔤 Process names in Cyrillic, Greek or with accents read correctly",
339
- "body": "An application named Яндекс Музыка was showing up as M-PM-/M-PM-=M-PM-4M-PM-5… in the process list, and so was every other name the deck could not spell in plain ASCII.\n\nThe deck was forcing a fixed locale on the commands it runs, to guarantee that a decimal point stays a point on a machine set to German or Russian — and that also forced the character set. It forces only the number format now. Your own character set is left alone, which is what makes the name arrive intact."
340
- },
341
- {
342
- "title": "📦 The text you copy to share accounts is a third of its size",
343
- "body": "Sharing three accounts used to produce 6168 characters. It is now 1896.\n\nMost of that text was never the credentials — two OAuth tokens are about two hundred characters between them, and the rest was the envelope, whose field names repeated in full for every account added. So the saving grows with the number of accounts rather than shrinking.\n\nA blob you copied before this still imports until it expires. One made now cannot be read by a deck older than this version — that is the one thing in this release that older decks cannot follow."
344
- }
345
- ],
346
- "1.48.0": [
347
- {
348
- "title": "👁 Browser Watch — see what a program opened while you were away",
349
- "body": "There is a new eye in the top bar. It watches the browsers on this machine and tells you when a program — an extension, an agent, a command — opened pages while nobody was touching the browser.\n\nUsually that is your own agent doing what you asked, and the panel says so rather than crying intrusion. What it gives you is the evidence: which pages, in which browser, at what time, with every address written out in full.\n\nIt is off until you switch it on, and nothing leaves this machine. It reads a copy of the browser's own history and only pages newer than the moment the deck started — never anything from before, never cookies, never saved passwords. Open ⚙ inside the panel and \"What Browser Watch can access\" says exactly what it reads, keeps and sends.\n\nWhen it finds something the eye turns amber with a count. You can dismiss a finding once you have looked at it and it stays gone, while the log file on disk keeps the record. On a Mac it can also close the tab it found, or quit the browser."
350
- },
351
- {
352
- "title": "🔑 Move all your accounts to another machine in one paste",
353
- "body": "Sharing accounts between two decks used to be one account at a time, and step four of six is where people stop — leaving the second deck half set up with nothing saying so.\n\nPick the accounts you want, copy one blob, paste it into the other deck. The import adds what is missing, leaves what already works alone, heals an account whose token has died, and tells you which of those happened to each one.\n\nThe blob carries real credentials, so it expires ten minutes after you make it and the deck refuses an expired one before it unwraps anything."
354
- }
355
- ],
356
- "1.47.0": [
357
- {
358
- "title": "🔌 The top bar goes quiet when everything is fine",
359
- "body": "The green \"live\" pill used to sit up there permanently to tell you nothing was broken. It is gone while things are working.\n\nYou will still see a pill when it matters: amber when you pause the canvas, with how many events are waiting, and red when the deck loses its connection to the server. No pill means all is well."
360
- },
361
- {
362
- "title": "🔴 The browser tab tells you when the connection drops",
363
- "body": "The little icon in the tab has always shown what your sessions are doing — grey when it is quiet, blue when something is working, amber when a session is waiting on you.\n\nIt could not say the one thing that matters most: that the deck has stopped hearing from the server. Until now it just kept showing whatever it had last seen. It turns into a broken red ring instead, so you can spot it from another tab without switching over."
364
- }
365
- ],
366
- "1.46.0": [
367
- {
368
- "title": "🔊 Pick a volume and a sound for each of the two tones",
369
- "body": "Click the speaker in the top bar and a small menu opens. There are two tones — one when a turn finishes, one when Claude is waiting for you — and each has its own volume and its own sound, six to choose from. Press \"Hear it\" to try one straight away, without waiting for a real turn.\n\nThe two lists are separate on purpose, so you can never end up with both tones sounding the same. A finish tone always falls; an asking tone always rises. That is how you tell them apart without looking at the screen.\n\nM still turns the sound on and off from anywhere."
370
- },
371
- {
372
- "title": "✨ This box is new",
373
- "body": "After an update, the deck tells you what changed — once, and only when there is something worth telling. Most updates have nothing to say and you will see nothing at all.\n\nClick the version at the top of the deck to read it again whenever you like."
374
- }
375
- ],
376
- "1.45.0": [
377
- {
378
- "title": "🔕 The deck no longer puts anything in your settings.json",
379
- "body": "It used to add a hook there so Claude Code would play a sound when a turn ended. That entry is removed the first time you start this version, and any sound hook you wrote yourself is put back exactly where you had it. Nothing else in that file is touched. The deck makes the sound itself now — which is also why you finally hear one when a Codex turn ends, not just a Claude one."
380
- }
381
- ],
382
- "3.16.0": [
383
- {
384
- "title": "🤝 Pair two decks with one piece of text",
385
- "body": "**Accounts ▸ Local network ▸ setup… ▸ make an invite.** Send the text however you already talk to them; they paste it and the two decks are paired. Nobody presses accept, and neither of you has to know which of your addresses the other can reach — the invite carries them all and their deck tries each one.\n\nDecks on the same router still find each other on their own and appear under **on this network right now**. An invite is for the case that does not work: another subnet, a VPN, a firewall.\n\nThe invite lasts 10 minutes and is used once. Anyone who gets hold of the text can pair with your deck until it runs out, so send it the way you would send anything else you would not want forwarded."
386
- },
387
- {
388
- "title": "⚠️ Both machines have to be on this version",
389
- "body": "The two decks no longer speak the old protocol, and there is no fallback: a 3.15 deck and a 3.16 deck cannot see each other at all. Update both.\n\nThe group passphrase is gone. If you had one set, that pairing is not carried over — make an invite and pair again. It was replaced because a passphrase that differs by one character produced a closed socket and no other symptom on either machine, and a secret is the one thing a panel must never print, so neither person could check theirs against the other's."
390
- }
391
- ],
392
- "3.22.1": [
393
- {
394
- "title": "🖥️ A first run now opens This machine too",
395
- "body": "Accounts and usage already opened themselves the first time you ran the deck; the machine panel did not, and now does. It is the panel that answers \"is this box coping\" — cores, memory, load, temperature — and somebody meeting the deck for the first time cannot ask for a panel they do not know is there.\n\nIt only does this when you have never said otherwise. Close it once and it stays closed, on that browser, for good — the same rule the other two panels have always followed. Nothing reopens it behind you.\n\nWhile it is open it polls this machine once every few seconds, and it stops the moment you close it or switch tabs."
396
- }
397
- ]
398
- }
@@ -1,52 +0,0 @@
1
- // Whether any agent is mid-turn, from the hook stream alone.
2
- //
3
- // For the one decision the server takes on its own that must not land in the
4
- // middle of a turn: restarting into a new version while nobody is looking
5
- // (auto-update.mjs). A restart mid-turn drops the hook events fired during the
6
- // gap, and the canvas shows those tools in flight until the stale sweeper
7
- // reaps them — restart.ts states the rule for the page, which decides it from
8
- // its reducer's `active` state.
9
- //
10
- // With no page open there is no reducer, so this keeps the smallest version of
11
- // it: a session is open from the first event of a turn until the turn ends
12
- // (`Stop`, `SessionEnd`). SessionStart and Notification move nothing — a CLI
13
- // sitting at its prompt is not busy, and the idle reminder arrives after the
14
- // turn it is about. An open session whose last event is older than
15
- // OPEN_CAP_MS counts as gone rather than busy: a CLI that crashed never sends
16
- // its Stop, and waiting on one forever would mean never updating.
17
- export const OPEN_CAP_MS = 30 * 60_000;
18
-
19
- const OPENS = new Set([
20
- "UserPromptSubmit", "PreToolUse", "PostToolUse", "PostToolUseFailure", "SubagentStart", "SubagentStop",
21
- ]);
22
- const CLOSES = new Set(["Stop", "SessionEnd"]);
23
-
24
- export function createActivity({ openCapMs = OPEN_CAP_MS } = {}) {
25
- /** session id → when its turn last produced an event */
26
- const open = new Map();
27
- let lastAt = 0;
28
- return {
29
- /** One hook payload, as pushEvent receives it. Anything else is ignored. */
30
- note(raw, at) {
31
- const name = raw?.hook_event_name;
32
- if (!OPENS.has(name) && !CLOSES.has(name)) return;
33
- if (at > lastAt) lastAt = at;
34
- const id = raw?.session_id;
35
- if (typeof id !== "string" || !id) return;
36
- if (CLOSES.has(name)) open.delete(id);
37
- else open.set(id, at);
38
- },
39
- /** Whether any turn is still running. */
40
- busy(now) {
41
- for (const [id, at] of open) {
42
- if (Math.abs(now - at) > openCapMs) open.delete(id);
43
- else return true;
44
- }
45
- return false;
46
- },
47
- /** How long since the last turn event of any session — Infinity before the first. */
48
- quietMs(now) {
49
- return lastAt ? Math.max(0, now - lastAt) : Infinity;
50
- },
51
- };
52
- }