cofluxd 0.14.0 → 0.15.0

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/cofluxd.mjs CHANGED
@@ -945,10 +945,14 @@ async function cmdHook() {
945
945
  // 不需要任何凭证:daemon 用调用方 pid 反查进程树确认它属于哪个会话,树外一律拒。
946
946
  // local-first(plan 094):send/read/wait/notify/progress 在 daemon 本地闭环,不经中心;只有
947
947
  // new/list/ports 由 daemon 代问中心(Task 要落库广播、预览 URL 由中心生成)。
948
+ // 跟随 cwd(plan 102):请求都带 process.cwd(),agent 挪进同设备另一个 coflux 工作区后,这些
949
+ // 命令就对那个工作区办事(notify/progress/ports 除外,它们挂在本会话上,与工作区无关)。
948
950
  // 与 `hook` 子命令的约定**相反**:这些命令必须写 stdout——输出就是给 agent 读的返回值。
949
951
  // 也刻意不做自动重试:terminal new 有副作用,重试会开出两个终端,失败就把错误交给 agent。
950
952
 
951
953
  const AGENT_TIMEOUT_MS = 30_000;
954
+ /** 调用方能收窄单次 `/agent` 等待的下限;再低就只够覆盖 node 自己的启动,等于必然超时。 */
955
+ const MIN_AGENT_TIMEOUT_MS = 200;
952
956
  const DEFAULT_READ_LINES = 200;
953
957
  // wait 的循环必须在 CLI 侧:单次 agentPost 有 25 秒的 loopback 应答上限。默认 30 分钟——编码任务
954
958
  // 常跑很久;轮询走 terminal.status(daemon 本地账本直接答,不经中心),3 秒一次对本机 loopback
@@ -956,6 +960,24 @@ const DEFAULT_READ_LINES = 200;
956
960
  const DEFAULT_WAIT_TIMEOUT_S = 1800;
957
961
  const WAIT_POLL_MS = 3000;
958
962
 
963
+ // 每条请求都带调用方 cwd(plan 102):agent 可以经 `/cd` 或 EnterWorktree 把活着的会话挪进同
964
+ // 设备的另一个 coflux 工作区,daemon 据此把本次请求的**目标**解析到 cwd 所在的工作区(会话的
965
+ // 归属工作区不变)。目录被删掉时 process.cwd() 会抛,按"报不出来"处理,daemon 退回归属工作区。
966
+ function callerCwd() {
967
+ try { return process.cwd(); } catch { return ""; }
968
+ }
969
+
970
+ // 调用方可以用 COFLUX_AGENT_TIMEOUT_MS 收窄单次请求的等待上限(plan 104)。默认 30 秒是为
971
+ // agent 定的——它等得起;hook 脚本等不起:宿主按秒杀 hook(SessionStart 只给几秒),而经中心的
972
+ // 动作最坏要等 daemon 的 20 秒中心超时。被宿主杀在半路比拿不到答案坏得多(连坐标块都印不出来),
973
+ // 所以这类调用方自报一个更小的预算,到点干净失败、让脚本走回退。
974
+ // 只允许收窄不允许放宽:上限仍是 AGENT_TIMEOUT_MS,畸形值一律按默认处理。
975
+ function agentTimeoutMs() {
976
+ const raw = Number(process.env.COFLUX_AGENT_TIMEOUT_MS);
977
+ if (!Number.isFinite(raw) || raw <= 0) return AGENT_TIMEOUT_MS;
978
+ return Math.min(Math.max(Math.floor(raw), MIN_AGENT_TIMEOUT_MS), AGENT_TIMEOUT_MS);
979
+ }
980
+
959
981
  async function agentPost(body) {
960
982
  const portResult = localGatewayPort();
961
983
  if (!portResult.ok) die(portResult.error);
@@ -964,8 +986,8 @@ async function agentPost(body) {
964
986
  res = await fetch(`http://127.0.0.1:${portResult.port}/agent`, {
965
987
  method: "POST",
966
988
  headers: { "content-type": "application/json" },
967
- body: JSON.stringify({ ...body, pid: process.pid, ppid: process.ppid }),
968
- signal: AbortSignal.timeout(AGENT_TIMEOUT_MS),
989
+ body: JSON.stringify({ ...body, pid: process.pid, ppid: process.ppid, cwd: callerCwd() }),
990
+ signal: AbortSignal.timeout(agentTimeoutMs()),
969
991
  });
970
992
  } catch (error) {
971
993
  die(`连不上本机 daemon:${error?.message || error}(daemon 没在跑?先看 cofluxd status)`);
@@ -1071,6 +1093,55 @@ async function cmdProgress() {
1071
1093
  console.log("已更新进度(显示在工作区卡片上,被下一条覆盖)");
1072
1094
  }
1073
1095
 
1096
+ // 「我在哪」与「跟着我搬」(plan 102 / 103)。三条都打一行 JSON,字段稳定——插件脚本按它比对,
1097
+ // agent 也直接读。
1098
+ //
1099
+ // cofluxd workspace 只读:cwd 所在的有效工作区 + 本终端的归属工作区
1100
+ // cofluxd workspace locate [path] 把本终端的**归属**搬到 path 所属的工作区(未登记先登记)
1101
+ // cofluxd workspace forget <path> 该 worktree 已被删掉:其下终端搬回主工作区、记录消失
1102
+ //
1103
+ // locate/forget 是插件在 SessionStart / PostToolUse(EnterWorktree|ExitWorktree) / WorktreeRemove
1104
+ // 上调的,同样零凭证(daemon 按进程树认身份)。daemon 旧到不认识这两个动作时它会回
1105
+ // 「未知 action …」,agentPost 原样报错并非零退出——脚本据此静默放弃,不干扰会话。
1106
+ async function cmdWorkspace() {
1107
+ const sub = positionals[1];
1108
+ if (!sub) {
1109
+ const result = await agentPost({ action: "workspace.current" });
1110
+ return void console.log(JSON.stringify({
1111
+ workspaceId: result.workspaceId,
1112
+ path: result.path,
1113
+ owningWorkspaceId: result.owningWorkspaceId,
1114
+ moved: Boolean(result.moved),
1115
+ }));
1116
+ }
1117
+ if (sub === "locate") {
1118
+ // 路径缺省取调用方 cwd;插件脚本一律显式传 hook 载荷里的 cwd(hook 在会话当前目录执行,
1119
+ // 与载荷里的 cwd 未必相同)。
1120
+ const path = positionals[2] || callerCwd();
1121
+ if (!path) die("workspace locate 需要 <path>(取不到当前目录)");
1122
+ const result = await agentPost({ action: "workspace.locate", path });
1123
+ return void console.log(JSON.stringify({
1124
+ workspaceId: result.workspaceId,
1125
+ path: result.path,
1126
+ branch: result.branch,
1127
+ created: Boolean(result.created),
1128
+ moved: Boolean(result.moved),
1129
+ }));
1130
+ }
1131
+ if (sub === "forget") {
1132
+ const path = positionals[2];
1133
+ if (!path) die("workspace forget 需要 <path>(被删掉的 worktree 目录)");
1134
+ const result = await agentPost({ action: "workspace.forget", path });
1135
+ return void console.log(JSON.stringify({
1136
+ workspaceId: result.workspaceId,
1137
+ fallbackWorkspaceId: result.fallbackWorkspaceId,
1138
+ movedTerminals: result.movedTerminals ?? 0,
1139
+ removed: Boolean(result.removed),
1140
+ }));
1141
+ }
1142
+ die(`workspace 的子命令只有 locate | forget(不带子命令 = 报出我在哪)`);
1143
+ }
1144
+
1074
1145
  async function cmdPorts() {
1075
1146
  const { ports } = await agentPost({ action: "ports" });
1076
1147
  if (!ports.length) return void console.log("本工作区暂无监听端口");
@@ -1110,6 +1181,20 @@ const HELP = `cofluxd —— coflux daemon 管理
1110
1181
  cofluxd notify "<一句话>" 叫人:工作区在侧栏转为「等待交互」并显示这句话
1111
1182
  cofluxd progress "<一句话>" 播报进度:显示在工作区卡片上,被下一条覆盖(不打扰用户)
1112
1183
  cofluxd ports 列出本工作区的监听端口及可直接打开的预览 URL
1184
+ cofluxd workspace 一行 JSON 报出「我在哪」:workspaceId(cwd 所在的有效工作区,本地命令
1185
+ 都落在它上面)、path、owningWorkspaceId(本终端此刻归属哪个工作区)、
1186
+ moved。用 /cd 挪进另一个 coflux 工作区后用它确认目标,调 MCP 时也传这个
1187
+ workspaceId
1188
+ cofluxd workspace locate [path]
1189
+ 把本终端的**归属**搬到 path(缺省=当前目录)所属的工作区:进入/离开
1190
+ worktree 后 coflux 跟着走,未登记的同仓库 worktree 先登记出一个子工作区。
1191
+ 插件自动调,一般不用手敲
1192
+ cofluxd workspace forget <path>
1193
+ 该 worktree 已被删掉:其下所有终端搬回项目主工作区、工作区记录消失
1194
+ (不执行 git worktree remove)
1195
+
1196
+ agent 命令的环境变量:COFLUX_AGENT_TIMEOUT_MS 收窄单次请求的等待上限(默认 30000,只能调小),
1197
+ 供有硬超时的 hook 脚本用——到点干净失败,好过被宿主杀在半路。
1113
1198
 
1114
1199
  up flags: --server <ws://.../daemon> --name <名> --shell <路径>
1115
1200
  通用: --version <vX|latest>(不传时 up 沿用已有二进制,update 默认 latest) --bin-dir <dir>(用本地 cargo 产物) --no-start
@@ -1147,7 +1232,7 @@ let cmd = positionals[0];
1147
1232
  if (values.help || cmd === "help") { console.log(HELP); process.exit(0); }
1148
1233
  if (!cmd) cmd = fs.existsSync(SETTINGS) ? "status" : "up"; // 首次裸跑 → 引导
1149
1234
 
1150
- const handlers = { up: cmdUp, update: cmdUpdate, restart: cmdRestart, down: cmdDown, status: cmdStatus, doctor: cmdDoctor, fda: cmdFda, logs: cmdLogs, uninstall: cmdUninstall, hook: cmdHook, terminal: cmdTerminal, notify: cmdNotify, progress: cmdProgress, ports: cmdPorts };
1235
+ const handlers = { up: cmdUp, update: cmdUpdate, restart: cmdRestart, down: cmdDown, status: cmdStatus, doctor: cmdDoctor, fda: cmdFda, logs: cmdLogs, uninstall: cmdUninstall, hook: cmdHook, terminal: cmdTerminal, notify: cmdNotify, progress: cmdProgress, ports: cmdPorts, workspace: cmdWorkspace };
1151
1236
  const h = handlers[cmd];
1152
1237
  if (!h) die(`未知命令: ${cmd}${MIGRATED[cmd] ? `\n${MIGRATED[cmd]}` : ""}\n\n${HELP}`);
1153
1238
  await h(values);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cofluxd",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "coflux daemon 管理 CLI:装/起/停/升级 Rust daemon(supervisor + worker)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: coflux
3
- description: When you run inside a coflux terminal, this skill documents the local cofluxd commands that open terminals the user can watch and take over from the coflux web/mobile app, report progress, call the user and hand out preview URLs, plus the center MCP for reaching other workspaces and devices. Your coordinates (device / project / workspace / terminal) arrive in a <coflux-session> block at session start, or via the COFLUX_* environment variables. Inside your own workspace always use the zero-credential local cofluxd commands (open, read, wait, send, report progress, call the user, get preview URLs); use the center's coflux MCP only to leave this workspace (child workspaces, other workspaces or devices). Use when the user should be able to watch, step into or stop a command (interactive steps, dev servers, a job they are waiting on), when the user has to decide something, when you want to hand the user a clickable preview URL, or when you need an isolated child workspace for parallel work.
3
+ description: When you run inside a coflux terminal, this skill documents the local cofluxd commands that open terminals the user can watch and take over from the coflux web/mobile app, report progress, call the user and hand out preview URLs, plus the center MCP for reaching other workspaces and devices. Your coordinates (device / project / workspace / terminal) arrive in a <coflux-session> block at session start, or via the COFLUX_* environment variables. For the workspace your cwd is in always use the zero-credential local cofluxd commands (open, read, wait, send, report progress, call the user, get preview URLs); use the center's coflux MCP only to reach beyond it (child workspaces, other workspaces or devices). Use when the user should be able to watch, step into or stop a command (interactive steps, dev servers, a job they are waiting on), when the user has to decide something, when you want to hand the user a clickable preview URL, or when you need an isolated child workspace for parallel work.
4
4
  ---
5
5
 
6
6
  # Working inside coflux
@@ -10,12 +10,12 @@ machines from a browser or a phone and take over at any time. This skill gives y
10
10
  user can see and take over, a progress line and a call button on the workspace card, preview URLs,
11
11
  and a way to operate the other workspaces and devices under the account when you need to.
12
12
 
13
- Two tracks, one rule: **whatever closes locally uses local commands; only leaving this
14
- workspace goes through MCP.**
13
+ Two tracks, one rule: **whatever closes locally uses local commands; only reaching beyond the
14
+ workspace you are in goes through MCP.**
15
15
 
16
16
  | Track | Credentials | Reach | Use for |
17
17
  |---|---|---|---|
18
- | Local commands `cofluxd terminal/progress/notify/ports` | none (the daemon identifies you by process tree) | **the workspace you are in** | open, read, wait, send, report progress, call the user, preview URLs: the default, fastest, no network dependency |
18
+ | Local commands `cofluxd terminal/progress/notify/ports` | none (the daemon identifies you by process tree) | **the workspace your cwd is in** | open, read, wait, send, report progress, call the user, preview URLs: the default, fastest, no network dependency |
19
19
  | Center MCP `coflux` | one OAuth authorization by the user in the host | **the whole account**: every device, project, workspace and terminal | child workspaces (git worktrees), cross-workspace / cross-device access, joining from outside coflux |
20
20
 
21
21
  Of the local commands, `send`/`read`/`wait`/`notify`/`progress` complete entirely inside the
@@ -42,7 +42,7 @@ env | grep '^COFLUX_'
42
42
  |---|---|
43
43
  | `COFLUX_DEVICE_ID` | id of the device you run on (the id in `list_devices`) |
44
44
  | `COFLUX_PROJECT_ID` | owning project id; empty string for a directory workspace without a repository |
45
- | `COFLUX_WORKSPACE_ID` | owning workspace id (the id in `list_workspaces`) |
45
+ | `COFLUX_WORKSPACE_ID` | the workspace this terminal was **opened** in (the id in `list_workspaces`). The variable is frozen when the terminal starts; the workspace the terminal *belongs to* can still change — see below. `cofluxd workspace` is the authority |
46
46
  | `COFLUX_TASK_ID` | id of this terminal (the taskId / terminalId used by local commands and `read_terminal`) |
47
47
  | `COFLUX_SESSION_ID` | id of this PTY session |
48
48
  | `COFLUX_MCP_URL` | the center's MCP URL; the user configures MCP with it |
@@ -53,6 +53,73 @@ env | grep '^COFLUX_'
53
53
  this machine's daemon has not been upgraded: tell the user to run `cofluxd update && cofluxd restart`;
54
54
  after reopening the terminal the variables and the local commands are there.
55
55
 
56
+ ### Two workspaces to keep apart: owning and effective
57
+
58
+ - **Owning workspace** = the workspace this terminal **belongs to**: what the user's sidebar shows it
59
+ under, what its turn state, branch and diff stats are attributed to. It starts out as
60
+ `COFLUX_WORKSPACE_ID` and moves with you when you enter or leave a git worktree (below).
61
+ - **Effective workspace** = the workspace **your current working directory is inside**. This is what
62
+ every local command acts on.
63
+
64
+ They are the same until your cwd wanders off. A plain `cd <path>` moves a *live* session — same
65
+ conversation, no restart — and a coflux child workspace is a normal registered git worktree, so a
66
+ session whose terminal belongs to workspace A can end up working inside workspace B. From that
67
+ moment, in B:
68
+
69
+ - `cofluxd terminal new` opens the terminal **in B**, under B in the user's sidebar, running in B's
70
+ directory, counting against B's terminal cap;
71
+ - `cofluxd terminal list` lists B's terminals, and A's terminals answer `read` / `wait` / `send`
72
+ with "not in this workspace or does not exist" (`cd` back to A to reach them again);
73
+ - MCP calls need **B's** id as `workspaceId`;
74
+ - the terminal itself stays under A, and `progress`, `notify` and `ports` still belong to it,
75
+ whatever your cwd is; `COFLUX_TASK_ID` and `COFLUX_SESSION_ID` never change.
76
+
77
+ If your cwd is outside every coflux workspace (say `/tmp`), local commands fall back to the owning
78
+ workspace.
79
+
80
+ A terminal opened before the daemon was upgraded is the one case with no owning workspace at all:
81
+ its local commands are refused with "predates the daemon upgrade" whatever your cwd is, because the
82
+ daemon never guesses ownership from a directory. Open a new terminal.
83
+
84
+ ### coflux follows you into a git worktree
85
+
86
+ `EnterWorktree` switches this live session into a git worktree (its own, or an existing one you point
87
+ it at), `ExitWorktree` switches back, and resuming a session that had entered one puts you straight
88
+ back in it. **coflux comes along**: the terminal's *owning* workspace moves to the workspace that
89
+ worktree is, and if coflux has never seen that worktree it registers it as a child workspace of this
90
+ project first — a new card appears in the user's sidebar, with its branch and diff stats. Nothing is
91
+ interrupted: same terminal, same PTY, same conversation, and the user keeps watching it where it now
92
+ lives. When Claude Code cleans up its own worktree on exit, that workspace's terminals move back to
93
+ the project's main workspace and the record disappears by itself.
94
+
95
+ So, after entering or leaving a worktree, owning **and** effective are both the new workspace: pass
96
+ its id to MCP tools and everything local already acts on it. The plugin drops the new id next to the
97
+ tool result, and `cofluxd workspace` always tells you. Two things stay behind on purpose:
98
+
99
+ - `COFLUX_WORKSPACE_ID` (and the id in the `<coflux-session>` block from earlier in this session)
100
+ still names where the terminal was *opened*; it is frozen when the PTY starts and cannot be
101
+ rewritten. Never reuse it after a move.
102
+ - The shell inside this terminal keeps its own directory. That is only about the shell; it does not
103
+ affect where your work is attributed.
104
+
105
+ Nothing happens when coflux cannot follow, and nothing is blocked either: another repository, a
106
+ directory that is not a git repository, a terminal opened in a directory workspace (no project), or
107
+ a daemon that is down or too old — the session just carries on with the ownership it had.
108
+
109
+ ### Ask where you are
110
+
111
+ ```sh
112
+ cofluxd workspace
113
+ {"workspaceId":"ws-b","path":"/Users/me/.coflux/worktrees/ws-b","owningWorkspaceId":"ws-a","moved":true}
114
+ ```
115
+
116
+ One line of JSON: `workspaceId` (+ `path`) is the **effective** workspace, `owningWorkspaceId` is the
117
+ workspace this terminal belongs to right now, and `moved` says whether they differ. With the plugin
118
+ installed you also get a `<coflux-session-moved>` block at the start of every prompt while the two
119
+ differ — but that block only arrives with the **next** user prompt. **About to call an MCP tool right
120
+ after a `cd`? Run `cofluxd workspace` first** and use the `workspaceId` it prints; do not reuse
121
+ `COFLUX_WORKSPACE_ID`.
122
+
56
123
  ## When to open a terminal
57
124
 
58
125
  A coflux terminal is a process the user can see: a titled entry in their sidebar that they can
@@ -81,7 +148,8 @@ cofluxd terminal new --title="Debug shell" # ses
81
148
  ```
82
149
 
83
150
  `--title` is the name the user sees in the sidebar; **name it properly**: "Run unit tests",
84
- "Start dev server", never "terminal 1". Either kind runs in the current workspace directory.
151
+ "Start dev server", never "terminal 1". Either kind runs in the directory of the workspace your cwd
152
+ is in (which is not always the one this terminal was opened in — see "owning and effective").
85
153
 
86
154
  Always write `--cmd=<value>` and `--title=<value>` with the `=`, never separated by a space: a
87
155
  value that starts with `-` is otherwise taken for another option and the call fails outright.
@@ -121,7 +189,7 @@ workspace as you).
121
189
  ### See how far it got
122
190
 
123
191
  ```sh
124
- cofluxd terminal list # every terminal in this workspace: id, state, exit code, title
192
+ cofluxd terminal list # every terminal in the workspace your cwd is in: id, state, exit code, title
125
193
  cofluxd terminal read <taskId> # a terminal's content (plain text, last 200 lines by default)
126
194
  cofluxd terminal read <taskId> --lines 50
127
195
  ```
@@ -223,16 +291,17 @@ server, use it to get the URL and tell the user directly; they click it and nobo
223
291
 
224
292
  Errors are one readable sentence; do what they say: "not inside a coflux terminal" = you are not
225
293
  in a coflux session; "terminal is not in this workspace or does not exist" = check the id with
226
- `list`; "predates the daemon upgrade" = that terminal was opened before the daemon upgrade, open a
227
- new one; a `new` without `--cmd` refused for a missing command = this machine's daemon is older
294
+ `list`, and if you moved into another workspace that is exactly what a terminal of the other one
295
+ looks like (`cofluxd workspace` to confirm, `cd` back to reach it); "predates the daemon upgrade" =
296
+ that terminal was opened before the daemon upgrade, open a new one; a `new` without `--cmd` refused for a missing command = this machine's daemon is older
228
297
  than session terminals, tell the user to run `cofluxd update && cofluxd restart` (or pass a command
229
298
  and use a job terminal); "daemon is not connected to the center" only appears on
230
299
  `new`/`list`/`ports`, retry once it reconnects.
231
300
 
232
301
  ## Center MCP: leaving this workspace
233
302
 
234
- Local commands only see the workspace you are in. Use the MCP server named `coflux` in the host
235
- **only** for these:
303
+ Local commands only see the workspace your cwd is in. Use the MCP server named `coflux` in the
304
+ host **only** for these:
236
305
 
237
306
  - **Open an isolated child workspace to work in parallel**: `create_workspace` (project id from
238
307
  `$COFLUX_PROJECT_ID`) really runs `git worktree add` on the device; then `create_terminal` runs
@@ -242,11 +311,14 @@ Local commands only see the workspace you are in. Use the MCP server named `cofl
242
311
  `read_terminal` / `send_terminal_input`.
243
312
  - **Join everything under the account when you are not inside a coflux terminal** (for example
244
313
  Claude Code the user started on their own machine).
245
- - **Never run `git worktree add` yourself**: inside a coflux project the plugin blocks
246
- `git worktree add|remove|move` and points you to `create_workspace` / `remove_workspace`; a
247
- worktree you create is invisible to the user and cannot host a terminal.
248
-
249
- Do not detour through MCP for work inside this workspace: that is an extra round trip to the
314
+ - **Deleting a workspace**: `remove_workspace` (it closes that workspace's terminals first, then
315
+ removes the worktree and the record). Inside a coflux project the plugin blocks
316
+ `git worktree remove|move` run by hand, because that leaves an orphan workspace record in the user's
317
+ sidebar. Creating a worktree is *not* blocked — coflux follows you into it (see above) — and Claude
318
+ Code's own worktrees need no cleanup from you at all.
319
+
320
+ Do not detour through MCP for work inside the workspace you are in — including one you moved into
321
+ with `cd` or EnterWorktree, where the local commands follow you: that is an extra round trip to the
250
322
  center, while a local command does it in one step.
251
323
 
252
324
  ### When MCP is not configured
@@ -268,8 +340,9 @@ it is set up, keep doing the work inside this workspace with local commands.
268
340
 
269
341
  The tool list and each tool's contract (parameters, limits, what an error means) come from the
270
342
  MCP server itself: read the tool descriptions in the host, they are the source of truth and this
271
- file does not repeat them. Take ids from the `COFLUX_*` variables first; for anything outside this
272
- workspace, find ids with the `list_*` tools.
343
+ file does not repeat them. Take ids from the `COFLUX_*` variables first except the workspace id
344
+ after you moved, which comes from `cofluxd workspace` (or the `<coflux-session-moved>` block); for
345
+ anything outside the workspace you are in, find ids with the `list_*` tools.
273
346
 
274
347
  The local-command disciplines apply to MCP just the same: `read_terminal` before
275
348
  `send_terminal_input`, stop when refused because the user is taking over (communicate with
@@ -282,8 +355,8 @@ The local-command disciplines apply to MCP just the same: `read_terminal` before
282
355
  - You can open, read, wait and type, but **typing is a restricted write with humans first**: you
283
356
  cannot write into a terminal the user is taking over (you are refused explicitly), and the user
284
357
  taking over at any time displaces you. Do not fight a human for a terminal.
285
- - Local commands only see **the workspace you are in**; other workspaces and other machines go
286
- through MCP and are limited to the same account.
358
+ - Local commands only see **the workspace your cwd is in** (`cofluxd workspace` says which one);
359
+ other workspaces and other machines go through MCP and are limited to the same account.
287
360
  - A workspace has a cap on concurrently live terminals (default 8, including the user's own).
288
361
  On hitting the cap, `list` first: usually some finished terminals were never collected. If the
289
362
  user really filled it up, `notify` them instead of forcing it.
@@ -291,4 +364,7 @@ The local-command disciplines apply to MCP just the same: `read_terminal` before
291
364
  user see" is their whole point. `send`/`read`/`wait`/`notify`/`progress` do not depend on the
292
365
  center. When disconnected they fail loudly rather than degrade silently.
293
366
  - `COFLUX_*` variables exist only in PTYs opened by coflux; exporting or changing them yourself
294
- has no effect, the center only trusts the ids it issued.
367
+ has no effect, the center only trusts the ids it issued. `COFLUX_WORKSPACE_ID` always means the
368
+ workspace this terminal was **opened** in and goes stale the moment coflux follows you into a
369
+ worktree; both "where does this terminal belong now" and "where am I acting" come from
370
+ `cofluxd workspace`.