@youdie006/prodex 0.40.49 → 0.40.51

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/dist/tui.js CHANGED
@@ -120,10 +120,13 @@ export function projectsWithIdsExpression() {
120
120
  return out;
121
121
  })()`;
122
122
  }
123
- /** The conversations that live inside a project, or all of them without one. */
123
+ /**
124
+ * The conversations that live inside a project. A project whose id is unknown
125
+ * claims none: returning all of them listed unrelated chats under its name.
126
+ */
124
127
  export function conversationsInProject(conversations, projectId) {
125
128
  if (!projectId)
126
- return conversations;
129
+ return [];
127
130
  return conversations.filter((conversation) => conversation.gizmoId === projectId);
128
131
  }
129
132
  /**
@@ -358,12 +361,26 @@ export function renderProgressBar(input) {
358
361
  // Saying how to abort belongs on the waiting line itself: a consult can run
359
362
  // for many minutes and the only other thing on screen is the bar.
360
363
  const stop = "ctrl-c to stop";
361
- if (!input.budgetMs || input.budgetMs <= 0)
362
- return `${spinner} ${input.label} ${elapsed} ${stop}`;
363
- const width = Math.max(4, input.width ?? 28);
364
- const ratio = input.elapsedMs / input.budgetMs;
365
- const filled = Math.min(width, Math.round(Math.min(ratio, 1) * width));
366
- const bar = `${"#".repeat(filled)}${"-".repeat(width - filled)}`;
367
- const over = ratio > 1 ? " over budget" : "";
368
- return `${spinner} [${bar}] ${elapsed} / ${formatElapsed(input.budgetMs)} ${input.label}${over} ${stop}`;
364
+ let compose;
365
+ if (!input.budgetMs || input.budgetMs <= 0) {
366
+ compose = (label) => `${spinner} ${label} ${elapsed} ${stop}`;
367
+ }
368
+ else {
369
+ const width = Math.max(4, input.width ?? 28);
370
+ const ratio = input.elapsedMs / input.budgetMs;
371
+ const filled = Math.min(width, Math.round(Math.min(ratio, 1) * width));
372
+ const bar = `${"#".repeat(filled)}${"-".repeat(width - filled)}`;
373
+ const over = ratio > 1 ? " over budget" : "";
374
+ const budget = formatElapsed(input.budgetMs);
375
+ compose = (label) => `${spinner} [${bar}] ${elapsed} / ${budget} ${label}${over} ${stop}`;
376
+ }
377
+ const line = compose(input.label);
378
+ if (input.columns === undefined || displayWidth(line) < input.columns)
379
+ return line;
380
+ // The line is redrawn in place, and one that wraps leaves a row behind on
381
+ // every redraw (found 2026-11-09: 102 columns at 80, a new row every 250
382
+ // ms). The label gives way first; a terminal too narrow even for the rest
383
+ // gets the line cut.
384
+ const room = input.columns - 1 - displayWidth(compose(""));
385
+ return truncateToWidth(compose(room > 0 ? truncateToWidth(input.label, room) : ""), input.columns - 1);
369
386
  }
package/docs/clients.md CHANGED
@@ -52,12 +52,17 @@ follow-up checkpoint (integer 0-1000; default 5). For example:
52
52
  PRODEX_MAX_AUTO_FOLLOWUPS = "8"
53
53
  ```
54
54
 
55
- The initial fresh consult is not a follow-up. Reservations are counted before sends
56
- and persist across reconnects, older task references, and different session keys in
57
- the same bridge and conversation. Failed/uncertain attempts count too. At the limit,
55
+ The initial fresh consult is not a follow-up. A follow-up is reserved once its prompt
56
+ is typed, right before it is submitted, and reservations persist across reconnects,
57
+ older task references, and different session keys in the same bridge and
58
+ conversation. A send refused before that point, such as `composer_not_ready`, does
59
+ not count; an attempt that fails or is uncertain after submitting does. At the limit,
58
60
  the tool returns `status: "awaiting_user"`, `task_id: null`, the intended continuation
59
61
  arguments, and `followup_budget: {limit, used, remaining}` without creating a consult
60
- task or sending a prompt. This response-only status is not a ledger task status.
62
+ task or sending a prompt. This response-only status is not a ledger task status. If
63
+ another follow-up in the same conversation uses the last of the budget while this one
64
+ waits for the browser, this one stops before submitting with the same response, and
65
+ its consult task is recorded as blocked with `followup_approval_required`.
61
66
 
