@cluesmith/codev 3.3.2 → 3.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/dashboard-dist/assets/{index-D3XaKpZW.js → index-DZf6pNPE.js} +41 -41
  2. package/dashboard-dist/assets/index-DZf6pNPE.js.map +1 -0
  3. package/dashboard-dist/assets/index-DnYLy5C_.css +32 -0
  4. package/dashboard-dist/index.html +2 -2
  5. package/dist/agent-farm/cli.d.ts.map +1 -1
  6. package/dist/agent-farm/cli.js +48 -0
  7. package/dist/agent-farm/cli.js.map +1 -1
  8. package/dist/agent-farm/commands/ide.d.ts +20 -0
  9. package/dist/agent-farm/commands/ide.d.ts.map +1 -0
  10. package/dist/agent-farm/commands/ide.js +138 -0
  11. package/dist/agent-farm/commands/ide.js.map +1 -0
  12. package/dist/agent-farm/commands/inbox.d.ts.map +1 -1
  13. package/dist/agent-farm/commands/inbox.js +79 -10
  14. package/dist/agent-farm/commands/inbox.js.map +1 -1
  15. package/dist/agent-farm/commands/interrupt.d.ts +4 -1
  16. package/dist/agent-farm/commands/interrupt.d.ts.map +1 -1
  17. package/dist/agent-farm/commands/interrupt.js +6 -3
  18. package/dist/agent-farm/commands/interrupt.js.map +1 -1
  19. package/dist/agent-farm/commands/reset.d.ts +4 -1
  20. package/dist/agent-farm/commands/reset.d.ts.map +1 -1
  21. package/dist/agent-farm/commands/reset.js +6 -3
  22. package/dist/agent-farm/commands/reset.js.map +1 -1
  23. package/dist/agent-farm/commands/send.d.ts +28 -0
  24. package/dist/agent-farm/commands/send.d.ts.map +1 -1
  25. package/dist/agent-farm/commands/send.js +79 -6
  26. package/dist/agent-farm/commands/send.js.map +1 -1
  27. package/dist/agent-farm/commands/spawn-worktree.d.ts.map +1 -1
  28. package/dist/agent-farm/commands/spawn-worktree.js +16 -7
  29. package/dist/agent-farm/commands/spawn-worktree.js.map +1 -1
  30. package/dist/agent-farm/commands/tower.d.ts +21 -9
  31. package/dist/agent-farm/commands/tower.d.ts.map +1 -1
  32. package/dist/agent-farm/commands/tower.js +90 -30
  33. package/dist/agent-farm/commands/tower.js.map +1 -1
  34. package/dist/agent-farm/db/index.d.ts +3 -1
  35. package/dist/agent-farm/db/index.d.ts.map +1 -1
  36. package/dist/agent-farm/db/index.js +8 -443
  37. package/dist/agent-farm/db/index.js.map +1 -1
  38. package/dist/agent-farm/db/mailbox.d.ts +47 -7
  39. package/dist/agent-farm/db/mailbox.d.ts.map +1 -1
  40. package/dist/agent-farm/db/mailbox.js +56 -12
  41. package/dist/agent-farm/db/mailbox.js.map +1 -1
  42. package/dist/agent-farm/db/migrations.d.ts +57 -0
  43. package/dist/agent-farm/db/migrations.d.ts.map +1 -0
  44. package/dist/agent-farm/db/migrations.js +530 -0
  45. package/dist/agent-farm/db/migrations.js.map +1 -0
  46. package/dist/agent-farm/db/schema.d.ts +1 -1
  47. package/dist/agent-farm/db/schema.d.ts.map +1 -1
  48. package/dist/agent-farm/db/schema.js +1 -0
  49. package/dist/agent-farm/db/schema.js.map +1 -1
  50. package/dist/agent-farm/db/tower-owner.d.ts +168 -0
  51. package/dist/agent-farm/db/tower-owner.d.ts.map +1 -0
  52. package/dist/agent-farm/db/tower-owner.js +369 -0
  53. package/dist/agent-farm/db/tower-owner.js.map +1 -0
  54. package/dist/agent-farm/db/types.d.ts +47 -0
  55. package/dist/agent-farm/db/types.d.ts.map +1 -1
  56. package/dist/agent-farm/db/types.js.map +1 -1
  57. package/dist/agent-farm/lib/ide-record.d.ts +65 -0
  58. package/dist/agent-farm/lib/ide-record.d.ts.map +1 -0
  59. package/dist/agent-farm/lib/ide-record.js +118 -0
  60. package/dist/agent-farm/lib/ide-record.js.map +1 -0
  61. package/dist/agent-farm/lib/tunnel-client.d.ts +22 -0
  62. package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -1
  63. package/dist/agent-farm/lib/tunnel-client.js +96 -19
  64. package/dist/agent-farm/lib/tunnel-client.js.map +1 -1
  65. package/dist/agent-farm/servers/cron-delivery.d.ts +7 -1
  66. package/dist/agent-farm/servers/cron-delivery.d.ts.map +1 -1
  67. package/dist/agent-farm/servers/cron-delivery.js +2 -1
  68. package/dist/agent-farm/servers/cron-delivery.js.map +1 -1
  69. package/dist/agent-farm/servers/gate-profiles.d.ts +44 -2
  70. package/dist/agent-farm/servers/gate-profiles.d.ts.map +1 -1
  71. package/dist/agent-farm/servers/gate-profiles.js +79 -6
  72. package/dist/agent-farm/servers/gate-profiles.js.map +1 -1
  73. package/dist/agent-farm/servers/ide-forward.d.ts +62 -0
  74. package/dist/agent-farm/servers/ide-forward.d.ts.map +1 -0
  75. package/dist/agent-farm/servers/ide-forward.js +274 -0
  76. package/dist/agent-farm/servers/ide-forward.js.map +1 -0
  77. package/dist/agent-farm/servers/ide-server.d.ts +68 -0
  78. package/dist/agent-farm/servers/ide-server.d.ts.map +1 -0
  79. package/dist/agent-farm/servers/ide-server.js +281 -0
  80. package/dist/agent-farm/servers/ide-server.js.map +1 -0
  81. package/dist/agent-farm/servers/mailbox-delivery.d.ts +296 -12
  82. package/dist/agent-farm/servers/mailbox-delivery.d.ts.map +1 -1
  83. package/dist/agent-farm/servers/mailbox-delivery.js +566 -69
  84. package/dist/agent-farm/servers/mailbox-delivery.js.map +1 -1
  85. package/dist/agent-farm/servers/mailbox-wiring.d.ts +66 -19
  86. package/dist/agent-farm/servers/mailbox-wiring.d.ts.map +1 -1
  87. package/dist/agent-farm/servers/mailbox-wiring.js +204 -37
  88. package/dist/agent-farm/servers/mailbox-wiring.js.map +1 -1
  89. package/dist/agent-farm/servers/message-write.d.ts +138 -11
  90. package/dist/agent-farm/servers/message-write.d.ts.map +1 -1
  91. package/dist/agent-farm/servers/message-write.js +144 -35
  92. package/dist/agent-farm/servers/message-write.js.map +1 -1
  93. package/dist/agent-farm/servers/overview.d.ts +28 -12
  94. package/dist/agent-farm/servers/overview.d.ts.map +1 -1
  95. package/dist/agent-farm/servers/overview.js +130 -79
  96. package/dist/agent-farm/servers/overview.js.map +1 -1
  97. package/dist/agent-farm/servers/render-gate.d.ts +77 -1
  98. package/dist/agent-farm/servers/render-gate.d.ts.map +1 -1
  99. package/dist/agent-farm/servers/render-gate.js +174 -25
  100. package/dist/agent-farm/servers/render-gate.js.map +1 -1
  101. package/dist/agent-farm/servers/session-submit.d.ts +7 -4
  102. package/dist/agent-farm/servers/session-submit.d.ts.map +1 -1
  103. package/dist/agent-farm/servers/session-submit.js +7 -4
  104. package/dist/agent-farm/servers/session-submit.js.map +1 -1
  105. package/dist/agent-farm/servers/tower-cron.d.ts.map +1 -1
  106. package/dist/agent-farm/servers/tower-cron.js +7 -3
  107. package/dist/agent-farm/servers/tower-cron.js.map +1 -1
  108. package/dist/agent-farm/servers/tower-instances.d.ts.map +1 -1
  109. package/dist/agent-farm/servers/tower-instances.js +17 -10
  110. package/dist/agent-farm/servers/tower-instances.js.map +1 -1
  111. package/dist/agent-farm/servers/tower-routes.d.ts +32 -0
  112. package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -1
  113. package/dist/agent-farm/servers/tower-routes.js +186 -26
  114. package/dist/agent-farm/servers/tower-routes.js.map +1 -1
  115. package/dist/agent-farm/servers/tower-server.js +79 -9
  116. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  117. package/dist/agent-farm/servers/tower-terminals.d.ts +21 -0
  118. package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -1
  119. package/dist/agent-farm/servers/tower-terminals.js +106 -13
  120. package/dist/agent-farm/servers/tower-terminals.js.map +1 -1
  121. package/dist/agent-farm/servers/tower-utils.d.ts +35 -0
  122. package/dist/agent-farm/servers/tower-utils.d.ts.map +1 -1
  123. package/dist/agent-farm/servers/tower-utils.js +39 -0
  124. package/dist/agent-farm/servers/tower-utils.js.map +1 -1
  125. package/dist/agent-farm/servers/tower-websocket.d.ts.map +1 -1
  126. package/dist/agent-farm/servers/tower-websocket.js +26 -2
  127. package/dist/agent-farm/servers/tower-websocket.js.map +1 -1
  128. package/dist/agent-farm/utils/message-format.d.ts +78 -2
  129. package/dist/agent-farm/utils/message-format.d.ts.map +1 -1
  130. package/dist/agent-farm/utils/message-format.js +107 -11
  131. package/dist/agent-farm/utils/message-format.js.map +1 -1
  132. package/dist/agent-farm/utils/port.d.ts +22 -0
  133. package/dist/agent-farm/utils/port.d.ts.map +1 -0
  134. package/dist/agent-farm/utils/port.js +35 -0
  135. package/dist/agent-farm/utils/port.js.map +1 -0
  136. package/dist/agent-farm/utils/server-utils.d.ts +26 -0
  137. package/dist/agent-farm/utils/server-utils.d.ts.map +1 -1
  138. package/dist/agent-farm/utils/server-utils.js +36 -0
  139. package/dist/agent-farm/utils/server-utils.js.map +1 -1
  140. package/dist/cli.d.ts.map +1 -1
  141. package/dist/cli.js +3 -1
  142. package/dist/cli.js.map +1 -1
  143. package/dist/commands/consult/index.d.ts.map +1 -1
  144. package/dist/commands/consult/index.js +61 -1
  145. package/dist/commands/consult/index.js.map +1 -1
  146. package/dist/commands/consult/tree-tripwire.d.ts +177 -0
  147. package/dist/commands/consult/tree-tripwire.d.ts.map +1 -0
  148. package/dist/commands/consult/tree-tripwire.js +356 -0
  149. package/dist/commands/consult/tree-tripwire.js.map +1 -0
  150. package/dist/commands/doctor.d.ts +69 -0
  151. package/dist/commands/doctor.d.ts.map +1 -1
  152. package/dist/commands/doctor.js +165 -2
  153. package/dist/commands/doctor.js.map +1 -1
  154. package/dist/commands/generate-image.d.ts +25 -1
  155. package/dist/commands/generate-image.d.ts.map +1 -1
  156. package/dist/commands/generate-image.js +410 -2
  157. package/dist/commands/generate-image.js.map +1 -1
  158. package/dist/commands/porch/prompts.d.ts.map +1 -1
  159. package/dist/commands/porch/prompts.js +18 -2
  160. package/dist/commands/porch/prompts.js.map +1 -1
  161. package/dist/lib/agent-env.d.ts +86 -0
  162. package/dist/lib/agent-env.d.ts.map +1 -0
  163. package/dist/lib/agent-env.js +126 -0
  164. package/dist/lib/agent-env.js.map +1 -0
  165. package/dist/lib/forge-contracts.d.ts +76 -5
  166. package/dist/lib/forge-contracts.d.ts.map +1 -1
  167. package/dist/lib/forge-contracts.js.map +1 -1
  168. package/dist/lib/forge-rate-limit.d.ts +45 -0
  169. package/dist/lib/forge-rate-limit.d.ts.map +1 -0
  170. package/dist/lib/forge-rate-limit.js +89 -0
  171. package/dist/lib/forge-rate-limit.js.map +1 -0
  172. package/dist/lib/forge.d.ts +29 -1
  173. package/dist/lib/forge.d.ts.map +1 -1
  174. package/dist/lib/forge.js +96 -21
  175. package/dist/lib/forge.js.map +1 -1
  176. package/dist/lib/github.d.ts +13 -1
  177. package/dist/lib/github.d.ts.map +1 -1
  178. package/dist/lib/github.js +58 -15
  179. package/dist/lib/github.js.map +1 -1
  180. package/dist/lib/test-env.d.ts +17 -0
  181. package/dist/lib/test-env.d.ts.map +1 -1
  182. package/dist/lib/test-env.js +20 -0
  183. package/dist/lib/test-env.js.map +1 -1
  184. package/dist/terminal/pty-manager.d.ts +10 -1
  185. package/dist/terminal/pty-manager.d.ts.map +1 -1
  186. package/dist/terminal/pty-manager.js +38 -9
  187. package/dist/terminal/pty-manager.js.map +1 -1
  188. package/dist/terminal/pty-session.d.ts +174 -8
  189. package/dist/terminal/pty-session.d.ts.map +1 -1
  190. package/dist/terminal/pty-session.js +299 -25
  191. package/dist/terminal/pty-session.js.map +1 -1
  192. package/dist/terminal/session-manager.d.ts +23 -2
  193. package/dist/terminal/session-manager.d.ts.map +1 -1
  194. package/dist/terminal/session-manager.js +115 -36
  195. package/dist/terminal/session-manager.js.map +1 -1
  196. package/dist/terminal/session-screen.d.ts +34 -5
  197. package/dist/terminal/session-screen.d.ts.map +1 -1
  198. package/dist/terminal/session-screen.js +52 -6
  199. package/dist/terminal/session-screen.js.map +1 -1
  200. package/dist/terminal/shellper-client.d.ts +73 -0
  201. package/dist/terminal/shellper-client.d.ts.map +1 -1
  202. package/dist/terminal/shellper-client.js +151 -0
  203. package/dist/terminal/shellper-client.js.map +1 -1
  204. package/dist/terminal/shellper-main.js +20 -3
  205. package/dist/terminal/shellper-main.js.map +1 -1
  206. package/dist/terminal/shellper-process.d.ts +2 -0
  207. package/dist/terminal/shellper-process.d.ts.map +1 -1
  208. package/dist/terminal/shellper-process.js +17 -1
  209. package/dist/terminal/shellper-process.js.map +1 -1
  210. package/dist/terminal/shellper-protocol.d.ts +28 -0
  211. package/dist/terminal/shellper-protocol.d.ts.map +1 -1
  212. package/dist/terminal/shellper-protocol.js.map +1 -1
  213. package/dist/terminal/terminal-replies.d.ts +102 -0
  214. package/dist/terminal/terminal-replies.d.ts.map +1 -0
  215. package/dist/terminal/terminal-replies.js +181 -0
  216. package/dist/terminal/terminal-replies.js.map +1 -0
  217. package/package.json +4 -4
  218. package/scripts/forge/gitea/_lib.sh +214 -0
  219. package/scripts/forge/gitea/issue-comment.sh +8 -1
  220. package/scripts/forge/gitea/issue-view.sh +95 -4
  221. package/scripts/forge/gitea/pr-create.sh +155 -0
  222. package/scripts/forge/gitea/pr-exists.sh +32 -4
  223. package/scripts/forge/gitea/pr-list.sh +40 -31
  224. package/scripts/forge/gitea/pr-view.sh +65 -4
  225. package/scripts/forge/gitea/recently-merged.sh +91 -21
  226. package/scripts/forge/gitea/user-identity.sh +30 -1
  227. package/scripts/forge/github/issue-view.sh +5 -2
  228. package/scripts/forge/github/pr-create.sh +47 -0
  229. package/scripts/forge/github/pr-list.sh +6 -2
  230. package/scripts/forge/github/pr-search.sh +6 -2
  231. package/scripts/forge/gitlab/issue-search.sh +1 -0
  232. package/scripts/forge/gitlab/issue-view.sh +24 -4
  233. package/scripts/forge/gitlab/pr-create.sh +53 -0
  234. package/scripts/forge/gitlab/pr-search.sh +37 -2
  235. package/scripts/forge/linear/auth-status.sh +1 -0
  236. package/scripts/forge/linear/issue-comment.sh +1 -0
  237. package/scripts/forge/linear/issue-list.sh +1 -0
  238. package/scripts/forge/linear/issue-search.sh +1 -0
  239. package/scripts/forge/linear/issue-view.sh +14 -2
  240. package/scripts/forge/linear/recently-closed.sh +1 -0
  241. package/scripts/forge/linear/user-identity.sh +1 -0
  242. package/skeleton/.claude/skills/generate-image/SKILL.md +39 -1
  243. package/skeleton/.codex/skills/generate-image/SKILL.md +39 -1
  244. package/skeleton/protocols/air/prompts/pr.md +6 -1
  245. package/skeleton/protocols/aspir/prompts/review.md +6 -1
  246. package/skeleton/protocols/bugfix/prompts/pr.md +6 -1
  247. package/skeleton/protocols/bugfix/protocol.md +2 -1
  248. package/skeleton/protocols/maintain/prompts/review.md +6 -1
  249. package/skeleton/protocols/pir/prompts/review.md +10 -7
  250. package/skeleton/protocols/spir/prompts/review.md +6 -1
  251. package/skeleton/resources/commands/agent-farm.md +28 -3
  252. package/skeleton/roles/consultant.md +32 -0
  253. package/skeleton/templates/AGENTS.md +1 -1
  254. package/skeleton/templates/CLAUDE.md +1 -1
  255. package/templates/tower.html +16 -1
  256. package/dashboard-dist/assets/index-BZRTEMtH.css +0 -32
  257. package/dashboard-dist/assets/index-D3XaKpZW.js.map +0 -1
