viewable-one 0.0.0-stage → 0.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1483 -0
- package/LICENSE +21 -0
- package/README.md +738 -2
- package/SPAWN.md +2108 -0
- package/glytch.mjs +517 -0
- package/lib/acp.mjs +1018 -0
- package/lib/admission.mjs +105 -0
- package/lib/agent-selection.mjs +124 -0
- package/lib/agents.mjs +914 -0
- package/lib/atomic.mjs +96 -0
- package/lib/auth.mjs +1491 -0
- package/lib/authenticatedHttp.mjs +75 -0
- package/lib/boardside.mjs +225 -0
- package/lib/child.mjs +1292 -0
- package/lib/cli-output.mjs +176 -0
- package/lib/cloudflared-release.json +25 -0
- package/lib/cloudflared.mjs +81 -0
- package/lib/codex-rpc.mjs +92 -0
- package/lib/codex.mjs +767 -0
- package/lib/codexApps.mjs +344 -0
- package/lib/codexPlanWindow.mjs +84 -0
- package/lib/codexToolInputs.mjs +139 -0
- package/lib/codingChecks.mjs +55 -0
- package/lib/compose.mjs +76 -0
- package/lib/computerApps.mjs +110 -0
- package/lib/conversationRules.mjs +45 -0
- package/lib/detect.mjs +629 -0
- package/lib/devserver.mjs +670 -0
- package/lib/digest.mjs +290 -0
- package/lib/doctor.mjs +464 -0
- package/lib/exitClass.mjs +64 -0
- package/lib/hosted.mjs +112 -0
- package/lib/imageAttachments.mjs +31 -0
- package/lib/init-connect.mjs +418 -0
- package/lib/kill.mjs +104 -0
- package/lib/logindaemon.mjs +976 -0
- package/lib/logs.mjs +120 -0
- package/lib/mcpjson.mjs +242 -0
- package/lib/memberPreview.mjs +173 -0
- package/lib/multiwatch.mjs +428 -0
- package/lib/pagePictures.mjs +396 -0
- package/lib/pageRoutes.mjs +70 -0
- package/lib/paths.mjs +10 -0
- package/lib/permissionAsk.mjs +418 -0
- package/lib/picker.mjs +300 -0
- package/lib/platform.mjs +60 -0
- package/lib/previewGateway.mjs +130 -0
- package/lib/previewTunnel.mjs +85 -0
- package/lib/procs.mjs +428 -0
- package/lib/project-mcp.mjs +288 -0
- package/lib/projectPaths.mjs +31 -0
- package/lib/refusal.mjs +70 -0
- package/lib/registry.mjs +204 -0
- package/lib/repomap.mjs +323 -0
- package/lib/restartPass.mjs +151 -0
- package/lib/retired-address.mjs +78 -0
- package/lib/rowsAnswers.mjs +388 -0
- package/lib/rowsEvents.mjs +86 -0
- package/lib/run-engine.mjs +16 -0
- package/lib/runner-health.mjs +57 -0
- package/lib/runner.mjs +1640 -0
- package/lib/runnerAdmission.mjs +35 -0
- package/lib/runnerApi.mjs +312 -0
- package/lib/runnerCompletion.mjs +406 -0
- package/lib/runnerLease.mjs +37 -0
- package/lib/runnerMaintenance.mjs +27 -0
- package/lib/runnerPace.mjs +283 -0
- package/lib/runnerPerformance.mjs +26 -0
- package/lib/runnerRealtime.mjs +340 -0
- package/lib/runnerRegister.mjs +96 -0
- package/lib/runnerSettings.mjs +40 -0
- package/lib/runnerSettlement.mjs +145 -0
- package/lib/runnerWorkspace.mjs +177 -0
- package/lib/serverSweep.mjs +90 -0
- package/lib/sharedPreview.mjs +292 -0
- package/lib/signIn.mjs +47 -0
- package/lib/spawn.mjs +330 -0
- package/lib/sweep.mjs +161 -0
- package/lib/sync.mjs +484 -0
- package/lib/uninstall-project.mjs +589 -0
- package/lib/uninstall.mjs +464 -0
- package/lib/update.mjs +302 -0
- package/lib/usageLimit.mjs +108 -0
- package/lib/viewableFolder.mjs +105 -0
- package/lib/windows-command.mjs +85 -0
- package/lib/worktree.mjs +1142 -0
- package/lib/writePolicy.mjs +45 -0
- package/package.json +58 -4
- package/spawn-runner.mjs +167 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,1483 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
`viewable-one`. Dates are the publish date, newest first.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and the versions follow [semver](https://semver.org/spec/v2.0.0.html). For
|
|
7
|
+
this package the public surface is **the commands, their flags, and what the
|
|
8
|
+
runner does with a run**, not the internals: renaming a command, dropping a
|
|
9
|
+
flag, or changing what the runner promises a session is a breaking change;
|
|
10
|
+
rewriting how one of them does its job is not. The board tools themselves are
|
|
11
|
+
served from the hosted endpoint and ship on their own schedule, not with this
|
|
12
|
+
package.
|
|
13
|
+
|
|
14
|
+
## [Unreleased]
|
|
15
|
+
|
|
16
|
+
## [0.12.2] - 2026-10-30
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Use Viewable for internal and outbound names, retaining old project settings, preview access, credentials and working folders (#2329).
|
|
21
|
+
- A late answer to a finished run's question continues that run: Claude Code
|
|
22
|
+
and Codex resume the same session in the same separate copy instead of
|
|
23
|
+
starting the idea again. When the session or its copy is gone, the run
|
|
24
|
+
fails with "Run again to start a new session" instead of silently starting
|
|
25
|
+
fresh (#2446, #2447).
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- Uninstall confirms that a personal board key no longer works before saying
|
|
30
|
+
"revoked". Refused or unconfirmed revocation keeps the key file for retry
|
|
31
|
+
(#2945). Requires the matching hosted revocation update.
|
|
32
|
+
- Preview finds the newest server record whether it was written as
|
|
33
|
+
`.viewable-server.json` or `.glytch-server.json`, so a resumed session's
|
|
34
|
+
newer record wins (#2329).
|
|
35
|
+
## [0.12.1] - 2026-10-25
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- Install Viewable with `npm install -g viewable-one` and use `viewable`. The old `glytch` command, settings and sign-in keep working.
|
|
40
|
+
- Interrupted Claude Code and Codex runs carry on in the same conversation and separate copy when the computer returns.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- `glytch update` finds Homebrew's Volta even when Homebrew is absent from
|
|
45
|
+
PATH, and accepts a symlinked Volta bin directory (#727).
|
|
46
|
+
|
|
47
|
+
## [0.12.0] - 2026-10-17
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- The runner now hears about new work the moment it happens. A run you
|
|
52
|
+
approve starts within a few seconds, and Stop reaches a running session
|
|
53
|
+
within a few seconds, without the runner asking the board every 10 seconds.
|
|
54
|
+
While it is connected it checks in every 5 minutes; if the connection drops
|
|
55
|
+
it goes back to checking every 10 seconds until it is back.
|
|
56
|
+
- A runner watching several folders now shows as one runner, with one id for
|
|
57
|
+
all of its folders.
|
|
58
|
+
- The runner talks to the board's server directly instead of through the
|
|
59
|
+
app's website.
|
|
60
|
+
- `glytch doctor` counts the runner as beating for 6 minutes after its last
|
|
61
|
+
check-in, to match the 5-minute check-in.
|
|
62
|
+
## [0.11.4] - 2026-10-16
|
|
63
|
+
|
|
64
|
+
### Fixed
|
|
65
|
+
|
|
66
|
+
- `glytch update` follows Volta's shared launcher with `volta which glytch`
|
|
67
|
+
in a fresh shell and restarts its package image even when `npm root -g`
|
|
68
|
+
points at the separate Node image. Another install is still refused (#707).
|
|
69
|
+
## [0.11.3] - 2026-10-16
|
|
70
|
+
|
|
71
|
+
### Changed
|
|
72
|
+
|
|
73
|
+
- The board server given to Codex now allows a tool call up to 120 seconds,
|
|
74
|
+
so a question can wait for the person's answer instead of being cut off
|
|
75
|
+
after a minute. A board entry already in your Codex config keeps its own
|
|
76
|
+
settings.
|
|
77
|
+
|
|
78
|
+
### Fixed
|
|
79
|
+
|
|
80
|
+
- Codex computer-app approvals now ask on the board and return the selected
|
|
81
|
+
answer instead of rejecting supported requests without a question. Stopped,
|
|
82
|
+
timed-out and resolved requests cannot receive a later approval. Unsupported
|
|
83
|
+
forms and sensitive input remain blocked.
|
|
84
|
+
- The runner calls the board far less, so a machine watching several folders
|
|
85
|
+
no longer trips the board's rate limit. One heartbeat covers every folder,
|
|
86
|
+
an idle folder asks for work only when the board says a run is waiting,
|
|
87
|
+
and folder registration repeats every 5 minutes instead of every beat. With
|
|
88
|
+
five idle folders that is about 6 calls a minute instead of about 45. When
|
|
89
|
+
the board is busy and says how long to wait, the runner waits that long,
|
|
90
|
+
and the log names the request so it can be looked up. Older boards keep
|
|
91
|
+
working: there each folder polls on its own, slowing to every 30 seconds
|
|
92
|
+
when nothing is waiting.
|
|
93
|
+
- Codex tool approvals in every form mode Codex sends are now asked, and a
|
|
94
|
+
tool whose name mentions keys or tokens is asked too; a message or field
|
|
95
|
+
that carries a password, token or code is still refused. Every refused request
|
|
96
|
+
writes its reason and field names (never values) to the runner log. Each
|
|
97
|
+
question offers one Cancel.
|
|
98
|
+
- The runner no longer exits by itself when it is idle or when no folders are
|
|
99
|
+
registered.
|
|
100
|
+
## [0.11.2] - 2026-10-15
|
|
101
|
+
|
|
102
|
+
### Fixed
|
|
103
|
+
|
|
104
|
+
- Codex runs preserve configured, plugin and built-in app tools with scoped
|
|
105
|
+
approval policies. Tool exposure is checked before a run starts; apps OFF
|
|
106
|
+
and plan drafts keep their restrictions. MCP discovery cleanup is bounded.
|
|
107
|
+
## [0.11.1] - 2026-10-15
|
|
108
|
+
|
|
109
|
+
### Added
|
|
110
|
+
|
|
111
|
+
- A coding run asks in the idea when it needs something outside its list
|
|
112
|
+
(#478): Allow, Not this, or Always allow in <folder>. Always allow is
|
|
113
|
+
remembered per folder under `~/.glytch/permissions/` and not asked again
|
|
114
|
+
there. Claude Code and Codex both ask; a plan draft never does.
|
|
115
|
+
- `webSearch` on a request lets the run search and read the web without
|
|
116
|
+
asking first (#478).
|
|
117
|
+
### Changed
|
|
118
|
+
|
|
119
|
+
- Keep changes (was the merge op) is one gesture (#483): it commits what
|
|
120
|
+
the agent left in the copy, sets the person's own uncommitted changes in
|
|
121
|
+
the folder aside and puts them back, merges into the default branch, and
|
|
122
|
+
deletes the copy and its branch. Nothing is pushed. A clash changes nothing
|
|
123
|
+
in the folder and names the files, so the app can send Coding back to fit
|
|
124
|
+
its changes around the person's. Throw away (was delete-branch) deletes
|
|
125
|
+
the copy and its branch. A run in a folder that is not git says Throw away
|
|
126
|
+
cannot undo it.
|
|
127
|
+
## [0.11.0] - 2026-10-14
|
|
128
|
+
|
|
129
|
+
### Removed
|
|
130
|
+
|
|
131
|
+
- Every run and agent limit (#364): no 2 dollar wall on a run, no 400,000
|
|
132
|
+
token context wall, no 4,000,000 token running total, and no
|
|
133
|
+
`--max-budget-usd` passed to Claude Code. A run is bounded by the wall
|
|
134
|
+
clock alone. `PM_SPAWN_MAX_PER_DAY` is gone; a value still set is ignored.
|
|
135
|
+
`PM_SPAWN_MAX_CONCURRENT` and the wall clock stay. Cost and tokens are
|
|
136
|
+
still recorded with the run.
|
|
137
|
+
- A Codex run no longer stands down when its plan window passes a
|
|
138
|
+
percentage (80 by default). It always starts. The window is still read for
|
|
139
|
+
the run's info line. The runner's status no longer carries the last window
|
|
140
|
+
it read.
|
|
141
|
+
|
|
142
|
+
### Changed
|
|
143
|
+
|
|
144
|
+
- No receipt or closing reply shows a cost any more: no dollar figure, no
|
|
145
|
+
estimate, no "cost unavailable" or "on your Codex plan". They show turns and
|
|
146
|
+
time. The cost is still recorded with the run.
|
|
147
|
+
- The board digest's flags section is read under its new name, `BOARD FLAGS`,
|
|
148
|
+
and the old `SUBCONSCIOUS FLAGS` still works.
|
|
149
|
+
- `GOAL` is no longer read as a fact on a card's line in the digest; the board
|
|
150
|
+
stopped writing it.
|
|
151
|
+
- `glytch init` exits 1, and does not say a runner is watching, when your
|
|
152
|
+
computer could not sign in or skipped sign-in for want of a terminal.
|
|
153
|
+
`--no-login` still exits 0, but no longer says a runner is watching.
|
|
154
|
+
|
|
155
|
+
### Security
|
|
156
|
+
|
|
157
|
+
- Relaunching a dev server runs the recorded command as one program and its
|
|
158
|
+
arguments, with no shell and only a plain environment (no model keys, cloud
|
|
159
|
+
credentials or runner settings). A recorded command with pipes, `&&`,
|
|
160
|
+
quotes or `$` is refused, and so is a recorded directory outside the linked
|
|
161
|
+
folder.
|
|
162
|
+
- A run whose request names a directory outside the linked folder is refused
|
|
163
|
+
before anything starts, the same way a chat turn already was.
|
|
164
|
+
- A message from another session can no longer be read as an answered
|
|
165
|
+
question, whatever its sender label says.
|
|
166
|
+
## [0.10.0] - 2026-10-04
|
|
167
|
+
|
|
168
|
+
### Changed
|
|
169
|
+
|
|
170
|
+
- The board has one kind of card now, the **idea**, and the runner speaks of
|
|
171
|
+
ideas everywhere. A run belongs to an idea, its outcome is written as a note
|
|
172
|
+
on that idea, and a session that asks for a decision with `ask_user` lands a
|
|
173
|
+
question on the idea it was started from, which you answer in the idea's
|
|
174
|
+
conversation. Nothing about how you start, stop or approve a run changed.
|
|
175
|
+
- The text a coding session is told before it starts says "idea" where it spoke of
|
|
176
|
+
circles, and `get_board` reads the new board
|
|
177
|
+
digest: one `[idea]` line per card and one row per question, naming the idea
|
|
178
|
+
it sits on.
|
|
179
|
+
- The board's write requests are `addIdea` (title, content, pictures) and
|
|
180
|
+
`addQuestion` (an idea, the question, why, options). A session that still
|
|
181
|
+
sends `addThought` or `addPrompt` gets one plain line telling it to use
|
|
182
|
+
`addIdea`; those two names go in the release after this one.
|
|
183
|
+
|
|
184
|
+
### Added
|
|
185
|
+
|
|
186
|
+
- A note you write to the Coding agent can start a session, and the session
|
|
187
|
+
answers on the same idea: when it finishes it leaves one reply with its
|
|
188
|
+
closing words, a line of figures and links to the branch, the files, a
|
|
189
|
+
preview and a report. Pictures in your notes reach a running session, and a
|
|
190
|
+
session can attach pictures to its replies and questions. A note written
|
|
191
|
+
while the session works reaches it, and counts as delivered only once the
|
|
192
|
+
session has taken it; one it never took reads "did not reach the session"
|
|
193
|
+
with Try again. After your computer restarts, a session that was working
|
|
194
|
+
carries on by itself once, in its own worktree; one that cannot says so in
|
|
195
|
+
a reply and keeps its branch. A follow-up note to a Codex or opencode
|
|
196
|
+
session runs in the branch the earlier session kept.
|
|
197
|
+
|
|
198
|
+
### Fixed
|
|
199
|
+
|
|
200
|
+
- Messages queued for a running session were never collected: the board read
|
|
201
|
+
the queue before it knew the session's row. Steering from the idea or the
|
|
202
|
+
session screen works now.
|
|
203
|
+
|
|
204
|
+
## [0.9.1] - 2026-10-03
|
|
205
|
+
|
|
206
|
+
### Changed
|
|
207
|
+
|
|
208
|
+
- The runner calls the board less. While nothing is queued it checks for new
|
|
209
|
+
runs less often, slowing from every 5 seconds to every 20, and goes back to
|
|
210
|
+
every 5 as soon as it picks one up. When the board is down or unreachable it
|
|
211
|
+
waits longer between tries, up to a minute, and goes back to normal on the
|
|
212
|
+
first answer. With several folders it registers them once per beat instead
|
|
213
|
+
of once per folder.
|
|
214
|
+
## [0.9.0] - 2026-10-03
|
|
215
|
+
|
|
216
|
+
### Changed
|
|
217
|
+
|
|
218
|
+
- The board moved to `https://app.glytch.ai`; `prompt.glytch.ai` no longer
|
|
219
|
+
answers. Your saved sign-in carries over without signing in again, and
|
|
220
|
+
`glytch init` moves the `glytch` entries it wrote for your agents to the
|
|
221
|
+
new address. An agent you connected by hand (claude.ai, for example)
|
|
222
|
+
needs reconnecting at `https://app.glytch.ai/mcp`.
|
|
223
|
+
|
|
224
|
+
### Added
|
|
225
|
+
|
|
226
|
+
- A `Glytch` folder in your home folder. The runner makes it (a git
|
|
227
|
+
repository with one commit) and adds it to your folders when it starts
|
|
228
|
+
watching, so a board has somewhere to work before you link a folder. A chat
|
|
229
|
+
on a board with no folder picked works in its own subfolder there, named
|
|
230
|
+
after the board. `GLYTCH_FOLDER=off` turns it off.
|
|
231
|
+
- The runner now times its own steps for each run (time spent queued,
|
|
232
|
+
setting up, launching, reaching first output, running, and settling) and
|
|
233
|
+
records them with the run and in its own log, so a slow run can be
|
|
234
|
+
diagnosed from data instead of a guess. It never looks at prompt text,
|
|
235
|
+
file paths, or identity, only timestamps.
|
|
236
|
+
- A run's final result is now kept as a local receipt and retried until the
|
|
237
|
+
board acknowledges it, instead of being sent once and dropped if that one
|
|
238
|
+
attempt failed. A network blip right as a run finishes no longer loses
|
|
239
|
+
the run's outcome; it is delivered as soon as the connection comes back.
|
|
240
|
+
|
|
241
|
+
### Changed
|
|
242
|
+
|
|
243
|
+
- `glytch init` no longer ends by telling you to link the project folder in
|
|
244
|
+
the app. A coding run needs only the project `init` registers; linking the
|
|
245
|
+
folder only shows its files on the board, and `init` now says so. The
|
|
246
|
+
README's quick start says the same, and its engines table has a Codex row.
|
|
247
|
+
- CLI and API messages that said "Prompting Machine" now say "Glytch" (the
|
|
248
|
+
sign-in prompt, the not-signed-in hint, and the package description).
|
|
249
|
+
|
|
250
|
+
### Fixed
|
|
251
|
+
|
|
252
|
+
- `bin` paths written without `./`: npm 11.8 warned on the 0.8.2 publish
|
|
253
|
+
that it had "removed" both bins, which read as a broken package until the
|
|
254
|
+
registry record showed it had only stripped the prefix. The plain form is
|
|
255
|
+
what npm stores anyway, and it publishes without the warning.
|
|
256
|
+
- README said Codex sessions could not be launched from the board yet. The
|
|
257
|
+
runner has started Codex sessions since the app-server engine landed; the
|
|
258
|
+
table and intro sentence now say so.
|
|
259
|
+
- A Codex session launched from the board could not read the board, ask a
|
|
260
|
+
question, or file a report, and finished with nothing drawn on the
|
|
261
|
+
canvas: codex-cli requires each board tool call to be approved, and
|
|
262
|
+
nothing was approving them. Codex sessions now set the one approval mode
|
|
263
|
+
that lets those calls through, and turn on Codex's own plan tool so its
|
|
264
|
+
planning steps draw as step discs on the canvas.
|
|
265
|
+
- A Codex session's first line in the transcript was the whole spawn
|
|
266
|
+
preamble and repo primer above what the person actually asked. It now
|
|
267
|
+
echoes just what the person typed.
|
|
268
|
+
- A persistent session running in place, in a folder that is not its own
|
|
269
|
+
Git worktree (for example, a folder that is not a Git repo at all), could
|
|
270
|
+
still show the merge and delete-branch buttons on its run card. That
|
|
271
|
+
option is now recorded only for an isolated worktree or a chat turn.
|
|
272
|
+
- A run that lost its claim on the board, for example because another
|
|
273
|
+
runner picked it up after a restart, kept executing locally instead of
|
|
274
|
+
stopping. The runner now renews its claim every 30 seconds and stops
|
|
275
|
+
local execution as soon as it learns the claim is gone.
|
|
276
|
+
- A run's final metadata (including the worktree path a run card uses to
|
|
277
|
+
decide whether to offer Merge or Delete) could be overwritten by an
|
|
278
|
+
earlier, stale snapshot taken before cleanup ran. Completion now always
|
|
279
|
+
writes the final metadata, so a cleaned-up path is not shown as still
|
|
280
|
+
live.
|
|
281
|
+
- Under sustained load, a lock file's owner record could be read in the
|
|
282
|
+
brief gap between another process creating the lock and writing that
|
|
283
|
+
record, which made a lock just taken look old enough to clear as stale
|
|
284
|
+
out from under its still-running holder. The lock's own age is now used
|
|
285
|
+
as a fallback when its owner record cannot be read yet, so a lock just
|
|
286
|
+
taken is never wrongly cleared.
|
|
287
|
+
- The coding-agent preamble's blocked-ask rule was written in capitals,
|
|
288
|
+
which current models tended to over-apply. It is now written at normal
|
|
289
|
+
case with the same instruction and the same meaning.
|
|
290
|
+
|
|
291
|
+
### Security
|
|
292
|
+
|
|
293
|
+
- Windows no longer runs an npm `.cmd`/`.bat` shim through `cmd.exe`. The
|
|
294
|
+
runner resolves it directly to the Node or native program it points to,
|
|
295
|
+
and refuses to run anything it cannot parse safely, closing a path where
|
|
296
|
+
data in a request or session could have reached a shell.
|
|
297
|
+
- Worktree cleanup, merge, and delete now confirm with Git that a directory
|
|
298
|
+
is actually a worktree of this project before acting on it, and refuse a
|
|
299
|
+
path that reaches outside the project through a symbolic link. None of
|
|
300
|
+
them fall back to a raw recursive delete when Git cannot confirm this, so
|
|
301
|
+
a worktree operation that cannot be verified is kept on disk rather than
|
|
302
|
+
removed blind.
|
|
303
|
+
- Sign-in now asks for the runner's own access scope by name and names the
|
|
304
|
+
runner's own endpoint as the token's resource, instead of a general board
|
|
305
|
+
grant against the board's URL. A computer already signed in keeps
|
|
306
|
+
working; the next sign-in picks up the narrower grant.
|
|
307
|
+
|
|
308
|
+
## [0.8.2] - 2026-09-21
|
|
309
|
+
|
|
310
|
+
### Fixed
|
|
311
|
+
|
|
312
|
+
- A run's worktree based on whatever the main checkout happened to have
|
|
313
|
+
checked out, not the repo's default branch: `makeWorktreeImpl` and the
|
|
314
|
+
chat-worktree path both branched off `git rev-parse HEAD`, so a run cut
|
|
315
|
+
while the checkout sat on a side branch inherited that branch's commits,
|
|
316
|
+
and the merge button (which checked out literal `main`) could have dragged
|
|
317
|
+
the whole side branch into main. Both paths now base on a new
|
|
318
|
+
`defaultBranchOf(root)` helper - local `main`, else local `master`, else a
|
|
319
|
+
HEAD fallback that says so - and the merge op uses the same helper to
|
|
320
|
+
decide what "main" means, so a `master`-only repo merges into `master`
|
|
321
|
+
rather than refusing or checking out a branch that repo does not have. The
|
|
322
|
+
completion text now reads "off main at <sha>" (or "off HEAD at
|
|
323
|
+
<sha>, no main or master branch" on the fallback) instead of a bare
|
|
324
|
+
sha, so which branch a run started from is visible without a `git log`.
|
|
325
|
+
## [0.8.1] - 2026-09-21
|
|
326
|
+
|
|
327
|
+
### Fixed
|
|
328
|
+
|
|
329
|
+
- Proven live on spawn-qa3-fable-02 (Claude Code 2.1.270), 2026-09-21: with
|
|
330
|
+
the per-message counting above fixed, a trivial, healthy 14-turn run was
|
|
331
|
+
still stopped - "404k tokens against a 400k tokens cap" - because every
|
|
332
|
+
turn of a Claude Code session re-reads its ~55k system context from cache,
|
|
333
|
+
so the running total `computeTurnTokens` sums climbs roughly 60k a turn:
|
|
334
|
+
`DEFAULT_RUN_TOKEN_CAP` of 400,000 was only about seven turns. It had been
|
|
335
|
+
set to the same figure as `RUN_TOKEN_CEILING`, but that reads ONE turn's
|
|
336
|
+
context fill while this is a RUNNING TOTAL across every turn - the same
|
|
337
|
+
number was wrong by an order of magnitude for the second meaning. The
|
|
338
|
+
default is now 4,000,000 (about sixty turns) everywhere it is set:
|
|
339
|
+
`src/components/run/runLimits.ts` (and its generated copy
|
|
340
|
+
`bridge/lib/runLimits.mjs`), `src/agent/agentRow.ts`'s `DEFAULT_CAPS` (and
|
|
341
|
+
`supabase/functions/board-agent/agentRow.mjs`), and the "Most tokens in a
|
|
342
|
+
run" field's fallback in `CapsSection.tsx`, whose stepper now moves by
|
|
343
|
+
500,000 instead of 50,000 so the new default is reachable in a reasonable
|
|
344
|
+
number of clicks. The $2 `DEFAULT_RUN_COST_CAP` is still the wall that
|
|
345
|
+
actually bounds money; this one only bounds runaway token volume.
|
|
346
|
+
|
|
347
|
+
- Proven live on spawn-qa3-fable-01, 2026-09-21: Claude Code's stream-json
|
|
348
|
+
emits one `assistant` event PER CONTENT BLOCK of a turn (thinking, text,
|
|
349
|
+
each tool_use), and every one carries the SAME `message.usage` - same
|
|
350
|
+
`message.id`. `bridge/lib/child.mjs` was summing it on every event, so
|
|
351
|
+
three real turns of ~52-58k tokens each were counted eight times over
|
|
352
|
+
(444,842), crossed `DEFAULT_RUN_TOKEN_CAP` (400,000) in 27 seconds, and the
|
|
353
|
+
run was killed with exit 143. `child.mjs` now counts a message's usage
|
|
354
|
+
ONCE per `message.id` - a repeat of the same id adds only the growth since
|
|
355
|
+
the last chunk - and exposes the running total as `outcome.totalTokens`.
|
|
356
|
+
`bridge/lib/runnerCompletion.mjs`'s completion text now names the wall a
|
|
357
|
+
run was stopped at (cost, tokens or the fixed context ceiling), the figure
|
|
358
|
+
it hit against its cap, and whether the child was told to wrap up or had
|
|
359
|
+
no channel left and was stopped - where it used to say only "The session
|
|
360
|
+
exited 143." with no reason. `bridge/lib/runner.mjs` passes the run's
|
|
361
|
+
`limits` through so the cap figure can be named.
|
|
362
|
+
## [0.8.0] - 2026-09-21
|
|
363
|
+
|
|
364
|
+
### Fixed
|
|
365
|
+
|
|
366
|
+
- 2026-09-21: the runner's completion flush wrote a run's ENTIRE streamed
|
|
367
|
+
log a second time into `run_events` - proven live on spawn-mub65omw-52eb3e,
|
|
368
|
+
where a four-step plan's TaskCreate/TaskUpdate calls were each counted
|
|
369
|
+
twice and the expanded run drew "eight of eleven done" instead of "four of
|
|
370
|
+
four". `rowsEvents.mjs`'s `settle` now tracks how much of the live stream
|
|
371
|
+
it has already sent and writes only the tail `outcome.log` had not already
|
|
372
|
+
delivered - nothing, on an ordinary run, since the live stream already
|
|
373
|
+
carries it all; the whole log, on a run whose stream never started.
|
|
374
|
+
`bridge/lib/rowsEvents.mjs`. `src/folder/sessionLog/plan.ts`'s `derivePlan`
|
|
375
|
+
is now defensive against an older run whose log is already doubled on
|
|
376
|
+
disk: a second `TaskCreate` reporting a task id already seen is read as
|
|
377
|
+
the same task rather than an extra step. Also rewrote three strings in
|
|
378
|
+
`bridge/lib/runnerCompletion.mjs` that still said "him"/"his" in words a
|
|
379
|
+
person reads, to "them"/"theirs".
|
|
380
|
+
|
|
381
|
+
- QA pass, 2026-09-21: the binary-not-found refusal told a person to
|
|
382
|
+
"restart the runner (`glytch restart`)", a verb `glytch` has never had.
|
|
383
|
+
It now gives the real recipe (`glytch stop`, then `glytch init --yes` in
|
|
384
|
+
the project folder) and says why installing the missing agent alone does
|
|
385
|
+
not fix it - the lookup is cached for the life of the runner process, so
|
|
386
|
+
nothing changes until it restarts. `bridge/lib/runner.mjs`.
|
|
387
|
+
|
|
388
|
+
- QA pass, 2026-09-21: an error the board-runner function sent back as an
|
|
389
|
+
OBJECT (the shape a Supabase gateway error carries) printed as
|
|
390
|
+
`[object Object]` in `runner.log` - seen all night as `board-runner
|
|
391
|
+
answered 502: [object Object]`. The detail is now read from `.message`
|
|
392
|
+
when that is a string, or stringified when it is not, and the same
|
|
393
|
+
applies to `payload.message` when there is no `payload.error` at all.
|
|
394
|
+
`bridge/lib/runnerApi.mjs`.
|
|
395
|
+
|
|
396
|
+
- QA pass, 2026-09-21: every runner.log line under the launchd/systemd
|
|
397
|
+
daemon was written TWICE - once by this package's own `appendFile`, once
|
|
398
|
+
because the plist already redirects the daemon's stdout into that same
|
|
399
|
+
file. The logger now echoes to stdout only when stdout is a real
|
|
400
|
+
terminal, so a person running `glytch runner watch` by hand still sees
|
|
401
|
+
it while the daemon writes each line once; `GLYTCH_LOG_ECHO=1` forces the
|
|
402
|
+
echo back on either way. `bridge/lib/logs.mjs`.
|
|
403
|
+
|
|
404
|
+
- QA pass, 2026-09-21: on a Mac where `opencode` lives only in its own
|
|
405
|
+
installer's `~/.opencode/bin` and an nvm bin directory, a daemon running
|
|
406
|
+
under bash - or with no `$SHELL` set at all, so the login-shell lookup
|
|
407
|
+
never even tries - could not find it. `~/.opencode/bin` is now one of the
|
|
408
|
+
known install directories the runner checks before giving up.
|
|
409
|
+
`bridge/lib/detect.mjs`.
|
|
410
|
+
- 2026-09-21: Claude Code 2.1.270 ships no `TodoWrite` tool at all - verified
|
|
411
|
+
live with `claude -p --allowedTools "... TodoWrite Task"`, which lists
|
|
412
|
+
`TaskCreate`/`TaskUpdate`/`TaskList`/`TaskGet` and no `TodoWrite` - so every
|
|
413
|
+
board-spawned session on that build reported "TodoWrite was not available
|
|
414
|
+
in this session" and drew no plan discs. `CLAUDE_TOOLS` now allowlists the
|
|
415
|
+
four Task tools alongside `TodoWrite` (kept for an older CLI that still has
|
|
416
|
+
it), the `claude` and `acp-claude` preambles tell a session to plan with
|
|
417
|
+
`TaskCreate`/`TaskUpdate` and name `TodoWrite` only as the fallback, and
|
|
418
|
+
`src/folder/sessionLog/plan.ts` derives the same plan from a `TaskCreate`
|
|
419
|
+
(one step per call, in tree order) plus `TaskUpdate` (status by task id,
|
|
420
|
+
read off the create call's own tool_result) as it already did from
|
|
421
|
+
`TodoWrite`. `runWords.ts`'s verb table no longer mislabels either call as
|
|
422
|
+
"Took another step". codex's `update_plan` and opencode's `todowrite` are
|
|
423
|
+
unrelated and untouched.
|
|
424
|
+
|
|
425
|
+
- 2026-09-21, the runner feedback batch: a runner whose daemon PATH lacked the
|
|
426
|
+
agent binary died with `spawn claude ENOENT` AFTER minting a worktree, the
|
|
427
|
+
empty worktree then held the prompt's lock, and the Delete clicked on the
|
|
428
|
+
card was never served because the server only handed out ops for runs
|
|
429
|
+
still marked running. The runner now resolves the agent binary before it
|
|
430
|
+
touches git (known install dirs, then the login shell with `-lc` and, when
|
|
431
|
+
that finds nothing because zsh skips `.zshrc` there, `-ilc`; the LAST line
|
|
432
|
+
of stdout that is an absolute path that exists, so a banner in an rc file
|
|
433
|
+
does not win), refuses in plain words with the technical detail in a
|
|
434
|
+
separate paragraph when it finds none, removes the worktree of a run that
|
|
435
|
+
never started, keeps the lock only while a failed run's tree has commits,
|
|
436
|
+
files or uncommitted work, and checks and retries an op result that did
|
|
437
|
+
not land. The server side ships separately: ops are served for a run in
|
|
438
|
+
any status and a claim older than five minutes with no result is served
|
|
439
|
+
again.
|
|
440
|
+
|
|
441
|
+
- 2026-09-19: `login()`'s own merge (the B7 fix above) moved a merged
|
|
442
|
+
record's `refreshToken` forward but left the still-valid OAuth ACCESS
|
|
443
|
+
token with nowhere to go - `{...existing, accessToken: scripted}`
|
|
444
|
+
overwrote it outright with the personal token, and the OLD
|
|
445
|
+
`accessExpiresAt` stayed on the record describing a credential no longer
|
|
446
|
+
in that field. `hostedToken`'s `personal: true` fallback reads
|
|
447
|
+
`oauthAccessToken`/`oauthAccessExpiresAt` (added the same day by the fix
|
|
448
|
+
above), which the merge never wrote, so a personal-token rejection right
|
|
449
|
+
after a `GLYTCH_BOARD_TOKEN` merge always spent one refresh it did not
|
|
450
|
+
need on a credential that still had hours left. Fixed by moving the
|
|
451
|
+
existing OAuth access token and its expiry into their own fields at merge
|
|
452
|
+
time and clearing the personal record's own (inapplicable)
|
|
453
|
+
`accessExpiresAt`; the reverse direction (`glytch login` over an existing
|
|
454
|
+
personal record) now keeps the personal token and writes the fresh OAuth
|
|
455
|
+
pair into the same shape, and no longer clears a live personal-token dead
|
|
456
|
+
mark it did not fix. `bridge/lib/auth.mjs`.
|
|
457
|
+
|
|
458
|
+
- 2026-09-19: a merged auth record (a `pmb_` personal token persisted
|
|
459
|
+
alongside an existing OAuth pair, brief 43 review B7) marked its dead
|
|
460
|
+
credential under the REFRESH TOKEN's fingerprint even when the PERSONAL
|
|
461
|
+
token was the one the endpoint rejected, because `fingerprintSubject`
|
|
462
|
+
always preferred the refresh token when one was on file. `hostedToken`
|
|
463
|
+
read that same mark, fell through to a forced OAuth refresh on every call
|
|
464
|
+
- not just the first - and a successful refresh cleared the mark outright,
|
|
465
|
+
so the next poll offered the revoked personal token again, got rejected
|
|
466
|
+
again, and the cycle repeated at poll cadence forever, rotating the
|
|
467
|
+
refresh token every time. Fixed with two independent dead marks (one per
|
|
468
|
+
credential, `auth.json.dead` for the OAuth pair and the new
|
|
469
|
+
`auth.json.dead.personal` for the personal token), callers now telling
|
|
470
|
+
`noteCredentialRejected` which bearer was actually sent, an ordinary
|
|
471
|
+
(non-forced) refresh once the OAuth fallback is engaged, and the OAuth
|
|
472
|
+
access token living in its own `oauthAccessToken`/`oauthAccessExpiresAt`
|
|
473
|
+
fields on a merged record rather than sharing `accessToken`/
|
|
474
|
+
`accessExpiresAt` with the personal token. `bridge/lib/auth.mjs`,
|
|
475
|
+
`bridge/lib/hosted.mjs`, `bridge/lib/runnerApi.mjs`.
|
|
476
|
+
|
|
477
|
+
- 2026-09-19, persona walk on the one-paste prompt (`setupPromptText`, four
|
|
478
|
+
findings): the pasting agent runs `glytch init --yes` with no TTY, and
|
|
479
|
+
`glytch init` previously left it with no way to reach the board and no
|
|
480
|
+
clear signal it was ever going to. Claude Code now gets the board server
|
|
481
|
+
registered directly - LOCAL scope (private, wins over the project entry), a bearer header, never the project's
|
|
482
|
+
`.mcp.json` - from a personal token already on this machine when there is
|
|
483
|
+
no terminal to run its own OAuth verb in; codex and opencode, whose CLIs
|
|
484
|
+
were not available to verify a header-add against, instead get one
|
|
485
|
+
actionable last line (`Type /mcp in this tab and sign in to glytch.` or
|
|
486
|
+
the agent's own equivalent) rather than a warn row buried mid-output.
|
|
487
|
+
`glytch init`'s own final line now literally reads "A runner is watching
|
|
488
|
+
this folder." once every check lands, which is the exact phrase the
|
|
489
|
+
pasted prompt's stop condition already named and had never actually seen
|
|
490
|
+
printed. `glytch init --yes` with no agent binary on PATH and no saved
|
|
491
|
+
selection no longer throws before the folder is even registered - it
|
|
492
|
+
finishes, with a warn row naming what to install, and the runner still
|
|
493
|
+
connects to the board. The prompt itself now tells the pasting agent to
|
|
494
|
+
install a missing Node without `sudo` (Homebrew, or `fnm`/`nvm` for one
|
|
495
|
+
user) and to stop and ask rather than run one, so a Mac with no Node
|
|
496
|
+
cannot leave the agent hung on a password prompt nobody can answer.
|
|
497
|
+
|
|
498
|
+
- Brief 43 review, 2026-09-19: three findings on the stored `personal: true`
|
|
499
|
+
token. A revoked personal token no longer bypasses the dead gate forever -
|
|
500
|
+
it now gets its own mark, backoff and `glytch doctor` wording ("rejected",
|
|
501
|
+
pointing at Settings rather than `glytch login`). `glytch login`, run with
|
|
502
|
+
`GLYTCH_BOARD_TOKEN` over an existing OAuth credential, now keeps that
|
|
503
|
+
credential's refresh token in the same record instead of overwriting it
|
|
504
|
+
whole, and `hostedToken()` falls back to it once the personal token has a
|
|
505
|
+
live dead mark. `glytch init --no-login`, run with `GLYTCH_BOARD_TOKEN`
|
|
506
|
+
set, no longer writes the token to `~/.glytch/auth.json` - the variable
|
|
507
|
+
still authenticates that one process, but a CI box that asked not to leave
|
|
508
|
+
a long-lived token on a shared runner's disk is now honoured.
|
|
509
|
+
|
|
510
|
+
- 2026-09-21, board reporting: a coding agent spawned from a prompt
|
|
511
|
+
on one board called `report` (or `propose`/`ask_user`/`mark`) and the
|
|
512
|
+
circles landed on whichever board the account had touched most recently,
|
|
513
|
+
not the board the prompt lived on - board-mcp's `boardFor` falls back to
|
|
514
|
+
`lastBoardId`, unset until the first `get_board`. The run's own row always
|
|
515
|
+
carried `board_id` (`src/runs/api.ts`'s `RunRequestRow`); the runner now
|
|
516
|
+
reads it off the claimed row and sends it on as an `X-Glytch-Board`
|
|
517
|
+
header, next to the existing `X-Glytch-Session`, on every engine's board
|
|
518
|
+
MCP config (`acpServersFor`, the headless `claude` engine's
|
|
519
|
+
`--mcp-config`, `prepareWorkspace` for opencode, `codexBoardArgs` for
|
|
520
|
+
codex). `supabase/functions/board-mcp/index.ts` reads the header the same
|
|
521
|
+
way it already reads `X-Glytch-Session` and seeds `boardTools`'s
|
|
522
|
+
`lastBoardId` with it before any tool call runs, so a session whose FIRST
|
|
523
|
+
call is `report` lands on the run's own board. No header behaves exactly
|
|
524
|
+
as before; a header naming a board this token cannot read fails the same
|
|
525
|
+
way an unknown `board_id` argument already does. `bridge/lib/runner.mjs`,
|
|
526
|
+
`bridge/lib/agents.mjs`, `bridge/lib/codex.mjs`,
|
|
527
|
+
`supabase/functions/board-mcp/index.ts`,
|
|
528
|
+
`supabase/functions/board-mcp/boardHeader.mjs` (new),
|
|
529
|
+
`supabase/functions/board-mcp/tools.mjs`.
|
|
530
|
+
|
|
531
|
+
### Added
|
|
532
|
+
|
|
533
|
+
- 2026-09-21, brief 46 follow-up C: a Claude Code agent's Limits pane was
|
|
534
|
+
missing the row the design asked for - `AgentCaps` had no per-run token
|
|
535
|
+
cap, so "Most tokens in a run" was left out rather than faked. Added
|
|
536
|
+
`maxTokensPerRun` (optional, defaulting to 400,000 - the same figure
|
|
537
|
+
`RUN_TOKEN_CEILING` already fixes, but a different wall: that one is the
|
|
538
|
+
LAST turn's context fill and never moves, this one is the RUNNING TOTAL
|
|
539
|
+
of every turn's input, output and cache tokens, which an agent may raise
|
|
540
|
+
or lower). It travels on the request the same way `maxSpendPerRun`
|
|
541
|
+
already does, and `runLimitHit` (`runLimits.mjs`) now checks it - after
|
|
542
|
+
cost, before the fixed context ceiling - and latches `limitHit: "tokens"`
|
|
543
|
+
with its own wrap-up message when it fires. `bridge/lib/child.mjs` sums
|
|
544
|
+
input+output+cache off every assistant usage event for the running
|
|
545
|
+
total, where it previously only kept the LAST event for the context
|
|
546
|
+
snapshot.
|
|
547
|
+
|
|
548
|
+
- 2026-09-21, brief 46 (triggers) Move 4: a Codex run now reads its own
|
|
549
|
+
5-hour and weekly plan windows (`account/rateLimits/read`, one extra call
|
|
550
|
+
on the same app-server connection, right after `initialize`) before
|
|
551
|
+
spending anything. A run whose request names a cap
|
|
552
|
+
(`request.planWindowPercent`, default 80) and whose 5-hour window is
|
|
553
|
+
already past it never reaches `thread/start`: it settles with a receipt -
|
|
554
|
+
"Its plan's 5-hour window is at N%, past the M% it stands down at; it
|
|
555
|
+
will try again after HH:MM" - and nothing is charged (0 turns, cost
|
|
556
|
+
still "unavailable"). An older Codex with no such method reads as "no
|
|
557
|
+
window": the run starts, and the log says so in plain words. The info
|
|
558
|
+
line a person reads in place of a dollar figure now names both windows
|
|
559
|
+
("Codex reports its plan window, not dollars: 5-hour at N%, week at M%.")
|
|
560
|
+
once a read succeeds, and the runner project's presence carries the same
|
|
561
|
+
reading under `plan` after every Codex run, for the Limits pane to draw
|
|
562
|
+
as two bars. `lib/codexPlanWindow.mjs` is the whole parser/decision/
|
|
563
|
+
message module, deliberately pure and tested on its own; `lib/codex.mjs`
|
|
564
|
+
and `lib/runner.mjs` only wire it in.
|
|
565
|
+
|
|
566
|
+
- `glytch init --yes` and `glytch login`, run with `GLYTCH_BOARD_TOKEN` set,
|
|
567
|
+
now persist it to `~/.glytch/auth.json` as a non-expiring personal token and
|
|
568
|
+
open no browser - so a daemon started from a shell with no such variable of
|
|
569
|
+
its own still has a bearer for the board. `glytch doctor`'s sign-in row
|
|
570
|
+
reports it as "a personal access token is stored in ~/.glytch/auth.json".
|
|
571
|
+
|
|
572
|
+
### Removed
|
|
573
|
+
|
|
574
|
+
- The `postinstall` script. It pre-fetched the cloudflared binary on every
|
|
575
|
+
install of the package, and cloudflared is only ever used by a SHARED
|
|
576
|
+
PREVIEW - so most machines paid for a download they never used, and a
|
|
577
|
+
machine that could not reach the download printed a warning line during
|
|
578
|
+
`npm install` (reported through the app's feedback). `lib/previewTunnel.mjs`
|
|
579
|
+
already calls `ensureCloudflared` lazily and says in plain words what to do
|
|
580
|
+
when it fails, so the fetch now happens the first time a preview is actually
|
|
581
|
+
shared. `lib/installCloudflared.mjs` went with it; nothing else imported it.
|
|
582
|
+
|
|
583
|
+
## [0.7.0] - 2026-09-14
|
|
584
|
+
|
|
585
|
+
The published 0.6.0 predates the hosted endpoint and the rows runner. Against
|
|
586
|
+
the app deployed since 2026-09-11 it cannot work: the app no longer writes the
|
|
587
|
+
`.glytch/canvas/` mirror 0.6.0's local MCP server read, and a session launched
|
|
588
|
+
from the board is a row 0.6.0's runner never looks at. Install this one.
|
|
589
|
+
|
|
590
|
+
### Added
|
|
591
|
+
|
|
592
|
+
- `glytch login` / `glytch logout`: sign this machine in to the hosted board
|
|
593
|
+
endpoint over OAuth once; the credential lands in `~/.glytch/auth.json` and
|
|
594
|
+
every agent session the runner starts uses it.
|
|
595
|
+
- The runner claims, streams and settles runs as rows over the hosted
|
|
596
|
+
`board-runner` endpoint: replies, stops, worktree ops, previews and the
|
|
597
|
+
runner's own presence are rows too. Nothing under `.glytch/inbox/` is read or
|
|
598
|
+
written any more.
|
|
599
|
+
- Read-only plan-mode drafts for Codex sessions.
|
|
600
|
+
- Images attached to a prompt or a reply ride the run rows to the session.
|
|
601
|
+
|
|
602
|
+
- `glytch uninstall [--project] [--keep-login] [--dry-run] [--yes]`: removing
|
|
603
|
+
glytch, its board grant and everything it wrote is one command instead of
|
|
604
|
+
`forget`, `stop` and `logout` run by hand. Confirms once with a printed plan
|
|
605
|
+
before touching anything (`--yes` skips the prompt; a non-TTY without it
|
|
606
|
+
prints the plan and refuses). Without `--project` it walks every registered
|
|
607
|
+
folder (delegating to `uninstallProject`), then stops the login daemon and
|
|
608
|
+
removes its file, revokes this machine's board sign-in at the board and
|
|
609
|
+
forgets the credential (a network failure is a warning, not a failure - the
|
|
610
|
+
credential is still deleted locally), and deletes `~/.glytch` outright.
|
|
611
|
+
`--project` narrows the whole run to the current folder, with no
|
|
612
|
+
machine-wide step. `--keep-login` leaves the machine signed in and its
|
|
613
|
+
credential on disk. `--dry-run` reports what every step would do and
|
|
614
|
+
changes nothing. `forget`, `stop` and `logout` remain as the three partial
|
|
615
|
+
verbs it runs together.
|
|
616
|
+
- `glytch init` signs this machine in when it is not already, running the same
|
|
617
|
+
OAuth flow `glytch login` runs instead of printing "now run `glytch login`".
|
|
618
|
+
`glytch login` stays as its own command for re-auth.
|
|
619
|
+
- `glytch init` runs each selected and installed agent's own sign-in:
|
|
620
|
+
`claude mcp login`, `codex mcp login`, `opencode mcp auth`. Each agent keeps
|
|
621
|
+
its own token under its own account - there is no shared credential written
|
|
622
|
+
into a config file. Claude Code's verb needs 2.1.186 or newer; below that
|
|
623
|
+
floor `init` prints the `/mcp` step, as it did before. An agent that already
|
|
624
|
+
reports holding a token is skipped; Codex does not report one either way, so
|
|
625
|
+
its sign-in runs every time and short-circuits itself.
|
|
626
|
+
- `glytch init --no-login` skips signing this machine in, and
|
|
627
|
+
`glytch init --no-agent-login` skips every agent's sign-in. Both print the
|
|
628
|
+
command they did not run. Without a terminal, `init` prints the commands
|
|
629
|
+
rather than opening a browser, with or without the flags.
|
|
630
|
+
|
|
631
|
+
- A run stops at its own two walls: a spend cap (2 dollars by default, or the
|
|
632
|
+
agent's own `maxSpendPerRun`) and a fixed 200,000-token context ceiling.
|
|
633
|
+
Either wall ends the run the same way - it is told to finish the turn it is
|
|
634
|
+
on, commit whatever is green, report and stop - and the session's report
|
|
635
|
+
carries an extra line naming which wall it was. The runner counts, off the
|
|
636
|
+
engine's own usage events and result envelopes, so nothing depends on a
|
|
637
|
+
session noticing its own size. The two do not land at the same moment and
|
|
638
|
+
the code says so: a context wall fires mid-turn while stdin is still open, a
|
|
639
|
+
cost wall as the turn ends. A one-shot child, which has no stdin to talk to,
|
|
640
|
+
falls back to the existing kill path. The ceiling is a fixed number and
|
|
641
|
+
never a share of the model's window.
|
|
642
|
+
|
|
643
|
+
### Changed
|
|
644
|
+
|
|
645
|
+
- **`npm publish` no longer runs the test suite.** `prepublishOnly` was
|
|
646
|
+
`node --test`, so every publish re-ran 1,070 tests, two of which race real
|
|
647
|
+
concurrent filesystem operations and assert an exact winner count. On one
|
|
648
|
+
machine, in one shell, those two intermittently answer 0 or 2 instead of 1,
|
|
649
|
+
and a person trying to ship met that instead of a release roughly ten times.
|
|
650
|
+
It never caught a real defect in a release. `prepublishOnly` is now
|
|
651
|
+
`scripts/prepublish.mjs`: it checks that every file `files` promises exists,
|
|
652
|
+
that both bins carry a shebang, that all 63 shipped modules parse, and that
|
|
653
|
+
the version is still free on npm. It takes about a second and cannot answer
|
|
654
|
+
differently twice. The suite still runs in `npm run verify` and as its own
|
|
655
|
+
step in the release workflow, where a failure still stops the publish.
|
|
656
|
+
- The package description on npm. It advertised "a stdio MCP server that
|
|
657
|
+
speaks the app's folder contract (canvas/ mirror out, inbox/ ops in)" - a
|
|
658
|
+
server that was deleted and a contract that is gone. It now says what the
|
|
659
|
+
package does: run coding agents from the board, in a worktree, streamed back
|
|
660
|
+
to the canvas.
|
|
661
|
+
- Admission's winner-count tests print why a count is wrong - the refusals,
|
|
662
|
+
the lock's bytes, the directory listing - because the directory is deleted
|
|
663
|
+
before anyone can look at it. One real gap closed alongside: the lock's
|
|
664
|
+
bytes and the inode filed as their generation were two separate reads of a
|
|
665
|
+
path another caller could replace in between, so they come off one open
|
|
666
|
+
handle now.
|
|
667
|
+
|
|
668
|
+
- `glytch init` writes the hosted endpoint URL (`https://prompt.glytch.ai/mcp`)
|
|
669
|
+
into each selected agent's configuration instead of a local server command;
|
|
670
|
+
the agent's own OAuth flow finishes connecting it, and `glytch doctor` names
|
|
671
|
+
that step when it is missing.
|
|
672
|
+
- On Windows the login daemon is registered from an XML task definition
|
|
673
|
+
(`~/.glytch/ai.glytch.runner.xml`) naming your own account on the trigger and
|
|
674
|
+
the principal, which a standard user may register. The old `schtasks /sc
|
|
675
|
+
onlogon` command line registered an all-users task and was refused with
|
|
676
|
+
"Access is denied" outside an elevated shell; it stays only as a fallback.
|
|
677
|
+
- `glytch doctor` rows can carry their own next step; a refused Windows
|
|
678
|
+
registration names the elevated re-run or the by-hand runner command instead
|
|
679
|
+
of sending you back to `glytch init`. A refused `schtasks /query` is no longer
|
|
680
|
+
read as "task missing", and a row whose load failed no longer says "loaded now".
|
|
681
|
+
|
|
682
|
+
- `glytch doctor`'s `board sign-in` row reports whether the credential WORKS,
|
|
683
|
+
not just whether `auth.json` exists. It used to read green on exactly the
|
|
684
|
+
machine that could not authenticate. It reads the dead-credential mark the
|
|
685
|
+
daemon leaves beside the credential, so it still costs no network call.
|
|
686
|
+
|
|
687
|
+
- `glytch init` step 1 is an arrow-key checkbox picker instead of a numbered
|
|
688
|
+
list and a typed comma-separated answer: Up/Down (`j`/`k`) move, Enter or
|
|
689
|
+
Space toggles the row under the cursor, `a` toggles all, and a "Continue"
|
|
690
|
+
row below the agents is where Enter finishes - Escape/Ctrl-C cancels the
|
|
691
|
+
same way the old prompt did on Ctrl-C. Piped or non-TTY stdin, `--agents`
|
|
692
|
+
and `--yes` are unchanged - there is no picker to open without a terminal.
|
|
693
|
+
No new dependency (`bridge/lib/picker.mjs`).
|
|
694
|
+
- The MCP server key is `glytch` (was `board`). `glytch init` and `glytch
|
|
695
|
+
doctor` rename an existing entry on the hosted URL in place, the same
|
|
696
|
+
upgrade path a legacy spawned-command entry already took. Per this
|
|
697
|
+
package's own definition this is a breaking change to the folder contract
|
|
698
|
+
- renaming what a tool is called - made survivable by the in-place upgrade.
|
|
699
|
+
- The terminal output is one presentation: `init` is a numbered four-step
|
|
700
|
+
walk, browser hand-offs name which agent is asking and show the URL dimmed,
|
|
701
|
+
results are green, warnings yellow, failures red, and the first line of an
|
|
702
|
+
error is red with the rest left plain so a pasted error stays readable.
|
|
703
|
+
`NO_COLOR`, `TERM=dumb` and a pipe still get plain text, and everything
|
|
704
|
+
wraps to the terminal width. No new dependency.
|
|
705
|
+
|
|
706
|
+
- `glytch doctor`'s `board sign-in` row names WHICH account the credential
|
|
707
|
+
reaches: the email, or the first segment of the account id when the board can
|
|
708
|
+
name the id but not the address. "Signed in" was the whole reason before, and
|
|
709
|
+
a credential left on a previous account reads exactly like one on the right
|
|
710
|
+
account - which is how two days of session reports landed on a board nobody
|
|
711
|
+
was looking at. The name comes from the board's `presence` op, which
|
|
712
|
+
resolved that very token, and the row already made that call, so it costs no
|
|
713
|
+
extra round trip.
|
|
714
|
+
|
|
715
|
+
### Removed
|
|
716
|
+
|
|
717
|
+
- Unused mirror reader, mirror-based board-root discovery, file-tool ID
|
|
718
|
+
validator, and generated bridge board-digest output. The canonical digest
|
|
719
|
+
source and the live hosted copies remain.
|
|
720
|
+
- Unused file heartbeat writers, receipt/grace defaults, file-based per-prompt
|
|
721
|
+
worktree check, and the macOS `launchagent.mjs` adapter. Row-backed
|
|
722
|
+
presence, admission, and historical scratch cleanup are unchanged. The
|
|
723
|
+
macOS tests now use the current login-daemon API directly, and the atomic
|
|
724
|
+
replacement tests exercise the live folder registry.
|
|
725
|
+
- `pm-agent`, the local stdio MCP server, and its `pm-agent` bin. `glytch mcp`
|
|
726
|
+
prints where the hosted endpoint is instead.
|
|
727
|
+
- The `.glytch/` mailbox (spawn requests, claims, receipts, logs, sidecars and
|
|
728
|
+
the merge/delete/relaunch/preview signals as files). Folder linking stays for
|
|
729
|
+
project files only.
|
|
730
|
+
- `GLYTCH_BOARD_SOURCE` and the `.glytch/config.json` row source added in
|
|
731
|
+
0.6.0: every board read is over the hosted endpoint now. `glytch login`'s
|
|
732
|
+
credential is the normal way in; `GLYTCH_BOARD_TOKEN` with a personal access
|
|
733
|
+
token still works for a script.
|
|
734
|
+
|
|
735
|
+
- The `board mirror` row is gone from `glytch doctor`. It read `.glytch/canvas/`,
|
|
736
|
+
a mirror nothing has written since the `.glytch/` mailbox contract was
|
|
737
|
+
deleted, so on every correctly installed machine it reported FAIL, made
|
|
738
|
+
`allGreen` false and gave `glytch doctor` exit 1, while telling the person to
|
|
739
|
+
"open the board and link this folder" on a folder that was already linked.
|
|
740
|
+
Whether a folder is linked is what the `daemon beating` row answers now.
|
|
741
|
+
|
|
742
|
+
### Fixed
|
|
743
|
+
|
|
744
|
+
- A credential the board has REFUSED is now presented once and then backed
|
|
745
|
+
off, instead of being re-presented at the watch loops' poll cadence for as
|
|
746
|
+
long as the machine is on. Five folders at `pollMs` 5s meant five posts of a
|
|
747
|
+
revoked refresh token every five seconds, and presenting a spent refresh
|
|
748
|
+
token is itself what makes board-oauth revoke the family, so the loops kept
|
|
749
|
+
re-arming the alarm that had killed them. `hostedToken` now tells a DEAD
|
|
750
|
+
credential (`invalid_grant`, `invalid_client`, or a 401 that survived the
|
|
751
|
+
forced-refresh retry) from a merely stale one, and backs off 60s doubling to
|
|
752
|
+
30 minutes; a 5xx, a timeout or an unreadable body is still treated as a
|
|
753
|
+
blip and retried as before. The backoff clears itself the moment the
|
|
754
|
+
credential changes, so `glytch login` needs no other step, and a successful
|
|
755
|
+
refresh deletes it outright. The mark lives beside `auth.json` as a SHA-256
|
|
756
|
+
fingerprint of the refresh token, never the token, so a second process sees
|
|
757
|
+
it too.
|
|
758
|
+
- A person whose runner cannot authenticate is told in one sentence what to do
|
|
759
|
+
("Your Prompting Machine sign-in has expired and cannot be renewed: run
|
|
760
|
+
`glytch login` to sign in again"), once, rather than being left to read a
|
|
761
|
+
log. It replaces the "Not signed in to the hosted board" wording on this
|
|
762
|
+
path, which was inaccurate and sent people to `glytch init`: they ARE signed
|
|
763
|
+
in, with a credential the board will not take.
|
|
764
|
+
|
|
765
|
+
- `glytch login` against a local app (`GLYTCH_APP_URL` pointed at a dev server
|
|
766
|
+
mirroring vercel.json's rewrites) derived the RFC 8707 `resource` as
|
|
767
|
+
`${appUrl}/mcp` and was refused by the real authorize endpoint ("This server
|
|
768
|
+
issues tokens for https://prompt.glytch.ai/mcp only") - the resource a token
|
|
769
|
+
is for is whatever the app's `/.well-known/oauth-protected-resource/mcp`
|
|
770
|
+
document names, not the app origin, and board-oauth's configured resource is
|
|
771
|
+
production regardless of which origin a person pasted. `login()` now reads
|
|
772
|
+
that document first and uses its `resource` (and the authorization server it
|
|
773
|
+
names) throughout, storing the real endpoint as `mcpUrl` while `appUrl`
|
|
774
|
+
stays the configured app; an older deploy with no such document falls back
|
|
775
|
+
to the previous guess and says so.
|
|
776
|
+
- `glytch doctor`'s "daemon beating" row reported a live daemon as stopped
|
|
777
|
+
forever: it read a local mark file nothing over rows has written since
|
|
778
|
+
brief 30 replaced runner presence with `runner_projects.last_seen_at`, so
|
|
779
|
+
the row's mtime froze at whatever an earlier, file-based daemon last wrote.
|
|
780
|
+
The row now asks the board directly, through a new read-only `presence` op.
|
|
781
|
+
|
|
782
|
+
- The money figure on a Claude Code run was always empty. The runner read
|
|
783
|
+
`costUSD`; Claude Code emits `total_cost_usd`. It now reads
|
|
784
|
+
`total_cost_usd` first and falls back to the two older spellings, and takes
|
|
785
|
+
the LAST envelope rather than adding them up, because the figure is the
|
|
786
|
+
session total so far and summing it bills a long run several times over.
|
|
787
|
+
|
|
788
|
+
## [0.6.0] - 2026-09-11
|
|
789
|
+
|
|
790
|
+
### Added
|
|
791
|
+
|
|
792
|
+
- A board with no `canvas/` mirror can now be read and written over the hosted
|
|
793
|
+
board endpoint, so a folder that has never had the app open on it still has
|
|
794
|
+
a board. Turn it on with `GLYTCH_BOARD_SOURCE=rows` and a personal access
|
|
795
|
+
token in `GLYTCH_BOARD_TOKEN`; `get_board`, `get_node` and `propose` answer
|
|
796
|
+
from the cloud, and everything else still needs the folder. A mirror on this
|
|
797
|
+
disk always wins where there is one.
|
|
798
|
+
- The row source can be configured in `.glytch/config.json` in the board root
|
|
799
|
+
(`boardSource`, `boardToken`, `boardUrl`) instead of the environment, for a
|
|
800
|
+
bridge started by a launch agent that has no environment to read.
|
|
801
|
+
|
|
802
|
+
### Changed
|
|
803
|
+
|
|
804
|
+
- The "no canvas/ mirror found" refusal now names the row source as the second
|
|
805
|
+
way to read the board, rather than saying only that the app must be open.
|
|
806
|
+
An endpoint that answers 401 says the access token was revoked and where to
|
|
807
|
+
mint a new one, instead of reporting a generic refusal.
|
|
808
|
+
- Three unused internal exports are gone: `forgetServerRecord`,
|
|
809
|
+
`tellFileExists` and `ACP_PRICES_CHECKED`. No tool and no file in the folder
|
|
810
|
+
contract changes.
|
|
811
|
+
|
|
812
|
+
### Fixed
|
|
813
|
+
|
|
814
|
+
- An admission lock whose bytes do not parse, including the 0-byte one an
|
|
815
|
+
unclean shutdown can leave, is recovered instead of refusing every caller
|
|
816
|
+
forever. A lock that cannot be read is still never stolen, and both
|
|
817
|
+
refusals now name the cause: the errno, or the pid and host holding it.
|
|
818
|
+
- `glytch init`/`glytch doctor` rewrite and reload the login daemon file when
|
|
819
|
+
it names a different script than the one running the command (resolved
|
|
820
|
+
with `realpath`, so an npm-linked symlink still counts as the checkout),
|
|
821
|
+
instead of reporting "PATH captured" and leaving a stale package running -
|
|
822
|
+
previously the only fix was deleting the file by hand and reinstalling.
|
|
823
|
+
## [0.5.0] - 2026-09-10
|
|
824
|
+
|
|
825
|
+
### Added
|
|
826
|
+
|
|
827
|
+
- `get_node` returns images attached to a circle as native MCP image blocks,
|
|
828
|
+
each labelled with the node and field it came from. A stored image the
|
|
829
|
+
mirror does not hold is reported as unavailable instead of being omitted.
|
|
830
|
+
- Reference images on a run are validated before an agent starts: PNG, JPEG,
|
|
831
|
+
GIF and WebP are delivered whole, and an unsupported format or damaged data
|
|
832
|
+
refuses the run naming the attachment, with nothing spawned and nothing
|
|
833
|
+
billed.
|
|
834
|
+
- Sessions are instructed to inspect every reference image and record what
|
|
835
|
+
they see before planning or editing, and to compare finished visual work
|
|
836
|
+
against the references.
|
|
837
|
+
- An ACP adapter must advertise image prompt support before an image-bearing
|
|
838
|
+
turn is sent; without it the turn is refused rather than sent as text alone.
|
|
839
|
+
|
|
840
|
+
### Changed
|
|
841
|
+
|
|
842
|
+
- A planning draft researches read-only in the project checkout instead of
|
|
843
|
+
being given its own git worktree.
|
|
844
|
+
- A planning draft is asked for a structured report: findings in their own
|
|
845
|
+
sections and the work under an `## Implementation plan` heading with one
|
|
846
|
+
`### Step N: ...` heading per item.
|
|
847
|
+
- Engines without an image channel receive every validated image as a file
|
|
848
|
+
written beside the run; a write that fails aborts the run instead of
|
|
849
|
+
continuing with fewer images.
|
|
850
|
+
|
|
851
|
+
### Fixed
|
|
852
|
+
|
|
853
|
+
- Runs delivered the prompt without the appended image file paths on every
|
|
854
|
+
path except stdin, so file-based engines were told nothing about the images
|
|
855
|
+
written for them.
|
|
856
|
+
- Reference images in a reply to a running session are validated on arrival;
|
|
857
|
+
a malformed one ends the turn with an error instead of being dropped
|
|
858
|
+
silently mid-session.
|
|
859
|
+
|
|
860
|
+
## [0.4.2] - 2026-09-09
|
|
861
|
+
|
|
862
|
+
### Added
|
|
863
|
+
|
|
864
|
+
- Questions created through MCP can include an optional recommended answer and
|
|
865
|
+
explanation. Recommendations are advisory; the human still chooses an answer.
|
|
866
|
+
|
|
867
|
+
### Fixed
|
|
868
|
+
|
|
869
|
+
- Automatic worktree cleanup preserves the worktree when Git cannot inspect
|
|
870
|
+
its changes or establish a comparison base.
|
|
871
|
+
## [0.4.1] - 2026-09-09
|
|
872
|
+
|
|
873
|
+
### Changed
|
|
874
|
+
|
|
875
|
+
- `glytch init` and `glytch doctor` now use readable grouped status sections,
|
|
876
|
+
wrapped details, terminal colors with `NO_COLOR` support, actionable failure
|
|
877
|
+
summaries, and a home-folder warning. Doctor distinguishes a loaded daemon
|
|
878
|
+
from a fresh or stale folder heartbeat.
|
|
879
|
+
- README setup and troubleshooting guidance now keeps project-folder commands,
|
|
880
|
+
restart/update steps, and shared-preview requirements together.
|
|
881
|
+
## [0.4.0] - 2026-09-09
|
|
882
|
+
|
|
883
|
+
### Added
|
|
884
|
+
|
|
885
|
+
- Automatic member-only local previews for shared boards, with scoped short-lived
|
|
886
|
+
authorization grants and verified dev-server ownership. The preview issuer and
|
|
887
|
+
public verifier configuration are required; missing configuration fails closed.
|
|
888
|
+
- Automatic checksum-verified Cloudflare tunnel installation on macOS x64/ARM64,
|
|
889
|
+
Linux x64/ARM64 and Windows x64, with first-preview retry and a local cache.
|
|
890
|
+
|
|
891
|
+
- `glytch init` lets users choose one or more coding agents per project,
|
|
892
|
+
with `--agents claude,codex` for scripts and `--yes` for saved/detected
|
|
893
|
+
defaults. Project choices are remembered and respected by doctor and
|
|
894
|
+
background repair.
|
|
895
|
+
- Codex board MCP registration in project `.codex/config.toml`, preserving
|
|
896
|
+
existing settings and board entries. No global Codex config is changed.
|
|
897
|
+
|
|
898
|
+
### Fixed
|
|
899
|
+
|
|
900
|
+
- Preview requests reject unrelated processes that take over a dev-server port.
|
|
901
|
+
Ownership is checked again before forwarding member requests and WebSockets.
|
|
902
|
+
- Preview shutdown cancels pending startup and closes remaining tunnels even if
|
|
903
|
+
writing an offline receipt fails. Public gateways bound slow clients and filter
|
|
904
|
+
reserved credentials and connection-specific headers.
|
|
905
|
+
|
|
906
|
+
- Project config updates retain private file permissions.
|
|
907
|
+
- Release version bumps update and commit the package lockfile alongside
|
|
908
|
+
package metadata and the changelog; non-increasing versions are rejected.
|
|
909
|
+
|
|
910
|
+
### Changed
|
|
911
|
+
|
|
912
|
+
- Node.js 22 or newer is now required, matching the bundled Claude ACP
|
|
913
|
+
adapter's existing runtime requirement.
|
|
914
|
+
|
|
915
|
+
- Selected opencode installations receive project JSON/JSONC configuration
|
|
916
|
+
directly, preserving comments and avoiding a second interactive setup.
|
|
917
|
+
- First-time non-interactive init requires explicit choices or `--yes`;
|
|
918
|
+
later runs reuse saved choices. Legacy installs retain their existing
|
|
919
|
+
repair behavior until a selection is saved.
|
|
920
|
+
## [0.3.1] - 2026-09-07
|
|
921
|
+
|
|
922
|
+
### Changed
|
|
923
|
+
|
|
924
|
+
- **The `session-end` op says when a run was stopped, and why a start was
|
|
925
|
+
refused.** 2026-09-07, the runner QA sweep. `endSession` carries
|
|
926
|
+
`stopped: true` when the session ended because of a stop request (the
|
|
927
|
+
`.stop` file, which the runner now consumes only after the end op is
|
|
928
|
+
written), and `reason` when the start-receipt gate gave up. Both absent
|
|
929
|
+
unless set, so every earlier end op reads the same. The app's record read
|
|
930
|
+
"Finished" for a stopped run before this.
|
|
931
|
+
- **The start-receipt gate waits 120 s and polls every 500 ms.** It was 30 s,
|
|
932
|
+
sized for an app whose inbox tick was 10 s; measured receipts that day ran
|
|
933
|
+
1 to 64 s and two real runs were refused as never spawned. A refusal now
|
|
934
|
+
files the end op with its reason instead of leaving a record whose end
|
|
935
|
+
came before its start.
|
|
936
|
+
- **The spawn preamble asks for a plan.** Before the first edit, 3 to 8 steps
|
|
937
|
+
through the engine's own todo tool, kept current as items finish, plus
|
|
938
|
+
`Decided:` lines for choices worth seeing. opencode's tool is `todowrite`,
|
|
939
|
+
lower case, and was listed as absent; no opencode run had ever drawn a
|
|
940
|
+
step disc.
|
|
941
|
+
- **The runner's words after brief 26.** The preamble, `ask_user`'s
|
|
942
|
+
description, `release` and the stop receipt spoke of a session frame, a
|
|
943
|
+
DONE badge and a terminal; a run is a record on the prompt now, its
|
|
944
|
+
card shows it, questions land as circles, and a report goes through the
|
|
945
|
+
Glytch Agent.
|
|
946
|
+
- **A prompt accepts a comment.** `report.mjs` no longer refuses the
|
|
947
|
+
run's outcome line on the prompt it is about; the app accepts it since the
|
|
948
|
+
same day.
|
|
949
|
+
|
|
950
|
+
### Added
|
|
951
|
+
|
|
952
|
+
- **A chat turn's `cwd` is resolved against the watched folder, and
|
|
953
|
+
`mintIfMissing: true` cuts the agent's chat worktree.** Brief 25 session 1b,
|
|
954
|
+
2026-09-07: the two halves of the brief's section 3 meeting, after session 2
|
|
955
|
+
fixed what the app can actually send. The app writes `cwd` RELATIVE to the
|
|
956
|
+
folder root, because a browser directory handle has a name and not a path,
|
|
957
|
+
and it cannot create a worktree at all. So the runner resolves a relative
|
|
958
|
+
`cwd` against the folder whose `.glytch/inbox/spawn` it is watching, uses an
|
|
959
|
+
absolute one as it stands, and REFUSES anything that escapes the folder
|
|
960
|
+
(`..`, an absolute path outside it) with the resolved path in the receipt.
|
|
961
|
+
When `mintIfMissing: true` and the directory is not there, it cuts it once as
|
|
962
|
+
a git worktree on `glytch/chat-<agentId>` off the checked-out commit - the
|
|
963
|
+
same base `makeWorktree` picks, and the branch name read off the directory
|
|
964
|
+
name - with `node_modules` symlinked the way a session's tree gets it, and
|
|
965
|
+
logs one line. Without the flag session 1's refusal stands unchanged, and the
|
|
966
|
+
mint refuses both a folder that is not a git repository (a chat needs one for
|
|
967
|
+
its own worktree; there is no unisolated fallback to offer) and any path that
|
|
968
|
+
is not `<root>/.claude/worktrees/chat-*`, so a request file cannot turn "mint
|
|
969
|
+
if missing" into "make a directory anywhere in my folder". The sidecar's
|
|
970
|
+
`worktreePath` is the RESOLVED ABSOLUTE path, which is what makes the second
|
|
971
|
+
turn resume: the app hands it straight back as the next turn's `cwd`.
|
|
972
|
+
Ruling 1 (one long-lived chat worktree per agent per folder, made once and
|
|
973
|
+
kept) is now held on three sides: `cleanWorktree` refuses a chat path, the
|
|
974
|
+
sweep's never-touch list says in words that it removes no worktree and that a
|
|
975
|
+
later stale-worktree rule must skip `chat-*`, and the orphan reaper steps
|
|
976
|
+
over a `chat-` name. A chat worktree is removed when the AGENT is deleted,
|
|
977
|
+
which is not built. 12 more tests in `test/chatturn.test.mjs` (39 there,
|
|
978
|
+
688 in the bridge suite).
|
|
979
|
+
|
|
980
|
+
- **`kind: "chat-turn"` on a spawn request: a DM with a coding agent answered
|
|
981
|
+
by the daemon.** Brief 25 session 1, on the rulings of 2026-09-07. Ruling 1A
|
|
982
|
+
is that a coding agent's chat turn runs THROUGH the daemon, as a persistent
|
|
983
|
+
Claude Code conversation on his subscription, rather than as the OpenRouter
|
|
984
|
+
API call every chat turn was until now - so an agent whose engine said
|
|
985
|
+
`claude-code` had never once run on Claude Code when you talked to it. This
|
|
986
|
+
is the bridge half. **A request file gains one field and every request
|
|
987
|
+
written before today is untouched: absent `kind` means `"session"`.** A
|
|
988
|
+
chat-turn request names `cwd` (the room's own long-lived chat worktree,
|
|
989
|
+
ruling 1) and `roomId`, and differs from a session in five ways and no
|
|
990
|
+
others: no frame on the board and so no wait for the app's receipt (ruling
|
|
991
|
+
3, chat bubble only); no worktree minted, and a `cwd` that is not a
|
|
992
|
+
directory is refused rather than fallen back from; no repo primer, no
|
|
993
|
+
attempts log and no runner preamble, all three of which are about a frame it
|
|
994
|
+
does not have; a per-ROOM lock (`activeChatTurnForRoom`) in place of the
|
|
995
|
+
per-prompt one, so one live turn per room; and `chat` on line one of the
|
|
996
|
+
receipt, after the `OK`/`ERROR` both sides of the contract parse by. Stop
|
|
997
|
+
cancels the turn and KEEPS the conversation (ruling 5): the sidecar carries
|
|
998
|
+
the conversation id with no `expired` mark, and the sidecar is now written
|
|
999
|
+
for every chat turn rather than only a persistent one, because a first
|
|
1000
|
+
message has no session id to resume and that file is how the room learns
|
|
1001
|
+
which conversation it opened. No caps are checked here (ruling 2, "no need
|
|
1002
|
+
for caps with coding agents"); an empty `model` passes no `--model` (ruling
|
|
1003
|
+
7). An engine name this bridge does not have is REFUSED for a chat turn
|
|
1004
|
+
where a session falls back to the default - answering as `claude` would be
|
|
1005
|
+
the exact confusion the feature exists to end (`isCodingCli`,
|
|
1006
|
+
`lib/agents.mjs`). `SPAWN.md` gains a "Chat turns" section; 27 tests in the
|
|
1007
|
+
new `test/chatturn.test.mjs`, three compatibility pins in
|
|
1008
|
+
`test/runner.test.mjs`, and `chat-envelope` / `chat-hang` modes in the fake
|
|
1009
|
+
binary. The app half - room routing, the streamed bubble, the ledger - is
|
|
1010
|
+
sessions 2, 3 and 4 of the brief and is not in this release.
|
|
1011
|
+
|
|
1012
|
+
### Fixed
|
|
1013
|
+
|
|
1014
|
+
- **A resume ran in the wrong directory and never started.** Claude Code keeps
|
|
1015
|
+
conversations per working directory, and `runRequest` built the command line
|
|
1016
|
+
- `--resume <id>` included - a hundred lines BEFORE it chose the working
|
|
1017
|
+
directory. So a "run again" whose recorded worktree had been merged away got
|
|
1018
|
+
the fallback's fresh tree with the resume flag still attached, and the child
|
|
1019
|
+
answered `{"subtype":"error_during_execution","num_turns":0,
|
|
1020
|
+
"total_cost_usd":0,"errors":["No conversation found with session ID: ..."]}`.
|
|
1021
|
+
The receipt said "ended with an error result" and the frame read DONE with
|
|
1022
|
+
nothing in it (spawn-mtqay776-f76882, 2026-09-06 21:06 UTC). The worktree is
|
|
1023
|
+
chosen FIRST now and the resume is decided from the tree actually obtained.
|
|
1024
|
+
Three behaviours follow: a resume whose `worktreePath` is on disk runs IN it,
|
|
1025
|
+
on its existing branch, with no new tree; a resume whose tree is gone starts
|
|
1026
|
+
a FRESH session in a fresh tree, is told in one line of prompt that its
|
|
1027
|
+
history could not come with it, says so in plain words at the top of the
|
|
1028
|
+
receipt, and records the NEW session id in the sidecar so the next run
|
|
1029
|
+
resumes something that exists; and a `No conversation found` envelope at 0
|
|
1030
|
+
turns and $0.00 - the tree was there and Claude Code still had no record -
|
|
1031
|
+
retries ONCE as a fresh session in that same worktree. That retry does not
|
|
1032
|
+
break "Exactly once": the envelope says on its face that nothing was billed
|
|
1033
|
+
and nothing was done, so it is the first attempt happening rather than a
|
|
1034
|
+
second one being paid for, and a second failure is terminal. `SPAWN.md` gains
|
|
1035
|
+
a "Resume - the worktree IS the session's life" section; five tests in
|
|
1036
|
+
`test/runner.test.mjs` and a `no-conversation` mode in the fake binary.
|
|
1037
|
+
|
|
1038
|
+
### Added
|
|
1039
|
+
|
|
1040
|
+
- **Windows, session 5 of brief 21: the words, and the app half.** Two things
|
|
1041
|
+
a Windows user meets that are text rather than process control, and the one
|
|
1042
|
+
fact both of them need. Ruling 2A: `.mcp.json` names the board server
|
|
1043
|
+
`{"command":"cmd","args":["/c","glytch","mcp"]}` when the WRITER is on
|
|
1044
|
+
Windows, and keeps `{"command":"glytch","args":["mcp"]}` everywhere else -
|
|
1045
|
+
npm installs the bin as `glytch.cmd` there, and an MCP client spawning
|
|
1046
|
+
`glytch` with no shell hits the same Node 18.20.2 / 20.12.2 refusal every
|
|
1047
|
+
other binary in this package hits. Both writers take the platform as an
|
|
1048
|
+
argument (`lib/mcpjson.mjs` defaults to `process.platform`, because `glytch
|
|
1049
|
+
init` IS that machine; the app is handed the runner's), and
|
|
1050
|
+
`test/fixtures/mcp-json-cases.json` grew nine Windows cases that both sides'
|
|
1051
|
+
tests read, so the two copies still cannot drift. A file already carrying
|
|
1052
|
+
EITHER shape is left exactly as it is, which is the presence rule this file
|
|
1053
|
+
has always had and is what stops a folder shared between a Mac and a
|
|
1054
|
+
Windows box being rewritten back and forth. Ruling 3A: the sentence a
|
|
1055
|
+
spawn prompt carries when the Browser switch is on - how to start a dev
|
|
1056
|
+
server that outlives the session - is PowerShell's `Start-Process
|
|
1057
|
+
-WindowStyle Hidden` with `-PassThru` for the pid on Windows, and the
|
|
1058
|
+
`setsid` / `nohup` words unchanged to the byte everywhere else; option B,
|
|
1059
|
+
`start /b`, was rejected because it does not reliably leave the console and
|
|
1060
|
+
the child dies with it. The `.glytch-server.json` record is identical on
|
|
1061
|
+
both - only the words changed. THE MACHINE IS THE RUNNER'S, NOT THE
|
|
1062
|
+
BROWSER'S: the liveness mark gains an optional `platform` key beside `at`,
|
|
1063
|
+
`version`, `updateAvailable` and `agents` (`lib/platform.mjs`, written by
|
|
1064
|
+
both `lib/multiwatch.mjs` and `spawn-runner.mjs`), which is
|
|
1065
|
+
`process.platform` or `wsl` when `/proc/version` names Microsoft - naming
|
|
1066
|
+
WSL rather than folding it in, per ruling 6C, because WSL is the Linux
|
|
1067
|
+
that has `setsid`. A runner from before this brief writes no platform and
|
|
1068
|
+
the app reads that as the POSIX shape rather than as an error. `README.md`
|
|
1069
|
+
and `SPAWN.md` gained a Windows section.
|
|
1070
|
+
|
|
1071
|
+
- **Windows, session 1 of brief 21.** A `windows-latest` CI job
|
|
1072
|
+
(`.github/workflows/bridge-tests.yml`) now runs the bridge test suite
|
|
1073
|
+
alongside `ubuntu-latest` on every change under `bridge/`, and per
|
|
1074
|
+
ruling 7A it BLOCKS - no `continue-on-error`, so a Windows regression fails
|
|
1075
|
+
the same way a macOS one does rather than reporting green next to a red
|
|
1076
|
+
platform nobody looks at. Expected to be red for now: brief 19 scoped
|
|
1077
|
+
install to macOS, and the daemon, spawning through `.cmd` shims, and
|
|
1078
|
+
killing a process tree are later sessions' jobs. This session only fixed
|
|
1079
|
+
the seams that were pure bugs on any platform - none of them behavior
|
|
1080
|
+
changes on macOS, all of them proven with Windows-shaped strings rather
|
|
1081
|
+
than a Windows machine: `sync.mjs`'s `pathEntries`/`mergePaths` split PATH
|
|
1082
|
+
on `path.delimiter` instead of a hardcoded `":"`; `worktree.mjs`'s
|
|
1083
|
+
`ensureIgnored` recognizes an absolute `git rev-parse --git-common-dir`
|
|
1084
|
+
path with `path.isAbsolute` instead of `startsWith("/")`;
|
|
1085
|
+
`procs.mjs`'s `parseProcessTable` and `reapableWorktreeName` take an
|
|
1086
|
+
argv0's basename with `path.win32.basename` (a strict superset of the old
|
|
1087
|
+
`lastIndexOf("/")` slice, since it also treats `\` as a separator) instead
|
|
1088
|
+
of missing every backslash path; `reapableWorktreeName`'s worktree-path
|
|
1089
|
+
pattern now matches on whichever separator built `wtRoot`, not a hardcoded
|
|
1090
|
+
`/`; `messaging.mjs`'s session-list display name uses the same
|
|
1091
|
+
`path.win32.basename` instead of `.split("/").pop()`.
|
|
1092
|
+
|
|
1093
|
+
- **Windows, session 3 of brief 21: what stops a process, and what proves it is
|
|
1094
|
+
the right one.** Every kill in the package was a POSIX signal, and on Windows
|
|
1095
|
+
Node maps both SIGTERM and SIGKILL to TerminateProcess - so the grace before
|
|
1096
|
+
the hard kill was a fiction, and it ended one pid while `claude.cmd` runs the
|
|
1097
|
+
real agent as a grandchild that survives and keeps billing. Ruling 4A
|
|
1098
|
+
(2026-09-07): `taskkill /pid <pid> /t` then `/t /f` after the same grace,
|
|
1099
|
+
behind the SAME verification guard. One seam, `lib/kill.mjs`, now holds both
|
|
1100
|
+
spellings and is used by the session kill closure, the worktree reaper and
|
|
1101
|
+
the dev-server stop; the macOS path is the same two signals sent by the same
|
|
1102
|
+
caller, unchanged to the byte. `verifyRecordedServer` gained the two Windows
|
|
1103
|
+
evidence sources that guard makes it need - `netstat -ano` for the pid
|
|
1104
|
+
listening on the recorded port, `Get-CimInstance Win32_Process` for the start
|
|
1105
|
+
time and command line - injected exactly where `ps` and `lsof` were, with
|
|
1106
|
+
pure parsers pinned against sample output. Option B of the same ruling, kill
|
|
1107
|
+
with no verification, was rejected on its face: it kills whatever inherited a
|
|
1108
|
+
reused pid.
|
|
1109
|
+
|
|
1110
|
+
- **Windows, session 2 of brief 21: spawning at all.** The three worst things
|
|
1111
|
+
brief 21's audit found - the `.cmd`-shim refusal (Node's own security fix
|
|
1112
|
+
since 18.20.2 / 20.12.2) and the `Path`-casing bug in the child's env
|
|
1113
|
+
allowlist - are fixed together, since either one alone still stops every
|
|
1114
|
+
session. A new `runnableCommand(bin, args, {platform, env})` in
|
|
1115
|
+
`bridge/lib/windows-command.mjs` resolves a binary to its `.cmd`/`.exe`/
|
|
1116
|
+
`.bat` through `PATHEXT` and `Path` (or hands it unresolved to a shell,
|
|
1117
|
+
which does its own search), pure and injectable so the whole thing is
|
|
1118
|
+
proven on macOS with a fake platform, env and file-existence check. It is
|
|
1119
|
+
used by `detect.mjs`'s agent probes (and, through the same default `exec`,
|
|
1120
|
+
`sync.mjs`'s opencode registration), `update.mjs`'s npm version check, and
|
|
1121
|
+
`child.mjs`'s session spawn - both `claude` and `opencode`, per ruling
|
|
1122
|
+
5B that both engines run on Windows in v1, codex staying grey. `childEnv`'s
|
|
1123
|
+
allowlist lookup is case-insensitive on Windows only (`Path` now matches
|
|
1124
|
+
`PATH`) and gains the Windows names a child needs to run at all:
|
|
1125
|
+
`SystemRoot`, `windir`, `COMSPEC`, `PATHEXT`, `USERPROFILE`, `HOMEDRIVE`,
|
|
1126
|
+
`HOMEPATH`, `APPDATA`, `LOCALAPPDATA`, `ProgramData`, `ProgramFiles`,
|
|
1127
|
+
`NUMBER_OF_PROCESSORS`, `OS` - without `SystemRoot` a Windows child cannot
|
|
1128
|
+
resolve DNS at all. Both session spawns now set `windowsHide: true`,
|
|
1129
|
+
harmless on POSIX. Detection and killing/cleanup (sessions 1, 3 and 4) are
|
|
1130
|
+
separate sessions and are not in this change.
|
|
1131
|
+
|
|
1132
|
+
- **A login daemon on Windows and on WSL, not only on macOS** (2026-09-07,
|
|
1133
|
+
Windows, session 4 of brief 21). `glytch init` installs a Task Scheduler
|
|
1134
|
+
per-user `onlogon` task on Windows (ruling 1A: Task Scheduler was chosen
|
|
1135
|
+
over a Startup shortcut and over a service because it can be QUERIED, which
|
|
1136
|
+
is what keeps `glytch doctor`'s two halves - is the daemon installed, and is
|
|
1137
|
+
it actually beating - working there) and a systemd user unit on WSL and
|
|
1138
|
+
Linux (ruling 6C: both native Windows and WSL count). One interface with
|
|
1139
|
+
three implementations, `lib/logindaemon.mjs`; `lib/launchagent.mjs` is now a
|
|
1140
|
+
thin re-export of the launchd one. `glytch doctor`'s first row names the
|
|
1141
|
+
machine - `macOS`, `Windows`, `WSL (Linux)` or `Linux` - and the daemon row
|
|
1142
|
+
is labelled in that platform's own words. An older WSL with no `systemctl
|
|
1143
|
+
--user` is told so in the row, with the fallback to type instead, rather
|
|
1144
|
+
than being reported as a daemon that merely is not running. Folder contract
|
|
1145
|
+
and install surface, so it belongs in this file.
|
|
1146
|
+
- **A `git` row in `glytch doctor`, as a warning** (ruling 8B). No git means
|
|
1147
|
+
no worktree, so sessions run unisolated in the linked folder - and on
|
|
1148
|
+
Windows it also means no Bash tool, which comes from Git for Windows. The
|
|
1149
|
+
row says what is lost and prints `warn`; it is not a FAIL and doctor still
|
|
1150
|
+
exits 0.
|
|
1151
|
+
- `GLYTCH_NO_DAEMON_INSTALL=1` writes the daemon's file and registers nothing,
|
|
1152
|
+
on all three platforms. `GLYTCH_NO_LAUNCHCTL=1` is its older name and keeps
|
|
1153
|
+
working.
|
|
1154
|
+
|
|
1155
|
+
- **A browser frame's dev server outlives the session that started it.** The
|
|
1156
|
+
second thing "a finished session exits" ended, caught by the 2026-09-06 test
|
|
1157
|
+
pass: a session with the Browser switch on ran `npx serve -l 8765`, filed the
|
|
1158
|
+
frame, finished, and the server died with the child - the frame on the board
|
|
1159
|
+
pointing at a port with no listener. Ruling A the same day: the session
|
|
1160
|
+
starts the server DETACHED and the cleanup sweep stops it later. The session
|
|
1161
|
+
records `{pid, port, cmd, startedAt}` in `.glytch-server.json` in its
|
|
1162
|
+
worktree; the runner mirrors that to `<id>.server` in the spawn channel at
|
|
1163
|
+
teardown so the record outlives the tree. `<id>.server` does not end in
|
|
1164
|
+
`.json` on purpose - the request scan would have read it as a spawn request.
|
|
1165
|
+
Folder contract, so it belongs in this file rather than in the internals.
|
|
1166
|
+
- **A fourth signal file, `<id>.relaunch`: start that dev server again.** The
|
|
1167
|
+
ask the same day - "When a dev server gets detached, add a button in terminal
|
|
1168
|
+
to relaunch dev server in this branch. Allows me to test browser." Because
|
|
1169
|
+
the sweep above stops the server later, a session frame outlives its port. The
|
|
1170
|
+
app files `<id>.relaunch` (an optional `{worktreePath}` and nothing else); the
|
|
1171
|
+
runner re-runs the `cmd` in the session's own record, detached, in the
|
|
1172
|
+
recorded worktree, writes a fresh record for both copies, and answers in
|
|
1173
|
+
`<id>.relaunch.result.txt` - its own file, so a relaunch can never overwrite
|
|
1174
|
+
the session's `.result.txt`. It costs no session and no credits. It refuses,
|
|
1175
|
+
by name, when there is no record, when the record has no `cmd`, when the
|
|
1176
|
+
worktree is gone, when the recorded port is held by something else, and when
|
|
1177
|
+
the command exits within 700ms of starting; a server that is still running is
|
|
1178
|
+
answered with the pid and port it is already on rather than started twice.
|
|
1179
|
+
The signal NEVER carries a command: the runner runs what a session recorded,
|
|
1180
|
+
so this is not a way to run arbitrary shell from the canvas.
|
|
1181
|
+
|
|
1182
|
+
### Changed
|
|
1183
|
+
|
|
1184
|
+
- **The daily sweep stops processes as well as removing files.** It stops the
|
|
1185
|
+
dev server recorded for a request when that request is swept at 14 days, and
|
|
1186
|
+
as soon as the worktree the server was serving is gone. It never kills on a
|
|
1187
|
+
bare pid: the process has to be listening on the recorded port or carrying
|
|
1188
|
+
the recorded command line, with a start time within ten minutes of the
|
|
1189
|
+
record's, or the sweep logs one line and leaves it alone. Pids are reused,
|
|
1190
|
+
and a wrong SIGTERM here is a SIGTERM to whatever the machine handed that
|
|
1191
|
+
number next.
|
|
1192
|
+
|
|
1193
|
+
### Fixed
|
|
1194
|
+
|
|
1195
|
+
- **Brief 21: CI green on ubuntu and windows, and what being red said.** The
|
|
1196
|
+
first run of `.github/workflows/bridge-tests.yml` (2026-09-07, on main
|
|
1197
|
+
`9a42d17`) failed both jobs - ubuntu 613 of 625, windows 572 of 613 with a
|
|
1198
|
+
file cancelled - while macOS was 625 of 625, which is the whole reason none
|
|
1199
|
+
of it was known. Four of the failures were REAL WINDOWS BUGS in shipped
|
|
1200
|
+
code, and this is the list a stranger installing the package would have met:
|
|
1201
|
+
`glytch runner` and `glytch mcp` both died before reaching their script,
|
|
1202
|
+
because `glytch.mjs` handed `import()` an absolute path and Windows reads
|
|
1203
|
+
`D:\...` as the URL scheme `d:` (it takes a `file://` URL now);
|
|
1204
|
+
`PM_CLAUDE_BIN` pointing at a file that is not there was reported as a
|
|
1205
|
+
session that ran and crashed, because an unresolved PATH was handed to
|
|
1206
|
+
cmd.exe, which exits 1 with a message rather than raising ENOENT (a path is
|
|
1207
|
+
never shelled now, a bare name still is); every receipt and refusal printed a
|
|
1208
|
+
mixed-separator `.glytch\inbox\spawn/spawn-....json`, because the words were
|
|
1209
|
+
built from the `join`ed constant (the contract's spelling is used for words,
|
|
1210
|
+
the joined one for opening files); and a real detached dev server was left
|
|
1211
|
+
running, because `commandsAgree` could not see through the quotes
|
|
1212
|
+
`Win32_Process` puts round an executable and its spaced arguments. The other
|
|
1213
|
+
36 were tests asserting the HOST rather than what they meant - a launchd
|
|
1214
|
+
plist suite inheriting a systemd runner, `"/"`-shaped literals compared
|
|
1215
|
+
against `join`ed paths, a `RegExp` built out of a backslashed temp directory,
|
|
1216
|
+
a rename awaited while the test still held the destination open (Windows
|
|
1217
|
+
refuses that, which is what session 3's retry loop is for), and a fixture
|
|
1218
|
+
that wrote its marker only from a signal handler on a platform with no
|
|
1219
|
+
signals. Two assertions were simply untrue and were replaced rather than
|
|
1220
|
+
relaxed: `lstat` cannot tell a junction from a directory symlink, and the
|
|
1221
|
+
proof of the junction is the file read through the link on a runner with no
|
|
1222
|
+
Developer Mode. `--test-timeout` went 60s to 180s because `node --test`
|
|
1223
|
+
applies it to the file entry as well: `runner.test.mjs` is 86 tests that each
|
|
1224
|
+
spawn a child and it does not fit in a minute on a Windows runner.
|
|
1225
|
+
|
|
1226
|
+
A SECOND ROUND, because a failing test hides what is behind it. With those
|
|
1227
|
+
out of the way the runner found four more shipped Windows bugs: `taskkill
|
|
1228
|
+
/pid <pid> /t` without `/f` asks by posting WM_CLOSE, so the polite pass
|
|
1229
|
+
ALWAYS fails for a console-less process and `killTree` threw that - which
|
|
1230
|
+
`stopRecordedServer` reads as "gone", so a verified detached dev server was
|
|
1231
|
+
never stopped there at all (the grace declined is now not a throw, and
|
|
1232
|
+
everything else taskkill can say still is); Windows has no signals, so
|
|
1233
|
+
`process.abort()` closes with code 134 and no signal, `classifyExit` called
|
|
1234
|
+
that a plain exit, and both halves of the crash-retry ruling were wrong on
|
|
1235
|
+
that platform (a 128+N table, gated on win32); the ACP Stop path sent
|
|
1236
|
+
`session/cancel` and killed, which gave the adapter a moment to act on it
|
|
1237
|
+
only by accident of asynchronous POSIX signal delivery, so on Windows it died
|
|
1238
|
+
with the cancel unread (it now waits for the turn to come back cancelled,
|
|
1239
|
+
bounded at 2s); and the repo primer handed to a session spelled its paths
|
|
1240
|
+
`src\index.js`, matching neither the imports in the files nor git nor a brief.
|
|
1241
|
+
No test was deleted; eight were added. Both jobs green at
|
|
1242
|
+
[34064042188](https://github.com/glytch-ai/prompting-machine/actions/runs/34064042188).
|
|
1243
|
+
|
|
1244
|
+
- **Two more Windows seams, from the same session.** A worktree's
|
|
1245
|
+
`node_modules` is linked with a `"junction"` on Windows rather than a
|
|
1246
|
+
directory symlink, which needs Developer Mode or admin there and otherwise
|
|
1247
|
+
throws EPERM - leaving the session unable to run the project's own tests,
|
|
1248
|
+
which is most of what a brief asks it to do. And `writeFileAtomic` retries a
|
|
1249
|
+
rename that fails EPERM or EBUSY, bounded at five tries over about 420ms:
|
|
1250
|
+
Windows file locking is mandatory, so a poller, an antivirus scanner or the
|
|
1251
|
+
search indexer holding the destination open loses a mark file, a registry
|
|
1252
|
+
write or an ops file outright. Neither code is ever produced on a Mac, so
|
|
1253
|
+
both paths are proven with injection.
|
|
1254
|
+
|
|
1255
|
+
- **`glytch init` in a second project is watched without a restart.** The
|
|
1256
|
+
daemon read `~/.glytch/folders.json` once at start, so the first test pass
|
|
1257
|
+
of brief 19 (2026-09-06) found a second project registered, reported green
|
|
1258
|
+
by `glytch doctor`, and watched by nothing until `launchctl kickstart -k
|
|
1259
|
+
ai.glytch.runner` was run by hand - the install-once promise held for the
|
|
1260
|
+
first folder only. It now watches the registry file itself (fs.watch on the
|
|
1261
|
+
file and on its directory, because an atomic write replaces the inode, plus
|
|
1262
|
+
a once-a-minute poll as the floor) and starts the new folder's sync and
|
|
1263
|
+
watcher live. The ruling: restarting the daemon from `init` was rejected,
|
|
1264
|
+
because a restart kills whatever session is running in another project. A
|
|
1265
|
+
folder that leaves the registry has its watcher stopped through its own
|
|
1266
|
+
AbortSignal, which drains the sessions it started; a registry that cannot
|
|
1267
|
+
be parsed is one log line and the last good list, never a stop and never an
|
|
1268
|
+
exit.
|
|
1269
|
+
|
|
1270
|
+
## [0.3.0] - 2026-09-06
|
|
1271
|
+
|
|
1272
|
+
NOTE FOR WHOEVER RELEASES THIS. `package.json` already reads **0.3.0**: brief
|
|
1273
|
+
19 set it by hand so the daemon's own version check has a real number to
|
|
1274
|
+
compare against npm. `scripts/release-pm-agent.mjs` bumps from what it finds,
|
|
1275
|
+
so cut this release as the manual path with the version left alone, or expect
|
|
1276
|
+
0.4.0 and say so here.
|
|
1277
|
+
|
|
1278
|
+
### Changed
|
|
1279
|
+
|
|
1280
|
+
- **The package is renamed to `glytch`**, the bare name, before its first
|
|
1281
|
+
publish - the morning's "add a bin now, rename after release" was reversed
|
|
1282
|
+
the same afternoon because the install line had not shipped anywhere yet,
|
|
1283
|
+
and the scoped `@glytch-ai/glytch` that reversal chose was itself replaced
|
|
1284
|
+
within the hour: the bare name is free on npm, it is what a tester types,
|
|
1285
|
+
and owning it is the stronger protection. The three bins (`glytch`,
|
|
1286
|
+
`pm-agent`, `pm-spawn-runner`) are unchanged, so every `.mcp.json` and
|
|
1287
|
+
opencode registration already written keeps working.
|
|
1288
|
+
- **A finished session exits.** The result envelope closes the child's stdin
|
|
1289
|
+
at once, so a `claude --input-format stream-json` session ends when its work
|
|
1290
|
+
does instead of holding an idle process open as a reply window until the
|
|
1291
|
+
90-minute wall clock. The exit settles exactly as before - finished, not a
|
|
1292
|
+
timeout, OK receipt, sidecar written, worktree kept - and the wall clock is
|
|
1293
|
+
unchanged for a session mid-turn. A reply that lands after the session ended
|
|
1294
|
+
becomes a new spawn request that resumes it in the same worktree, so nothing
|
|
1295
|
+
typed is lost. The ruling, 2026-09-06.
|
|
1296
|
+
|
|
1297
|
+
### Added
|
|
1298
|
+
|
|
1299
|
+
- **A third bin, `glytch`**, beside `pm-agent` and `pm-spawn-runner`. The two
|
|
1300
|
+
older ones are unchanged aliases, not deprecations, so nothing already
|
|
1301
|
+
wired into someone's `.mcp.json` has to move.
|
|
1302
|
+
`glytch init | doctor | forget | stop | runner ... | mcp | --version`;
|
|
1303
|
+
`runner` and `mcp` hand straight over to the existing scripts.
|
|
1304
|
+
- **`glytch init`**: registers the folder in `~/.glytch/folders.json`, writes
|
|
1305
|
+
`.mcp.json` by the same rules the app uses, runs `opencode mcp add board --
|
|
1306
|
+
glytch mcp` when opencode is on PATH, installs a macOS login agent so the
|
|
1307
|
+
spawn runner watches without a terminal open, prints which of `claude`,
|
|
1308
|
+
`opencode` and `codex` this machine has, and says to open the board. Safe to
|
|
1309
|
+
run twice: it puts back what is missing and touches nothing that is there.
|
|
1310
|
+
- **`glytch doctor`**: ten rows and an exit code - folder registered,
|
|
1311
|
+
`.mcp.json`, opencode registration, login agent and its captured PATH,
|
|
1312
|
+
daemon loaded, daemon beating, board mirror, agents found, Claude Code
|
|
1313
|
+
logged in, version against npm. Exits 0 when every row is green.
|
|
1314
|
+
- **A multi-root watch.** `glytch runner watch` with no `PM_BOARD_ROOT` reads
|
|
1315
|
+
the registry and watches every folder in it, each with its own runner id and
|
|
1316
|
+
its own `.runner.json` mark. A folder that has disappeared is one log line,
|
|
1317
|
+
never an exit. With `PM_BOARD_ROOT` set, behaviour is exactly as before.
|
|
1318
|
+
- **A cleanup sweep**, on daemon start and once a day: finished spawn requests
|
|
1319
|
+
whose receipt is over 14 days old, rotated logs past the keep count, and
|
|
1320
|
+
registry entries whose folder no longer exists. The mark file, the mirror,
|
|
1321
|
+
un-ingested ops and any unfinished request are never touched.
|
|
1322
|
+
- **Logs** at `~/.glytch/logs/runner.log`, rotated at 5 MB, three kept.
|
|
1323
|
+
- `GLYTCH_HOME` (moves the registry, the logs and the plist) and
|
|
1324
|
+
`GLYTCH_NO_LAUNCHCTL` (write the plist, load nothing).
|
|
1325
|
+
|
|
1326
|
+
- **The runner writes what the machine has into the mark file**, as an
|
|
1327
|
+
`agents` block: `{ claude: {found, version, loggedIn}, opencode: {found,
|
|
1328
|
+
version}, codex: {found} }`. Probed at daemon start and every five minutes,
|
|
1329
|
+
from a cache the liveness beat reads without waiting - a beat that blocks on
|
|
1330
|
+
three `--version` children is a beat the app calls stopped. A probe that
|
|
1331
|
+
fails is `found: false`, never a missing key and never a crash; a probe that
|
|
1332
|
+
has not answered yet writes no `agents` key at all, because "not asked yet"
|
|
1333
|
+
and "not installed" are different facts. Login is asked about Claude Code
|
|
1334
|
+
only, by the presence of `~/.claude/.credentials.json` - the file is never
|
|
1335
|
+
read.
|
|
1336
|
+
|
|
1337
|
+
### Changed
|
|
1338
|
+
|
|
1339
|
+
- **The runner mark file gains three optional keys**, `version`,
|
|
1340
|
+
`updateAvailable` and `agents`. This is NOT a change to the folder contract:
|
|
1341
|
+
`runnerId`, `pid` and `at` are exactly what they were, so an app built
|
|
1342
|
+
before these keys reads a new runner unchanged, and a new app reads a runner
|
|
1343
|
+
that writes none of them. Both directions are pinned by
|
|
1344
|
+
`test/fixtures/runner-mark-cases.json`, read by a test on each side of the
|
|
1345
|
+
import ban - `test/runnermark.test.mjs` here and
|
|
1346
|
+
`src/folder/spawnRequest.contract.test.ts` in the app.
|
|
1347
|
+
|
|
1348
|
+
### What the app does with it
|
|
1349
|
+
|
|
1350
|
+
Not this package's surface, and listed only because these keys exist FOR it -
|
|
1351
|
+
a reader wondering why a daemon probes anything should not have to go looking.
|
|
1352
|
+
The app (brief 19 stages 2 and 3, same day, not on main):
|
|
1353
|
+
|
|
1354
|
+
- draws a filled dot and the version for a found engine and a hollow one for a
|
|
1355
|
+
missing engine on the agent editor's engine row, the prompt card's Runs-on
|
|
1356
|
+
row and a new toolbar pill; a mark with no `agents` block draws no dots at
|
|
1357
|
+
all, because "nothing has looked" is not "you have none of these";
|
|
1358
|
+
- shows a setup card - `npm i -g glytch`, `cd <your project> && glytch init`,
|
|
1359
|
+
a Copy button - when a coding engine is picked with no runner
|
|
1360
|
+
watching, whose status row goes green by itself when the mark appears;
|
|
1361
|
+
- adds a guide step, "Connect your coding agent", done when the mark says a
|
|
1362
|
+
runner is watching.
|
|
1363
|
+
## [0.2.0] - 2026-09-02
|
|
1364
|
+
|
|
1365
|
+
### Changed
|
|
1366
|
+
|
|
1367
|
+
- `get_board` returns a **slim** digest by default, and takes filters:
|
|
1368
|
+
`tag`, `group`, `kind`, `since_days`, `match`, `limit`, plus `view`,
|
|
1369
|
+
`geometry` and `detail` to get the old shape back. Not a breaking change to
|
|
1370
|
+
the tool list, but it IS a change to what a call returns: geometry, the
|
|
1371
|
+
150-char body preview under each title, group member lists and the `AGES`
|
|
1372
|
+
block are omitted unless asked for, and every reply ends with a line naming
|
|
1373
|
+
what it left out. Measured on the live board: 12,014 tokens to 4,788, and
|
|
1374
|
+
1,401 with `tag:"next"`. The reason is a spend audit over 201 transcripts
|
|
1375
|
+
and 26,841 API requests on 2026-08-31 - one `get_board` cost about $0.50
|
|
1376
|
+
once you count it riding in context for the rest of the session, which was
|
|
1377
|
+
roughly 60% of everything the board bridge cost. Call frequency was already
|
|
1378
|
+
fine (107 of 137 board-using sessions called it exactly once), so the fix
|
|
1379
|
+
was to return less, not to call it less.
|
|
1380
|
+
- `report`'s tool description is shorter. It was 3,497 characters sitting in
|
|
1381
|
+
every request's system prompt, the largest of the twelve schemas. Rules are
|
|
1382
|
+
unchanged - the evidence rule is still on `flags`, where it is enforced -
|
|
1383
|
+
and the cut rationale is retired in a comment beside it, not deleted.
|
|
1384
|
+
- **A finished stream is finished.** A persistent streaming session emits its
|
|
1385
|
+
final result envelope and then holds its reply window open, so the runner's
|
|
1386
|
+
old process-exit reading killed done work at the wall clock and receipted it
|
|
1387
|
+
as ERROR: 26 of 28 ninety-minute kills had their envelope in the log, about
|
|
1388
|
+
$151 of finished work called a failure. The runner reads the envelope and
|
|
1389
|
+
settles the receipt as success; the wall clock stays exactly what it was for
|
|
1390
|
+
a child that never emits one (2adf56b).
|
|
1391
|
+
- **A session's verdict and cost land on the frame and in the receipt**, one
|
|
1392
|
+
line: "finished; $7.52, 41 turns, 38m, context 212k". Context fill is the
|
|
1393
|
+
LAST assistant step's input plus cache-read plus cache-creation tokens, never
|
|
1394
|
+
the cumulative total, and events carrying `parent_tool_use_id` are skipped so
|
|
1395
|
+
a subagent's small turn cannot overwrite the lead's number. The session
|
|
1396
|
+
sidecar gains `costUsd`, `numTurns` and `contextFill` (8ba824f, 168a485).
|
|
1397
|
+
- **A crash before any output is retried once**, inside the same wall clock.
|
|
1398
|
+
SIGSEGV, SIGABRT, SIGBUS, SIGILL and the Windows access-violation and
|
|
1399
|
+
stack-overflow codes count as crashes; SIGKILL never retries, and a crash
|
|
1400
|
+
AFTER any assistant event is not retried either, because the partial work
|
|
1401
|
+
would be duplicated. The receipt names the class (848fac8, 12f67c4).
|
|
1402
|
+
- **Worktree git mutations run one at a time per repo root.** Create, clean,
|
|
1403
|
+
reuse, merge and delete take an in-process lock; reads stay unlocked. Two
|
|
1404
|
+
sessions finishing together could both run checkout-main, pull and merge
|
|
1405
|
+
(565758e).
|
|
1406
|
+
- **The merge button keeps the branch and the worktree alive**, and does NOT
|
|
1407
|
+
push. Merging and deleting in one gesture had made the delete control
|
|
1408
|
+
meaningless (6579269, fa31197).
|
|
1409
|
+
|
|
1410
|
+
Corrected 2026-09-02: this entry said "and pushes the merged main to
|
|
1411
|
+
origin", naming the two commits that between them prove the opposite.
|
|
1412
|
+
`fa31197` (23:04) added `git push` after a merge; `6579269` (23:11) took it
|
|
1413
|
+
out again seven minutes later, replacing it with the comment that still
|
|
1414
|
+
stands - "No push: the user pushes when they are ready". The entry was
|
|
1415
|
+
written across both and kept the half that was reverted. `git log -S` over
|
|
1416
|
+
`bridge/lib/worktree.mjs` returns exactly those two commits and no others.
|
|
1417
|
+
|
|
1418
|
+
`git pull --ff-only` went the same way on 2026-09-02, for the same reason
|
|
1419
|
+
stated out loud: a merge is local work on local branches, and a network
|
|
1420
|
+
operation on his main branch that nobody asked for can move main under him,
|
|
1421
|
+
prompt for credentials with nobody watching, or hang on an absent remote
|
|
1422
|
+
while a button on his board spins.
|
|
1423
|
+
- **A prompt keeps its worktree and its session across runs**, so a re-run
|
|
1424
|
+
continues work rather than starting over (8eb136c).
|
|
1425
|
+
- **The propose vocabulary stops refusing nine ops the app has always
|
|
1426
|
+
accepted** (cffd809).
|
|
1427
|
+
|
|
1428
|
+
### Added
|
|
1429
|
+
|
|
1430
|
+
- `hooks/board-budget.mjs`, a PreToolUse cap: it shapes the digest exactly as
|
|
1431
|
+
the pending `get_board` would, measures it, and refuses only when the real
|
|
1432
|
+
number is over `PM_BOARD_MAX_TOKENS` (default 6,000), naming the filters
|
|
1433
|
+
that would fit. A cap rather than a confirmation prompt because MCP cannot
|
|
1434
|
+
prompt, the permission dialog cannot show a price, and these sessions run
|
|
1435
|
+
unattended - a dialog would stall an overnight run at the first board read.
|
|
1436
|
+
Wired in `.claude/settings.json` behind an existence check; it exits 0 on
|
|
1437
|
+
every ambiguous case, so it can never break a session.
|
|
1438
|
+
- **Two MCP tools, `list_sessions` and `send_message`**, so a session can
|
|
1439
|
+
discover the other sessions running in this project folder and deliver a
|
|
1440
|
+
message to one. Discovery scans `inbox/spawn/` for requests without
|
|
1441
|
+
receipts; delivery writes a from-stamped reply file the target's runner
|
|
1442
|
+
already polls. Twelve tools now, where 0.1.0 shipped ten (85e9e06).
|
|
1443
|
+
- **`PM_SPAWN_STALL_MS`** (default 15 minutes): a stall watchdog on a
|
|
1444
|
+
streaming session, armed only when the MODEL should be producing output -
|
|
1445
|
+
a turn open and no tool still running. A hung tool remains the wall clock's
|
|
1446
|
+
job. The receipt says "stopped responding", which is neither a success nor a
|
|
1447
|
+
timeout (848fac8, 12f67c4).
|
|
1448
|
+
- **A repository primer on a fresh session's prompt**: the file tree plus
|
|
1449
|
+
exported symbols per source file, deterministic, no model call, cached
|
|
1450
|
+
keyed to the HEAD sha and capped at ~10KB. A fresh session was spending its
|
|
1451
|
+
first turns rediscovering the tree (40061ad).
|
|
1452
|
+
- **An attempts log for a re-run.** A prompt that failed before is given
|
|
1453
|
+
bounded summaries of what earlier attempts tried and why they failed, built
|
|
1454
|
+
from receipts and log tails on disk with no model call, and the section says
|
|
1455
|
+
so when something was dropped (709a3c1).
|
|
1456
|
+
- **Images reach a coding session**, and a reply file can carry them
|
|
1457
|
+
(547f27d).
|
|
1458
|
+
|
|
1459
|
+
### Fixed
|
|
1460
|
+
|
|
1461
|
+
- The request scan skips the runner's own `*.session.json` sidecars and
|
|
1462
|
+
dotfiles instead of refusing them as malformed requests - 47 refusal
|
|
1463
|
+
receipts against its own bookkeeping, each one counting against the daily
|
|
1464
|
+
spend bound (2adf56b).
|
|
1465
|
+
- A session running in a worktree writes to the real board rather than into
|
|
1466
|
+
its own copy of the folder (2caedb1).
|
|
1467
|
+
- A headless session can no longer park itself in plan mode, where nothing
|
|
1468
|
+
would ever answer it (915b9d1).
|
|
1469
|
+
- A dead pipe, a duplicated reply, and an orphan reaper that killed processes
|
|
1470
|
+
merely mentioning a worktree path (49093ab).
|
|
1471
|
+
## [0.1.0] - 2026-08-28
|
|
1472
|
+
|
|
1473
|
+
First published version. Everything below already existed; this is the point
|
|
1474
|
+
it became installable by someone who does not have the repo.
|
|
1475
|
+
|
|
1476
|
+
### Added
|
|
1477
|
+
|
|
1478
|
+
- Ten MCP tools over stdio: `get_board`, `get_node`, `propose`, `ask_user`,
|
|
1479
|
+
`check_answer`, `report`, `open_session`, `close_session`,
|
|
1480
|
+
`collect_session`, `set_brief_status`.
|
|
1481
|
+
- `pm-agent` and `pm-spawn-runner` on PATH.
|
|
1482
|
+
- MIT licence, so the package can actually be used.
|
|
1483
|
+
- An install written for someone who has never seen the repo.
|