62
67
  Ask the user whether to continue. Only after an explicit user request/approval may
63
68
  the caller send that continuation with `user_approved: true`. This human-directed
@@ -86,6 +91,17 @@ ChatGPT is still generating. Recover that marked request instead of resending it
86
91
  The separate `prodex start` HTTP server runs in the foreground and also needs its
87
92
  own process kept alive; neither command installs a background service.
88
93
 
94
+ Cancelling a `pro_consult` (Esc in the client, or the client's own tool timeout,
95
+ both of which send `notifications/cancelled`) stops it while its prompt has not been
96
+ submitted: nothing is sent, the consult is recorded as blocked with
97
+ `consult_cancelled`, and a follow-up spends none of its budget. After the prompt is
98
+ submitted, cancelling changes nothing on prodex's side: the answer is still waited
99
+ for and saved to the consult's record.
100
+
101
+ One stdio message may be up to 128 MiB, which covers the largest call any tool
102
+ accepts. A larger one ends the server with exit code 1 and a one-line reason on
103
+ stderr, so the client sees the server go away instead of waiting on it.
104
+
89
105
  ## Claude Code
90
106
 
91
107
  See [claude.md](claude.md), or:
package/docs/http-mcp.md CHANGED
@@ -19,6 +19,7 @@ Typical use:
19
19
 
20
20
  The server is personal and local-first. Do not expose it as a shared service.
21
21
  HTTP MCP requests are bounded: malformed JSON returns `400`, and request bodies over 1 MiB are rejected with `413`.
22
+ Sessions are bounded too. A session with no request for 30 minutes and no stream open is closed, and at most 100 sessions are kept: initializing one more closes the least recently used. A request carrying a closed session's `Mcp-Session-Id` gets `404`, as an unknown id does, and MCP clients answer that by initializing a new session.
22
23
 
23
24
  ## Build And Prepare
24
25
 
@@ -193,8 +194,9 @@ The HTTP server exposes the same tool catalog as stdio MCP:
193
194
  Writes are deliberately gated and require a git worktree with a committed HEAD:
194
195
 
195
196
  1. `repo_write_file_dry_run` previews replacing an existing repo-relative text file. It records the diff, git HEAD, preimage hash, and a replacement-text artifact under `.bridge/artifacts/repo-writes/`.
197
+ The file keeps its CRLF line endings, UTF-8 BOM and final newline when the new content leaves them out, and apply keeps its file mode, so an edited script stays executable. A file that is not UTF-8 text (EUC-KR, Latin-1, UTF-16) is refused rather than re-encoded.
196
198
  2. `repo_write_file_apply` applies that exact receipt only if git HEAD, file path, preimage hash, and artifact content still match.
197
- 3. `repo_stage_reviewed_paths` stages only files backed by matching applied write receipts.
199
+ 3. `repo_stage_reviewed_paths` stages only files backed by matching applied write receipts. Their paths reach git literally, so a name such as `pages/[id].tsx` stages that file and nothing it would match as a pattern.
198
200
 
199
201
  There is no direct ungated write tool and no shell execution tool.
200
202
 
package/docs/releasing.md CHANGED
@@ -16,6 +16,145 @@ Release target: `v0.40.13` on `main`. The GitHub Release records the resolved co
16
16
 
17
17
  The release adds a guarded, opt-in transition and more accurate blocker reporting. It does not claim that headless Pro consultation is verified on either deployment target or that authentication can be retained indefinitely.
18
18
 