@@ -31,3 +31,35 @@ You think alongside the other agents, helping them see blind spots. You have fil
31
31
  - **ALWAYS read files directly from disk** when reviewing specs, plans, or code. File paths are provided in the query — open and read them.
32
32
  - **NEVER rely on `git diff` or `git log -p` as your primary review source.** Diffs are lossy, get truncated, and miss uncommitted work. Read the actual files instead.
33
33
  - If you need to understand what changed, read the full file first, then optionally use `git diff` as a secondary reference.
34
+
35
+ ## You Read the Tree; You Do Not Change It
36
+
37
+ Your filesystem access is for **reading**. The tree you are reviewing belongs to the builder
38
+ who is working in it — usually right now, in a live session. It is not yours to edit.
39
+
40
+ - **Never modify, revert, create, or delete a file in the tree under review.** Not to try a
41
+ fix, not to check a hypothesis, not temporarily, not even if you intend to restore it.
42
+ - **Never run a command that changes state**: `git checkout`/`reset`/`stash`/`commit`, package
43
+ installs, code formatters, migrations, anything that writes. Read-only commands are fine —
44
+ `git log`, `git diff`, `git show`, `git status`, `ls`, `grep`. `git fetch` is not: it writes
45
+ to the repository, and the review is of what is in front of you, not of a newer remote.
46
+ - **A restore afterwards does not make it safe.** Reviews run in parallel, so a second reviewer
47
+ can read your mutation and review code nobody wrote; and if you crash, hit your turn limit,
48
+ or simply lose track, the builder ships whatever you left behind. Silent, and hard to trace
49
+ back to you.
50
+ - **This holds even when the review prompt invites it.** If a prompt asks you to edit — to
51
+ strip a guard, apply a patch, run a formatter — don't. Say in your review that you were
52
+ asked to and declined, and give the finding you would have gotten.
53
+
54
+ ### Testing a hypothesis without touching the tree
55
+
56
+ The common temptation is the vacuity check: *would these tests still pass if the guard were
57
+ removed?* Answer it by **reading**, not by mutating:
58
+
59
+ - Read the guard, read every test that reaches it, and reason about which assertion fails when
60
+ the guard is gone. For a guard of any normal size this is a decisive argument, not a guess.
61
+ - Then **report the finding and the reasoning** — "the `x < 0` guard is not covered: no test
62
+ passes a negative, so deleting it breaks nothing" — and name the test that is missing.
63
+ That is more useful to the builder than a mutation you ran and threw away.
64
+ - If you genuinely cannot settle it by reading, say so and say what you would need. An
65
+ unresolved question in your review is a fine outcome. An edited tree is not.
@@ -134,7 +134,7 @@ Agents within a workspace communicate through `afx send`. Four addressing forms
134
134
  | `afx send architect:<name> "msg"` | Explicit per-architect addressing. **Architects (including `main`)**: open address grammar — any architect can address any other architect (sibling-architect messaging). **Builders**: allowed ONLY when `<name>` matches the builder's own spawning architect; mismatches are rejected by Tower's spoofing check. From a builder, this is an explicit form of the affinity routing, NOT an override. | Any sender (with the spoofing constraint above for builders). |