19
+ ## 0.40.50 Release Record
20
+
21
+ Release commit `759065f` on `main` (cut after PRs #84-#87, last merge `d8333f2`); tag `v0.40.50`, published 2026-11-04. Released on the user's go-ahead as a batch, together with the container swaps:
22
+ - #84: session ids carried the start of the prompt.
23
+ - #85: the model lookup took an open "Edit message" editor for the composer.
24
+ - #86: the tool-state check read a hidden workspace's composer.
25
+ - #87: an MCP follow-up refused before submitting still spent the automatic follow-up budget.
26
+
27
+ **Publishing**
28
+ - `npm run release:verify` passed on the release commit after a build.
29
+ - `publish.yml` run 41746949557 passed. It published to npm with SLSA v1 provenance and created the GitHub Release, which lists the four entries from `CHANGELOG.md`.
30
+
31
+ **Installed**
32
+ - WSL, M3, Mac mini: `prodex --version` reports 0.40.50.
33
+ - The maintainer watchdog checkout was pulled to `759065f` and rebuilt.
34
+ - MacBook M4: not installed. `ssh m4` timed out before the cut and again after the publish, so it stays on 0.40.49 until it can be reached.
35
+ - No host-side `prodex mcp` was found running on WSL, M3 or the Mac mini. The MCP entries on WSL and M3 relay into the browser containers. Those relays reconnected into the new containers when they were swapped, at 00:24:39 UTC on M3 and 00:50:51 UTC on WSL.
36
+
37
+ **Container**
38
+ - `container/redesign-patches` `c7c9f35` ports #87, on top of the ports of #85 (`8ce48b9`) and #86 (`e8e3c68`); #84 was ported in `f2d13e6`.
39
+ - tsc, the full suite (2179 passed, 3 skipped) and `release:verify` pass on it.
40
+ - The agent ran both build-and-swap scripts this time; the user had asked for the swaps.
41
+ - M3 (colima `prodex-check`): image `5760b1464db6`, started 00:24:41 UTC.
42
+ - It came up healthy with the 1024 process limit, using 193.
43
+ - `pro browser check` reported logged in with a composer, and the model selection was verified.
44
+ - `pro browser smoke` answered `PRODEX_PRO_SMOKE_OK` with its request verified.
45
+ - WSL: image `ea56b6ee0f42`, recreated 00:51 UTC.
46
+ - Another session's send held the browser lock about 25 minutes before the swap. The swap script recreates the container only once the lock is free.
47
+ - It came up healthy with the 1024 process limit, using 298, and logged no thread errors.
48
+ - `pro browser check` reported logged in with a composer.
49
+
50
+ **Verified live on the WSL container (#87)**
51
+ - The check was run through `performBrowserConsultForMcp` in a fresh chat with no project, budget 5. It sent two prompts.
52
+ - Root: answered `ALPHA`.
53
+ - A follow-up naming a tool the menu does not offer was refused before anything was typed (`tool_not_offered`).
54
+ - The next follow-up answered `BETA` and reported `followup_budget` used 1 of 5.
55
+ - The ledger holds a single reservation for that conversation, written at 00:52:49 UTC. That is three seconds after the second follow-up's task was created, right before it was submitted. Before #87 the refused follow-up would have been reservation 1, and this one reservation 2.
56
+
57
+ ## 0.40.49 Release Record
58
+
59
+ Release commit `74b9c4d` on `main` (cut after PRs #79-#83, last merge `be947df`, docs `7b6a3bd`); tag `v0.40.49`, published 2026-11-01. Released on the user's go-ahead as a batch: #79 (the page's render error saved as the answer), #80 (blank `--tool`), #81 (`--continue` matching a project by the end of its name) and #83 (projects behind the sidebar's "Show more"). #82 changed a test only and has no changelog entry.
60
+
61
+ **Before the cut**
62
+ - `main` CI on `7b6a3bd` (docs only) failed on macOS Intel: `npm test` ran past the release check's 15-minute budget on a slow runner, with no failing test. On the four runs before it that step took 10.5 to 11.8 minutes. The failed job was rerun and passed.
63
+
64
+ **Publishing**
65
+ - `npm run release:verify` passed on the release commit after a build.
66
+ - `publish.yml` run 41283791768 passed all six platforms, then npm publish with SLSA v1 provenance and the GitHub Release, which lists the four entries from `CHANGELOG.md`.
67
+
68
+ **Installed**
69
+ - WSL, M3, Mac mini: `prodex --version` reports 0.40.49.
70
+ - The maintainer watchdog checkout was pulled to `74b9c4d` and rebuilt.
71
+ - MacBook M4 (`m4`), added to the install targets this release. It had two copies:
72
+ - `~/.local/bin/prodex`, which its Codex MCP entry runs directly, links into `~/.gemini/harness-antigravity/runtime-prodex` (an npm project, 0.40.32). It was updated there with `npm i @youdie006/prodex@0.40.49`.
73
+ - A global copy under `/opt/homebrew` (0.40.0) was updated with `npm i -g --prefix /opt/homebrew`.
74
+ - The link pointed at `dist/cli.js`, which is not executable in the new install, because the package's executable has been `prodex.mjs` since 0.40.18. Run directly, it failed with "permission denied", so Codex could not have started the MCP server again. The link now points at the runtime's `node_modules/.bin/prodex`. Through it, `--version` reports 0.40.49 and `prodex mcp` answers `initialize` as 0.40.49.
75
+ - Running services were not restarted. They keep the old code until their client starts them again:
76
+ - M4: Codex's `prodex mcp`, started before the install.
77
+ - M3: a `prodex mcp` running since 2026-09-15.
78
+ - Mac mini and WSL: none on the host. WSL's MCP entry goes through the browser container.
79
+
80
+ **Verified live on installed 0.40.49 (WSL host browser, scratch repo)**
81
+ - `pro browser projects` listed 25 projects; 0.40.48 listed 9 of the same 25.
82
+ - `pro browser smoke` returned `requestVerified: true`.
83
+
84
+ **After the release**
85
+ - PR #84 (merge `b7b5d61`, unreleased): session ids no longer carry the start of the prompt.
86
+
87
+ **Container**
88
+ - `container/redesign-patches` `f2d13e6` ports #84 on top of the ports of #79-#83 (`f93b63f`, `661b2c6`, `106bd0a`, `facd4fd`) and the 1024 process limit (`55f2f3e`). tsc, the full suite (2161 tests) and `release:verify` pass on it.
89
+ - The agent's permission classifier refused the WSL `docker compose build`, so the user ran the build-and-swap scripts. Both images were built from `f2d13e6`. Both swaps waited for the container's send lock to be free.
90
+ - M3 (colima `prodex-check`): image `4bc8fac19752`, started 07:57 UTC.
91
+ - It came up healthy with the 1024 process limit, using 146.
92
+ - `pro browser check` reported logged in with a composer.
93
+ - `pro browser smoke` answered `PRODEX_PRO_SMOKE_OK` with its request verified.
94
+ - A dry-run `pro ask "Draft the payroll memo"` (nothing sent) got the id `sess_20261101_075935_93598ff4`, with no prompt words in it.
95
+ - WSL: image `e2e3523b640a`.
96
+ - The first run built the image but did not swap, because a Pro consult from another session had held the send lock since 07:54 UTC.
97
+ - The swap waited for that consult, which finished `done`. The lock cleared at 08:06:32, and the container was recreated at 08:06:35.
98
+ - It came up healthy with the 1024 process limit, using 177, and logged no thread errors.
99
+ - `pro browser check`, `smoke` and the dry-run id check gave the same results as on M3 (id `sess_20261101_080804_33e445d8`).
100
+ - The swap dropped every MCP client of the WSL container, so each WSL Claude Code session needs `/mcp`.
101
+ - Both containers' `prodex --version` still prints 0.40.18, because the container branch never bumps its version.
102
+
103
+ **MCP clients kept connected across container swaps (2026-11-01, after the swaps)**
104
+ - Both swaps had dropped every MCP client of their container: `scripts/container-client.mjs mcp` piped the client into `docker exec` and ended with it, so each Claude Code session needed `/mcp` typed by hand.
105
+ - `container/redesign-patches` `964b847` turns that command into a relay that reconnects:
106
+ - it answers requests in flight with an error that names `pro_recover`;
107
+ - it runs `docker exec` again until the container is back, holding new requests for up to 60 seconds;
108
+ - it replays the client's handshake, tells the client its tools may have changed, and then passes the held requests on.
109
+ - Verified:
110
+ - 6 new tests; suite 2167 passed; `release:verify` ok.
111
+ - On a throwaway container from the same image, one client connection listed 20 tools, survived a restart and then a delete-and-recreate, and listed 20 tools again each time.
112
+ - WSL deployment:
113
+ - The prodex MCP entries used to run the script from `/mnt/d/myproject/gptprouse`, a checkout another task keeps on its own branch.
114
+ - Three Claude configs now run it from a checkout pinned for this purpose, `~/.local/share/prodex-maint/container-src` at `964b847`: `~/.claude.json` and the two swapdex slot configs that had the entry.
115
+ - The Codex config (`~/.codex/config.toml`) was changed the same way.
116
+ - `claude mcp get prodex` reports Connected. A handshake through it reached the WSL container (server 0.40.18, 20 tools).
117
+ - M3 deployment:
118
+ - Its Claude and Codex configs run an installed copy, `~/.local/share/prodex/container-installed-8d7a094-wP3RK2/container-client.mjs`.
119
+ - That file was replaced in place with `964b847`'s; the old one is kept beside it as `container-client.mjs.8d7a094.bak`. The configs are unchanged.
120
+ - A handshake through it reached the M3 container.
121
+ - Sessions that were open during the swaps still need one `/mcp`. After that, a container restart or swap no longer disconnects them.
122
+ - When `container-client.mjs` changes again, both copies must be updated:
123
+ - WSL: move the pinned checkout to the new commit;
124
+ - M3: replace the installed copy.
125
+
126
+ ## Container consults ran on the last model selected (2026-11-01)
127
+
128
+ **What happened**
129
+ - The user reported that some sessions' consults did not use Pro but whatever model the browser last had.
130
+ - In the WSL container's results, 20 consults since 2026-10-10 carry `model_selection_warning: no model/effort was selected`. Today's 07:54 UTC Pro consult from another session is one of them.
131
+ - Cause: a consult that names no model, with no saved or environment default, keeps the picker's current selection. That is the designed fallback, but in a shared browser the current selection is whatever another session (or an Instant smoke test) picked last.
132
+ - The container's MCP server had neither default. `container-client.mjs mcp` passed no variables into the container, so an MCP entry's env block could not reach it, and the bridge in the container had no saved config.
133
+ - The host entries on WSL that run `prodex mcp` directly set `PRODEX_DEFAULT_MODEL=Pro` and were not affected.
134
+
135
+ **Fixes**
136
+ - `container/redesign-patches` `cd3b671`: `mcp` passes the client's `PRODEX_DEFAULT_MODEL`, `_EFFORT`, `_PRO_MODE` and `_PROJECT` into the container, and adds `PRODEX_DEFAULT_MODEL=Pro` when none of them names a model or a reasoning step. A consult's own `model` or `effort` still wins.
137
+ - Verified: 3 new tests, suite 2170 passed, `release:verify` ok.
138
+ - In the container, the resolved defaults went from `null` to `{"model":"Pro"}`.
139
+ - A consult that named no model, sent through the new client, came back done.
140
+ - Deployed to the WSL pinned checkout (`~/.local/share/prodex-maint/container-src` at `cd3b671`) and to the M3 installed copy.
141
+ - The 16 WSL sessions had reconnected (17:35-17:37 KST) before `cd3b671` was in place, so their relays still run the version without the default.
142
+ - So the bridge inside each container now saves Pro: `setup --cwd /home/node/bridge --model Pro` on WSL and M3. That writes `.bridge/config.local.json` with a loopback HTTP profile; no HTTP server runs there.
143
+ - Each consult reads this default, so it applies to connected clients at once. A consult through one of the old relays, naming no model, came back done from Pro.
144
+ - Each such consult carries the warning `model_is_now_an_effort`, because the current picker has Pro as an effort step. The model default is kept because a model or effort the consult names itself overrides it. A saved effort default would be applied on top of a model the consult names.
145
+
146
+ **M4 and Mac mini (host `prodex mcp`, 2026-11-03)**
147
+ - Neither machine's Claude or Codex entry set `PRODEX_DEFAULT_MODEL`. On M4, `.bridge/results` held one consult of five with the no-model warning (`~/.gemini/harness-antigravity/verification`).
148
+ - M4:
149
+ - `~/.claude.json` now has `env: {PRODEX_DEFAULT_MODEL: Pro}`, set with `claude mcp remove` and `add-json`.
150
+ - `~/.codex/config.toml` `[mcp_servers.prodex]` now has `env = { PRODEX_DEFAULT_MODEL = "Pro" }`. A backup is kept as `config.toml.bak-prodex-default-20261103`, and `codex mcp get prodex` parses it.
151
+ - The three running `prodex mcp` processes keep their old environment until their clients restart them.
152
+ - Mac mini: the same change, made on the user's explicit request after the first attempt was refused:
153
+ - Claude `~/.claude.json` now carries `env: {PRODEX_DEFAULT_MODEL: Pro}`.
154
+ - Codex `~/.codex/config.toml` has the same `env` line, with the backup `config.toml.bak-prodex-default-20261103`. `codex mcp get prodex` shows the variable set.
155
+ - No `prodex mcp` was running there, so the next client start uses it.
156
+ - A WSL Codex session started on 2026-10-28 still runs the client from the old path until it restarts. Its config, `~/.codex/config.toml` (linked from every swapdex slot), already points at the pinned checkout.
157
+
19
158
  ## Browser Incident 2026-10-30: WSL browser container out of threads
20
159
 
21
160
  **What happened**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youdie006/prodex",
3
- "version": "0.40.49",
3
+ "version": "0.40.51",
4
4
  "description": "Local receipt bus for coordinating Codex execution with ChatGPT Pro/Projects consultation.",
5
5
  "author": "youdie006",
6
6
  "license": "MIT",