135
135
  | `afx send <workspace>:architect "msg"` | Cross-workspace addressing (e.g. `afx send marketmaker:architect "..."`). | Any sender. |
136
136
 
137
- **Send outcomes (delivered vs held)**: `afx send` reports the real first outcome. **delivered** means the message was written to the recipient's prompt after a clean render-gate pass; **held** means the prompt wasn't clear, so the message was persisted in Tower's durable mailbox and delivers automatically once the prompt is clean — with a why-held reason (`busy`, `no-profile`, or `no-live-pty`) and a mailbox id. A held message is never force-injected onto a busy line, so it can't corrupt a half-typed draft, and held rows survive a Tower restart. List held messages with `afx inbox`, read one (including its body) with `afx inbox show <id>`, and clear one with `afx inbox dismiss <id>`; `afx send --interrupt` is the explicit bypass.
137
+ **Send outcomes (delivered vs held)**: `afx send` reports the real first outcome. **delivered** means the message was written to the recipient's prompt after a clean render-gate pass; **held** means the prompt wasn't clear, so the message was persisted in Tower's durable mailbox and delivers automatically once the prompt is clean — with a why-held reason (`busy`, `no-profile`, or `no-live-pty`) and a mailbox id. A held message is never force-injected onto a busy line, so it can't corrupt a draft the gate can see, and held rows survive a Tower restart. (The gate reads the composer off the rendered screen and counts input through `PtySession.write()`; `afx attach` writes the shellper socket directly, so a draft typed there is seen only once the app echoes it.) List held messages with `afx inbox`, read one (including its body) with `afx inbox show <id>`, and clear one with `afx inbox dismiss <id>`; `afx send --interrupt` is the explicit bypass.
138
138
 
139
139
  **Sibling-architect messaging**: when a workspace hosts more than one architect (added via `afx workspace add-architect --name <name>`), sibling architects message each other via the `architect:<name>` form. Example: `main` running `afx send architect:ob-refine "PR-iter-2 feedback ready"` lands on the `ob-refine` architect's terminal. This works because sender = architect bypasses the spoofing check.
140
140
 
@@ -132,7 +132,7 @@ Agents within a workspace communicate through `afx send`. Four addressing forms
132
132
  | `afx send architect:<name> "msg"` | Explicit per-architect addressing. **Architects (including `main`)**: open address grammar — any architect can address any other architect (sibling-architect messaging). **Builders**: allowed ONLY when `<name>` matches the builder's own spawning architect; mismatches are rejected by Tower's spoofing check. From a builder, this is an explicit form of the affinity routing, NOT an override. | Any sender (with the spoofing constraint above for builders). |
133
133
  | `afx send <workspace>:architect "msg"` | Cross-workspace addressing (e.g. `afx send marketmaker:architect "..."`). | Any sender. |
134
134
 
135
- **Send outcomes (delivered vs held)**: `afx send` reports the real first outcome. **delivered** means the message was written to the recipient's prompt after a clean render-gate pass; **held** means the prompt wasn't clear, so the message was persisted in Tower's durable mailbox and delivers automatically once the prompt is clean — with a why-held reason (`busy`, `no-profile`, or `no-live-pty`) and a mailbox id. A held message is never force-injected onto a busy line, so it can't corrupt a half-typed draft, and held rows survive a Tower restart. List held messages with `afx inbox`, read one (including its body) with `afx inbox show <id>`, and clear one with `afx inbox dismiss <id>`; `afx send --interrupt` is the explicit bypass.
135
+ **Send outcomes (delivered vs held)**: `afx send` reports the real first outcome. **delivered** means the message was written to the recipient's prompt after a clean render-gate pass; **held** means the prompt wasn't clear, so the message was persisted in Tower's durable mailbox and delivers automatically once the prompt is clean — with a why-held reason (`busy`, `no-profile`, or `no-live-pty`) and a mailbox id. A held message is never force-injected onto a busy line, so it can't corrupt a draft the gate can see, and held rows survive a Tower restart. (The gate reads the composer off the rendered screen and counts input through `PtySession.write()`; `afx attach` writes the shellper socket directly, so a draft typed there is seen only once the app echoes it.) List held messages with `afx inbox`, read one (including its body) with `afx inbox show <id>`, and clear one with `afx inbox dismiss <id>`; `afx send --interrupt` is the explicit bypass.
136
136
 
137
137
  **Sibling-architect messaging**: when a workspace hosts more than one architect (added via `afx workspace add-architect --name <name>`), sibling architects message each other via the `architect:<name>` form. Example: `main` running `afx send architect:ob-refine "PR-iter-2 feedback ready"` lands on the `ob-refine` architect's terminal. This works because sender = architect bypasses the spoofing check.
138
138
 
@@ -1667,6 +1667,11 @@
1667
1667
 
1668
1668
  async function fetchCloudStatus() {
1669
1669
  try {
1670
+ // Served through the tunnel, /api/tunnel/* is local-only — blocked at
1671
+ // the tunnel edge and again by Tower (#1370), so this poll can only
1672
+ // ever 403 (#1599). The widget renders nothing cloud-served anyway;
1673
+ // skip the forbidden request instead of paying it every refresh.
1674
+ if (isCloudServed()) return null;
1670
1675
  const res = await authFetch('./api/tunnel/status');
1671
1676
  if (res.status === 404) return null;
1672
1677
  if (!res.ok) return { state: 'error' };
@@ -1676,6 +1681,16 @@
1676
1681
  }
1677
1682
  }
1678
1683
 
1684
+ // Is this page being viewed through Codev Cloud rather than served by the
1685
+ // local Tower directly? One shared predicate for the tunnel-status poll
1686
+ // and the cloud widget render, so the two can never drift (#1599).
1687
+ // Exact-or-subdomain match, not a bare suffix — `endsWith('codevos.ai')`
1688
+ // would also match an unrelated `evilcodevos.ai` (CMAP).
1689
+ function isCloudServed() {
1690
+ const h = window.location.hostname;
1691
+ return h === 'codevos.ai' || h.endsWith('.codevos.ai');
1692
+ }
1693
+
1679
1694
  function formatUptime(ms) {
1680
1695
  const s = Math.floor(ms / 1000);
1681
1696
  if (s < 60) return s + 's';
@@ -1723,7 +1738,7 @@
1723
1738
  noteCloudStateChange(status && status.state ? status.state : null);
1724
1739
 
1725
1740
  // When accessed through codevos.ai, cloud status is irrelevant
1726
- if (window.location.hostname.endsWith('codevos.ai')) {
1741
+ if (isCloudServed()) {
1727
1742
  el.innerHTML = '';
1728
1743
  return;
1729
1744
  }
@@ -1,32 +0,0 @@
1
- /**
2
- * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3
- * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4
- * https://github.com/chjj/term.js
5
- * @license MIT
6
- *
7
- * Permission is hereby granted, free of charge, to any person obtaining a copy
8
- * of this software and associated documentation files (the "Software"), to deal
9
- * in the Software without restriction, including without limitation the rights
10
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- * copies of the Software, and to permit persons to whom the Software is
12
- * furnished to do so, subject to the following conditions:
13
- *
14
- * The above copyright notice and this permission notice shall be included in
15
- * all copies or substantial portions of the Software.
16
- *
17
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
- * THE SOFTWARE.
24
- *
25
- * Originally forked from (with the author's permission):
26
- * Fabrice Bellard's javascript vt100 for jslinux:
27
- * http://bellard.org/jslinux/
28
- * Copyright (c) 2011 Fabrice Bellard
29
- * The original design remains. The terminal itself
30
- * has been extended to include xterm CSI codes, among
31
- * other features.
32
- */.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}:root{--bg-primary: #1a1a1a;--bg-secondary: #252525;--bg-tertiary: #2a2a2a;--bg-hover: #333333;--text-primary: #e0e0e0;--text-secondary: #a0a0a0;--text-muted: #666666;--border-color: #333333;--accent: #3b82f6;--accent-hover: #2563eb;--status-active: #22c55e;--status-waiting: #eab308;--status-error: #ef4444;--status-implementing: #f97316}*{margin:0;padding:0;box-sizing:border-box}html,body,#root{height:100%;width:100%;overflow:hidden}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,monospace;background:var(--bg-primary);color:var(--text-primary);font-size:13px}.fullscreen-terminal{height:100vh;width:100vw;overflow:hidden}.fullscreen-terminal>div{height:100%;width:100%}.app{display:flex;flex-direction:column;height:100vh}.app-header{display:flex;align-items:center;justify-content:space-between;padding:8px 16px;background:var(--bg-secondary);border-bottom:1px solid var(--border-color);height:40px;flex-shrink:0}.app-title{font-size:14px;font-weight:600;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.header-version{font-size:11px;color:var(--text-secondary);font-weight:400}.app-body{flex:1;overflow:hidden}.split-pane{display:flex;height:100%;width:100%}.split-left,.split-right{height:100%;overflow:hidden;position:relative}.split-handle{width:4px;cursor:col-resize;background:var(--border-color);flex-shrink:0}.split-handle:hover{background:var(--accent)}.expand-bar{width:24px;height:100%;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:var(--bg-secondary);border:none;border-left:1px solid var(--border-color);border-right:1px solid var(--border-color);color:var(--text-muted);cursor:pointer;padding:0;font-family:inherit}.expand-bar:hover{background:var(--bg-hover);color:var(--accent)}.header-controls{display:flex;align-items:center;gap:4px}.header-btn{display:flex;align-items:center;justify-content:center;width:32px;height:28px;padding:0;background:transparent;border:1px solid var(--border-color);border-radius:4px;color:var(--text-secondary);cursor:pointer}.header-btn:hover{background:var(--bg-hover);color:var(--text-primary);border-color:var(--accent)}.right-panel{display:flex;flex-direction:column;height:100%}.tab-bar{display:flex;background:var(--bg-secondary);border-bottom:1px solid var(--border-color);overflow-x:auto;flex-shrink:0;height:34px}.tab{display:flex;align-items:center;gap:6px;padding:6px 12px;background:none;border:none;border-bottom:2px solid transparent;color:var(--text-secondary);font-size:12px;cursor:pointer;white-space:nowrap;font-family:inherit}.tab:hover{background:var(--bg-hover);color:var(--text-primary)}.tab-active{color:var(--text-primary);border-bottom-color:var(--accent)}.tab-icon{flex-shrink:0}.tab-label{max-width:120px;overflow:hidden;text-overflow:ellipsis}.tab-close{font-size:14px;line-height:1;opacity:.5;padding:0 2px;border-radius:3px}.tab-close:hover{opacity:1;background:var(--bg-hover)}.remove-architect-modal-backdrop,.confirm-dialog-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:1000}.remove-architect-modal,.confirm-dialog{background:var(--bg-primary, #1e1e1e);color:var(--text-primary, #e0e0e0);border-radius:6px;padding:24px;max-width:500px;width:90%;box-shadow:0 10px 40px #00000080}.remove-architect-modal h2,.confirm-dialog h2{margin:0 0 12px;font-size:18px}.remove-architect-modal p,.confirm-dialog p{margin:0 0 16px;line-height:1.5}.remove-architect-modal code,.confirm-dialog code{background:var(--bg-secondary, #2a2a2a);padding:2px 6px;border-radius:3px;font-family:ui-monospace,monospace}.remove-architect-error{color:var(--text-error, #ff6b6b)}.remove-architect-modal-actions,.confirm-dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}.remove-architect-modal-actions button,.confirm-dialog-actions button{padding:6px 14px;border:1px solid var(--border-color, #444);background:var(--bg-secondary, #2a2a2a);color:var(--text-primary, #e0e0e0);border-radius:4px;cursor:pointer}.remove-architect-modal-actions button:hover:not(:disabled),.confirm-dialog-actions button:hover:not(:disabled){background:var(--bg-hover, #333)}.remove-architect-modal-actions button.primary,.confirm-dialog-actions button.primary{background:var(--accent-color, #f44747);color:#fff;border-color:var(--accent-color, #f44747)}.remove-architect-modal-actions button:disabled,.confirm-dialog-actions button:disabled{opacity:.6;cursor:not-allowed}.tab-content{flex:1;overflow:hidden}.terminal-tab-pane{width:100%;height:100%}.file-path-decoration{border-bottom:1px dotted rgba(255,255,255,.3)!important;background:transparent!important;pointer-events:none}.file-path-decoration-hover{border-bottom-color:#ffffffb3!important}.terminal-iframe{display:block}.file-tree{padding:8px;overflow-y:auto;height:100%}.file-node{display:flex;align-items:center;gap:6px;padding:3px 0;cursor:pointer;font-size:12px;border-radius:4px}.file-node:hover{background:var(--bg-hover)}.file-icon{font-size:14px;flex-shrink:0}.file-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-tree-error,.file-tree-loading{padding:16px;color:var(--text-muted);font-size:12px}.no-architect{display:flex;align-items:center;justify-content:center;height:100%;color:var(--text-muted);font-size:14px}.architect-pane{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;min-height:0}.architect-pane-body{flex:1;min-height:0}.virtual-keyboard{display:flex;gap:6px;padding:6px 8px;background:var(--bg-secondary);border-bottom:1px solid var(--border-color);flex-shrink:0}.virtual-key{padding:4px 12px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:4px;color:var(--text-secondary);font-size:12px;font-family:inherit;cursor:pointer;touch-action:manipulation;user-select:none;-webkit-user-select:none}.virtual-key:active{background:var(--bg-hover);color:var(--text-primary)}.virtual-key-active{background:var(--accent);border-color:var(--accent);color:#fff}.virtual-key-active:active{background:var(--accent-hover)}.terminal-controls{position:absolute;top:8px;right:12px;z-index:20;display:flex;align-items:center;gap:4px;padding:4px 6px;background:#1e1e1ed9;border:1px solid rgba(255,255,255,.12);border-radius:8px}.terminal-control-btn{display:flex;align-items:center;justify-content:center;min-width:32px;min-height:32px;padding:0;background:transparent;border:1px solid transparent;border-radius:5px;color:silver;cursor:pointer;touch-action:manipulation;user-select:none;-webkit-user-select:none}.terminal-control-btn:hover{background:#ffffff1a;border-color:#ffffff26;color:#fff}.terminal-control-btn:active{background:#ffffff26}.toolbar-divider{width:1px;height:20px;background:#ffffff26;margin:0 2px;flex-shrink:0}.terminal-status-icon{cursor:default}.terminal-status-icon.terminal-status-connected{color:#22c55e;opacity:.7}.terminal-status-icon.terminal-status-reconnecting{color:#eab308;opacity:.8;animation:terminal-pulse 1.2s ease-in-out infinite}.terminal-status-icon.terminal-status-disconnected{color:#ef4444;opacity:.8}@keyframes terminal-pulse{0%,to{opacity:.8}50%{opacity:.2}}.mobile-layout{display:flex;flex-direction:column;height:100vh}.mobile-content{flex:1;overflow:hidden;display:flex;flex-direction:column}@media(max-width:768px){.app-header{padding:6px 12px}.tab{padding:4px 8px;font-size:11px}}.file-viewer{display:flex;flex-direction:column;height:100%;background:var(--bg-primary)}.file-loading,.file-error{display:flex;align-items:center;justify-content:center;height:100%;color:var(--text-secondary)}.file-error{color:var(--status-error)}.file-header{display:flex;align-items:center;gap:12px;padding:8px 12px;background:var(--bg-secondary);border-bottom:1px solid var(--border-color);flex-shrink:0}.file-path{font-family:monospace;font-size:12px;color:var(--text-primary);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-language{font-size:11px;color:var(--text-muted);text-transform:uppercase}.file-save-btn{padding:4px 12px;font-size:11px;background:var(--accent);color:#fff;border:none;border-radius:4px;cursor:pointer}.file-save-btn:hover{background:var(--accent-hover)}.file-save-btn:disabled{opacity:.5;cursor:not-allowed}.file-content{display:flex;flex:1;overflow:auto;background:var(--bg-primary)}.line-numbers{display:flex;flex-direction:column;padding:12px 8px;background:var(--bg-secondary);color:var(--text-muted);font-family:Menlo,Monaco,Courier New,monospace;font-size:13px;line-height:1.5;text-align:right;-webkit-user-select:none;user-select:none;flex-shrink:0;min-width:40px}.line-number{padding-right:8px}.line-number.highlighted-line{background:#3b82f64d}.file-text{flex:1;padding:12px;font-family:Menlo,Monaco,Courier New,monospace;font-size:13px;line-height:1.5;background:var(--bg-primary);color:var(--text-primary);border:none;outline:none;resize:none;white-space:pre;overflow:auto}.file-image-container,.file-video-container{flex:1;display:flex;align-items:center;justify-content:center;overflow:auto;padding:20px}.file-image{max-width:100%;max-height:100%;object-fit:contain}.file-video{max-width:100%;max-height:100%}.file-tree-container{display:flex;flex-direction:column;height:100%;overflow:hidden}.file-search{position:relative;padding:8px;border-bottom:1px solid var(--border-color);flex-shrink:0}.file-search-input{width:100%;padding:6px 10px;font-size:12px;font-family:inherit;background:var(--bg-primary);border:1px solid var(--border-color);border-radius:4px;color:var(--text-primary);outline:none}.file-search-input:focus{border-color:var(--accent)}.file-search-input::placeholder{color:var(--text-muted)}.file-search-suggestions{position:absolute;top:100%;left:8px;right:8px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:4px;box-shadow:0 4px 12px #0000004d;z-index:100;max-height:240px;overflow-y:auto}.file-search-suggestion{display:flex;flex-direction:column;padding:8px 10px;cursor:pointer;border-bottom:1px solid var(--border-color)}.file-search-suggestion:last-child{border-bottom:none}.file-search-suggestion:hover{background:var(--bg-hover)}.suggestion-name{font-size:12px;color:var(--text-primary);font-weight:500}.suggestion-path{font-size:10px;color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-tree-section{display:flex;flex-direction:column;overflow:hidden}.file-tree-section:first-of-type{flex-shrink:0}.file-tree-section:last-of-type{flex:1;overflow:hidden}.file-tree-section-header{padding:8px 12px;font-size:11px;font-weight:600;text-transform:uppercase;color:var(--text-muted);letter-spacing:.5px;border-bottom:1px solid var(--border-color);flex-shrink:0}.recent-file{color:var(--text-secondary)}.recent-file:hover{color:var(--text-primary)}.file-path-hint{font-size:10px;color:var(--text-muted);margin-left:auto;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.git-indicator{font-family:monospace;font-size:10px;font-weight:700;padding:1px 4px;border-radius:3px;margin-left:auto;flex-shrink:0}.git-modified{color:var(--status-implementing);background:#f9731626}.git-staged{color:var(--status-active);background:#22c55e26}.git-untracked{color:var(--text-muted);background:#66666626}.cloud-status{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--text-secondary);margin-right:12px}.cloud-status--none{color:var(--text-muted)}.cloud-status--error{color:var(--status-error)}.cloud-dot{display:inline-block;width:8px;height:8px;border-radius:50%;flex-shrink:0}.cloud-dot--green{background:var(--status-active)}.cloud-dot--yellow{background:var(--status-waiting);animation:cloud-pulse 1.2s ease-in-out infinite}@keyframes cloud-pulse{0%,to{opacity:1}50%{opacity:.3}}.cloud-dot--red{background:var(--status-error)}.cloud-dot--gray{background:var(--text-muted)}.cloud-uptime{color:var(--text-muted);font-size:11px}.held-badge{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--text-secondary);white-space:nowrap}.held-badge--attention{color:var(--status-waiting);font-weight:600}.held-dot{display:inline-block;width:8px;height:8px;border-radius:50%;flex-shrink:0;background:var(--text-muted)}.held-dot--attention{background:var(--status-waiting);animation:cloud-pulse 1.2s ease-in-out infinite}.cloud-hint{color:var(--text-muted);font-size:11px;font-style:italic}.cloud-link{color:var(--accent);text-decoration:none;font-size:11px}.cloud-link:hover{text-decoration:underline}.cloud-btn{background:transparent;border:1px solid var(--border-color);color:var(--text-secondary);font-size:11px;padding:1px 6px;border-radius:3px;cursor:pointer}.cloud-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.cloud-btn:disabled{opacity:.5;cursor:default}.mobile-wrapper{display:flex;flex-direction:column;height:100vh}.mobile-header{padding:4px 8px;border-bottom:1px solid var(--border-color);background:var(--bg-primary)}.mobile-wrapper .mobile-layout{flex:1;min-height:0}.work-view{display:flex;flex-direction:column;height:100%;overflow:hidden}.work-content{flex:1;overflow-y:auto;padding:12px 16px}.work-view.file-panel-open .work-content{flex:2}.work-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}.work-title{font-size:19px;font-weight:600;color:var(--text-primary)}.work-actions{display:flex;gap:6px}.work-btn{background:var(--accent);color:#fff;border:none;padding:4px 10px;border-radius:4px;font-size:14px;cursor:pointer}.work-btn:hover{background:var(--accent-hover)}.work-btn-secondary{background:transparent;border:1px solid var(--border-color);color:var(--text-secondary)}.work-btn-secondary:hover{background:var(--bg-hover);color:var(--text-primary)}.tip-banner{display:flex;align-items:center;gap:8px;padding:6px 12px;margin-bottom:16px;background:var(--bg-tertiary);border-radius:4px;font-size:14px;color:var(--text-secondary)}.tip-banner-label{font-weight:600;color:var(--text-muted);white-space:nowrap}.tip-banner-text{flex:1;min-width:0}.tip-banner-text code{font-family:var(--font-mono, monospace);background:var(--bg-primary);padding:1px 4px;border-radius:2px;font-size:13px;color:var(--text-primary)}.tip-banner-nav{background:none;border:none;color:var(--text-muted);cursor:pointer;padding:2px 4px;font-size:14px;line-height:1;border-radius:2px}.tip-banner-nav:hover{color:var(--text-primary);background:var(--bg-hover)}.tip-banner-dismiss{background:none;border:none;color:var(--text-muted);cursor:pointer;padding:2px 4px;font-size:14px;line-height:1;border-radius:2px;margin-left:4px}.tip-banner-dismiss:hover{color:var(--text-primary);background:var(--bg-hover)}.work-section{margin-bottom:20px}.work-section-title{font-size:13px;font-weight:600;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px;display:flex;align-items:center;gap:6px}.work-empty{color:var(--text-muted);font-style:italic;font-size:14px;padding:8px 0}.work-unavailable{color:var(--status-waiting);font-size:14px;padding:8px 0}.work-error{color:var(--status-error);font-size:14px;padding:8px;margin-bottom:12px;background:#ef44441a;border-radius:4px}.work-loading{color:var(--text-muted);padding:16px}.builder-table{width:100%;border-collapse:separate;border-spacing:0 4px}.builder-table thead th{font-size:13px;font-weight:500;color:var(--text-secondary);text-align:left;padding:0 10px 4px;white-space:nowrap}.builder-row td{background:var(--bg-secondary);padding:8px 10px;font-size:16px;border-top:1px solid var(--border-color);border-bottom:1px solid var(--border-color)}.builder-row td:first-child{border-left:1px solid var(--border-color);border-radius:8px 0 0 8px}.builder-row td:last-child{border-right:1px solid var(--border-color);border-radius:0 8px 8px 0}.builder-row:hover td{border-color:var(--accent)}.builder-row--blocked td:first-child{border-left:3px solid var(--status-waiting)}.builder-col-id{font-weight:500;color:var(--accent);white-space:nowrap;min-width:60px}.builder-attribution{font-size:.9em;color:var(--text-secondary);font-weight:400}.builder-col-title{color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:0;width:40%}.builder-col-state{white-space:nowrap;width:140px}.builder-state-active{font-size:14px;color:var(--accent)}.builder-state-blocked{font-size:14px;color:var(--status-waiting)}.builder-col-progress{width:120px;white-space:nowrap}.progress-bar{width:80px;height:4px;background:var(--bg-tertiary);border-radius:2px;overflow:hidden;display:inline-block;vertical-align:middle}.progress-fill{height:100%;background:var(--status-active);border-radius:2px;transition:width .3s ease}.progress-fill--blocked{background:var(--status-waiting)}.progress-pct{font-size:13px;color:var(--text-secondary);margin-left:6px;vertical-align:middle}.builder-col-elapsed{font-size:14px;color:var(--text-secondary);white-space:nowrap;width:70px}.builder-col-actions{width:60px;text-align:right}.builder-row-open{font-size:13px;color:var(--accent);background:#3b82f614;border:1px solid rgba(59,130,246,.2);padding:3px 10px;border-radius:5px;cursor:pointer;white-space:nowrap}.builder-row-open:hover{background:#3b82f62e}.pr-rows{display:flex;flex-direction:column;gap:4px}a.pr-row{text-decoration:none;color:inherit;cursor:pointer}.pr-row{display:flex;align-items:center;gap:10px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:8px;padding:10px 14px}.pr-row:hover{border-color:var(--accent)}.pr-row-number{font-size:16px;font-weight:500;color:var(--accent);white-space:nowrap}.pr-row-title{flex:1;font-size:16px;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pr-row-status{font-size:13px;white-space:nowrap}.pr-status--approved{color:var(--status-active)}.pr-status--reviewing{color:var(--status-waiting)}.pr-status--changes{color:var(--status-error)}.pr-row-age{font-size:13px;color:var(--text-muted);white-space:nowrap}.pr-row-author{font-size:12px;color:var(--text-muted);white-space:nowrap}.attention-rows{display:flex;flex-direction:column;gap:4px}a.attention-row{text-decoration:none;color:inherit;cursor:pointer}.attention-row{display:flex;align-items:center;gap:10px;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:8px;padding:10px 14px}.attention-row:hover{border-color:var(--accent)}.attention-row-id{font-size:16px;font-weight:500;color:var(--accent);white-space:nowrap}.attention-row-title{flex:1;font-size:16px;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.attention-row-kind{font-size:13px;white-space:nowrap}.attention-kind--pr{color:var(--status-waiting)}.attention-kind--spec,.attention-kind--plan{color:var(--status-error)}.attention-kind--dev{color:var(--status-implementing)}.attention-kind--verify{color:var(--status-waiting)}.attention-row-age{font-size:13px;color:var(--text-muted);white-space:nowrap}.backlog-rows{display:flex;flex-direction:column;gap:2px}.backlog-row{display:flex;align-items:center;gap:6px;padding:4px 14px;border-radius:6px}.backlog-row:hover{background:var(--bg-secondary)}.backlog-row-main{display:flex;align-items:center;gap:10px;flex:1;min-width:0;text-decoration:none;color:inherit}.backlog-row-main:hover .backlog-row-title{color:var(--accent)}.backlog-artifacts{display:flex;gap:4px;flex-shrink:0}.backlog-artifact-link{font-size:12px;padding:1px 5px;border-radius:3px;border:1px solid var(--border-color);background:transparent;color:var(--text-secondary);cursor:pointer;white-space:nowrap}.backlog-artifact-link:hover{background:var(--bg-hover);color:var(--accent);border-color:var(--accent)}.backlog-priority-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}.priority-dot--high{background:var(--status-error)}.priority-dot--med{background:var(--status-waiting)}.priority-dot--low{background:var(--text-muted)}.backlog-row-number{font-size:16px;font-weight:500;color:var(--accent);white-space:nowrap}.backlog-type-tag{font-size:12px;padding:1px 5px;border-radius:3px;font-weight:500;white-space:nowrap}.type-tag--bug{background:#ef444426;color:var(--status-error)}.type-tag--project{background:#a855f71f;color:#a855f7}.type-tag--spike{background:#0ea5e91f;color:#0ea5e9}.backlog-row-title{flex:1;font-size:16px;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.backlog-row-age{font-size:13px;color:var(--text-muted);white-space:nowrap}.backlog-row-author,.backlog-row-assignees{font-size:12px;color:var(--text-muted);white-space:nowrap}.recently-closed-rows{display:flex;flex-direction:column;gap:2px}.recently-closed-row{display:flex;align-items:center;gap:6px;padding:4px 14px;border-radius:6px;opacity:.7}.recently-closed-row:hover{background:var(--bg-secondary);opacity:1}.recently-closed-row-main{display:flex;align-items:center;gap:10px;flex:1;min-width:0;text-decoration:none;color:inherit}.recently-closed-check{font-size:13px;color:var(--status-active);flex-shrink:0}.work-file-panel{border-top:1px solid var(--border-color);background:var(--bg-secondary)}.work-file-panel.expanded{flex:1;display:flex;flex-direction:column;overflow:hidden}.work-file-panel.collapsed{flex-shrink:0}.work-file-panel-header{display:flex;align-items:center;gap:6px;padding:6px 12px;cursor:pointer;-webkit-user-select:none;user-select:none}.work-file-panel-header:hover{background:var(--bg-hover)}.work-file-panel-toggle{font-size:12px;color:var(--text-muted)}.work-file-panel-label{font-size:14px;font-weight:500;color:var(--text-secondary)}.work-file-panel-search{flex:1;margin-left:8px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:3px;color:var(--text-primary);font-size:14px;padding:3px 8px;outline:none}.work-file-panel-search::placeholder{color:var(--text-muted)}.work-file-panel-search:focus{border-color:var(--accent)}.work-file-panel-content{flex:1;overflow-y:auto;padding:0 12px 12px}@media(max-width:768px){.work-content{padding:8px 12px}.work-header{flex-wrap:wrap;gap:8px}.pr-row,.attention-row{flex-wrap:wrap}.builder-table{font-size:14px}.builder-col-elapsed,.builder-table thead th:nth-child(5){display:none}.pr-row-title,.attention-row-title,.backlog-row-title{min-width:60%}}.analytics-view{display:flex;flex-direction:column;height:100%;overflow:hidden}.analytics-content{flex:1;overflow-y:auto;padding:12px 16px}.analytics-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}.analytics-title{font-size:16px;font-weight:600;color:var(--text-primary)}.analytics-actions{display:flex;gap:6px;align-items:center}.analytics-range-selector{display:flex;border:1px solid var(--border-color);border-radius:4px;overflow:hidden}.analytics-range-btn{background:transparent;border:none;border-right:1px solid var(--border-color);padding:3px 8px;font-size:11px;cursor:pointer;color:var(--text-secondary)}.analytics-range-btn:last-child{border-right:none}.analytics-range-btn:hover{background:var(--bg-hover)}.analytics-range-btn.active{background:var(--accent);color:#fff}.analytics-section{margin-bottom:16px}.analytics-section-title{font-size:11px;font-weight:600;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px;display:flex;align-items:center;gap:4px}.analytics-collapse-icon{font-size:10px;width:12px;display:inline-block}.analytics-error{color:var(--status-error);font-size:12px;margin-bottom:8px;padding:6px 8px;background:#ef44441a;border-radius:4px}.analytics-loading{color:var(--text-secondary);font-size:13px;padding:20px 0;text-align:center}.analytics-metric-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:8px}.analytics-metric{display:flex;flex-direction:column;padding:8px 10px;background:var(--bg-secondary, var(--bg-hover));border-radius:4px}.analytics-metric-value{font-size:18px;font-weight:600;color:var(--text-primary);line-height:1.2}.analytics-metric-label{font-size:10px;color:var(--text-secondary);margin-top:2px}.analytics-sub-section{margin-top:10px}.analytics-sub-title{font-size:11px;font-weight:500;color:var(--text-secondary);margin-bottom:4px}.analytics-table{width:100%;border-collapse:collapse;font-size:11px}.analytics-table th{text-align:left;color:var(--text-secondary);font-weight:500;padding:3px 6px;border-bottom:1px solid var(--border-color)}.analytics-table td{padding:3px 6px;color:var(--text-primary);border-bottom:1px solid var(--border-subtle, var(--border-color))}.analytics-list{list-style:none;padding:0;margin:0}.analytics-list li{display:flex;justify-content:space-between;padding:2px 0;font-size:12px}.analytics-list-label{color:var(--text-secondary)}.analytics-list-value{color:var(--text-primary);font-weight:500}.analytics-charts-row{display:flex;gap:8px}.analytics-chart-half{flex:1;min-width:0}.team-view{display:flex;flex-direction:column;height:100%;overflow:hidden}.team-content{flex:1;overflow-y:auto;padding:12px 16px}.team-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}.team-title{font-size:16px;font-weight:600;color:var(--text-primary);margin:0}.team-refresh-btn{background:transparent;border:1px solid var(--border-color);border-radius:4px;padding:4px 10px;font-size:11px;color:var(--text-secondary);cursor:pointer}.team-refresh-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.team-refresh-btn:disabled{opacity:.5;cursor:default}.team-error{color:var(--status-error);font-size:12px;margin-bottom:8px;padding:6px 8px;background:#ef44441a;border-radius:4px}.team-loading{color:var(--text-secondary);font-size:13px;padding:20px 0;text-align:center}.team-section{margin-bottom:16px}.team-section-title{font-size:11px;font-weight:600;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px}.team-member-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:8px}.team-member-card{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:6px;padding:10px 12px}.team-member-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:4px}.team-member-name{font-size:13px;font-weight:600;color:var(--text-primary)}.team-member-role{font-size:10px;color:var(--text-muted);background:var(--bg-tertiary);padding:1px 5px;border-radius:3px;text-transform:uppercase;letter-spacing:.3px}.team-member-github{font-size:11px;color:var(--accent);text-decoration:none;display:block;margin-bottom:6px}.team-member-github:hover{text-decoration:underline}.team-member-section{margin-top:6px}.team-section-label{font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.3px;display:block;margin-bottom:2px}.team-item-list{display:flex;flex-direction:column}.team-item-link{display:block;font-size:11px;color:var(--accent);text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;padding:1px 0}.team-item-link:hover{text-decoration:underline}.team-item-empty{font-size:11px;color:var(--text-muted);font-style:italic}.team-member-activity{display:flex;gap:8px;font-size:10px;color:var(--text-muted);margin-top:4px}.team-review-blocking-list{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:2px}.team-review-blocking-item{display:flex;align-items:baseline;gap:6px;font-size:11px;line-height:1.3;min-width:0}.team-review-blocking-sentence{flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.team-review-blocking-sentence strong{font-weight:600}.team-review-blocking-link{display:inline;white-space:normal}.team-review-blocking-age{flex:0 0 auto;font-size:10px;color:var(--text-muted);font-style:italic}.team-activity-label{color:var(--text-muted);font-style:italic}.team-messages{display:flex;flex-direction:column;gap:6px}.team-message{padding:8px 10px;background:var(--bg-secondary);border-radius:4px;border-left:2px solid var(--border-color)}.team-message-header{display:flex;align-items:baseline;gap:8px;margin-bottom:4px}.team-message-author{font-size:12px;font-weight:600;color:var(--text-primary)}.team-message-time{font-size:10px;color:var(--text-muted)}.team-message-body{font-size:12px;color:var(--text-secondary);white-space:pre-wrap;word-break:break-word}.team-no-messages{font-size:12px;color:var(--text-muted);padding:8px 0}.team-activity-feed{display:flex;flex-direction:column;gap:2px}.team-activity-entry{display:flex;align-items:baseline;gap:6px;font-size:12px;color:var(--text-secondary);text-decoration:none;padding:4px 8px;border-radius:3px}.team-activity-entry:hover{background:var(--bg-secondary)}.team-activity-date{font-size:10px;color:var(--text-muted);min-width:50px;flex-shrink:0}.team-activity-author{color:var(--accent);flex-shrink:0}.team-activity-action{color:var(--text-muted);flex-shrink:0}.team-activity-ref{color:var(--text-secondary);flex-shrink:0}.team-activity-title{color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}.ofs-rows{display:flex;flex-direction:column;gap:2px}.ofs-subgroup-label{font-size:11px;font-weight:600;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.05em;padding:4px 14px 2px}.ofs-row{display:flex;align-items:center;gap:8px;padding:5px 14px;border-radius:6px;cursor:pointer;font-size:14px}.ofs-row:hover{background:var(--bg-secondary)}.ofs-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.ofs-dot--running{background:var(--status-active)}.ofs-dot--idle{background:var(--text-secondary)}.ofs-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ofs-idle{font-size:12px;color:var(--text-secondary);white-space:nowrap}.ofs-file-icon{font-size:14px;flex-shrink:0}.ofs-path{font-size:12px;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}