@cluesmith/codev 3.3.0 → 3.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/dashboard-dist/assets/{index-JCbkpAcV.js → index-D3XaKpZW.js} +45 -43
  2. package/dashboard-dist/assets/index-D3XaKpZW.js.map +1 -0
  3. package/dashboard-dist/index.html +1 -1
  4. package/dist/agent-farm/cli.d.ts.map +1 -1
  5. package/dist/agent-farm/cli.js +99 -36
  6. package/dist/agent-farm/cli.js.map +1 -1
  7. package/dist/agent-farm/commands/reset/constants.d.ts +119 -1
  8. package/dist/agent-farm/commands/reset/constants.d.ts.map +1 -1
  9. package/dist/agent-farm/commands/reset/constants.js +119 -1
  10. package/dist/agent-farm/commands/reset/constants.js.map +1 -1
  11. package/dist/agent-farm/commands/reset/context.d.ts +20 -2
  12. package/dist/agent-farm/commands/reset/context.d.ts.map +1 -1
  13. package/dist/agent-farm/commands/reset/context.js +44 -3
  14. package/dist/agent-farm/commands/reset/context.js.map +1 -1
  15. package/dist/agent-farm/commands/reset/index.d.ts +7 -7
  16. package/dist/agent-farm/commands/reset/index.js +8 -8
  17. package/dist/agent-farm/commands/reset/index.js.map +1 -1
  18. package/dist/agent-farm/commands/reset/receipt.d.ts +1 -1
  19. package/dist/agent-farm/commands/reset/receipt.d.ts.map +1 -1
  20. package/dist/agent-farm/commands/reset/receipt.js +19 -8
  21. package/dist/agent-farm/commands/reset/receipt.js.map +1 -1
  22. package/dist/agent-farm/commands/reset/reorient.d.ts +1 -1
  23. package/dist/agent-farm/commands/reset/reorient.d.ts.map +1 -1
  24. package/dist/agent-farm/commands/reset/reorient.js +12 -5
  25. package/dist/agent-farm/commands/reset/reorient.js.map +1 -1
  26. package/dist/agent-farm/commands/reset/self.d.ts +335 -0
  27. package/dist/agent-farm/commands/reset/self.d.ts.map +1 -0
  28. package/dist/agent-farm/commands/reset/self.js +636 -0
  29. package/dist/agent-farm/commands/reset/self.js.map +1 -0
  30. package/dist/agent-farm/commands/reset.d.ts +18 -3
  31. package/dist/agent-farm/commands/reset.d.ts.map +1 -1
  32. package/dist/agent-farm/commands/reset.js +55 -33
  33. package/dist/agent-farm/commands/reset.js.map +1 -1
  34. package/dist/agent-farm/commands/self-refresh.d.ts +60 -0
  35. package/dist/agent-farm/commands/self-refresh.d.ts.map +1 -0
  36. package/dist/agent-farm/commands/self-refresh.js +506 -0
  37. package/dist/agent-farm/commands/self-refresh.js.map +1 -0
  38. package/dist/agent-farm/commands/send.d.ts.map +1 -1
  39. package/dist/agent-farm/commands/send.js +27 -2
  40. package/dist/agent-farm/commands/send.js.map +1 -1
  41. package/dist/agent-farm/commands/spawn-worktree.js +1 -1
  42. package/dist/agent-farm/commands/spawn-worktree.js.map +1 -1
  43. package/dist/agent-farm/commands/spawn.d.ts +7 -0
  44. package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
  45. package/dist/agent-farm/commands/spawn.js +13 -1
  46. package/dist/agent-farm/commands/spawn.js.map +1 -1
  47. package/dist/agent-farm/commands/tower.d.ts.map +1 -1
  48. package/dist/agent-farm/commands/tower.js +13 -0
  49. package/dist/agent-farm/commands/tower.js.map +1 -1
  50. package/dist/agent-farm/lib/reconnect-backoff.d.ts +10 -0
  51. package/dist/agent-farm/lib/reconnect-backoff.d.ts.map +1 -1
  52. package/dist/agent-farm/lib/reconnect-backoff.js +10 -0
  53. package/dist/agent-farm/lib/reconnect-backoff.js.map +1 -1
  54. package/dist/agent-farm/lib/tower-client.d.ts +12 -0
  55. package/dist/agent-farm/lib/tower-client.d.ts.map +1 -1
  56. package/dist/agent-farm/lib/tower-client.js +12 -0
  57. package/dist/agent-farm/lib/tower-client.js.map +1 -1
  58. package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -1
  59. package/dist/agent-farm/lib/tunnel-client.js +7 -3
  60. package/dist/agent-farm/lib/tunnel-client.js.map +1 -1
  61. package/dist/agent-farm/servers/canvas-relay.d.ts +60 -0
  62. package/dist/agent-farm/servers/canvas-relay.d.ts.map +1 -0
  63. package/dist/agent-farm/servers/canvas-relay.js +376 -0
  64. package/dist/agent-farm/servers/canvas-relay.js.map +1 -0
  65. package/dist/agent-farm/servers/gate-profiles.d.ts +1 -1
  66. package/dist/agent-farm/servers/gate-profiles.js +1 -1
  67. package/dist/agent-farm/servers/mailbox-delivery.d.ts +141 -13
  68. package/dist/agent-farm/servers/mailbox-delivery.d.ts.map +1 -1
  69. package/dist/agent-farm/servers/mailbox-delivery.js +172 -16
  70. package/dist/agent-farm/servers/mailbox-delivery.js.map +1 -1
  71. package/dist/agent-farm/servers/mailbox-wiring.d.ts +24 -2
  72. package/dist/agent-farm/servers/mailbox-wiring.d.ts.map +1 -1
  73. package/dist/agent-farm/servers/mailbox-wiring.js +92 -32
  74. package/dist/agent-farm/servers/mailbox-wiring.js.map +1 -1
  75. package/dist/agent-farm/servers/message-write.d.ts +58 -18
  76. package/dist/agent-farm/servers/message-write.d.ts.map +1 -1
  77. package/dist/agent-farm/servers/message-write.js +73 -28
  78. package/dist/agent-farm/servers/message-write.js.map +1 -1
  79. package/dist/agent-farm/servers/overview.d.ts +19 -0
  80. package/dist/agent-farm/servers/overview.d.ts.map +1 -1
  81. package/dist/agent-farm/servers/overview.js +78 -1
  82. package/dist/agent-farm/servers/overview.js.map +1 -1
  83. package/dist/agent-farm/servers/render-gate.d.ts +13 -0
  84. package/dist/agent-farm/servers/render-gate.d.ts.map +1 -1
  85. package/dist/agent-farm/servers/render-gate.js +21 -0
  86. package/dist/agent-farm/servers/render-gate.js.map +1 -1
  87. package/dist/agent-farm/servers/session-submit.d.ts +221 -28
  88. package/dist/agent-farm/servers/session-submit.d.ts.map +1 -1
  89. package/dist/agent-farm/servers/session-submit.js +334 -43
  90. package/dist/agent-farm/servers/session-submit.js.map +1 -1
  91. package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -1
  92. package/dist/agent-farm/servers/tower-routes.js +269 -64
  93. package/dist/agent-farm/servers/tower-routes.js.map +1 -1
  94. package/dist/agent-farm/servers/tower-server.js +25 -3
  95. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  96. package/dist/agent-farm/servers/tower-terminals.d.ts +6 -0
  97. package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -1
  98. package/dist/agent-farm/servers/tower-terminals.js +30 -5
  99. package/dist/agent-farm/servers/tower-terminals.js.map +1 -1
  100. package/dist/agent-farm/servers/tower-tunnel.d.ts.map +1 -1
  101. package/dist/agent-farm/servers/tower-tunnel.js +14 -0
  102. package/dist/agent-farm/servers/tower-tunnel.js.map +1 -1
  103. package/dist/agent-farm/servers/tower-websocket.d.ts +6 -1
  104. package/dist/agent-farm/servers/tower-websocket.d.ts.map +1 -1
  105. package/dist/agent-farm/servers/tower-websocket.js +90 -37
  106. package/dist/agent-farm/servers/tower-websocket.js.map +1 -1
  107. package/dist/agent-farm/types.d.ts +37 -2
  108. package/dist/agent-farm/types.d.ts.map +1 -1
  109. package/dist/agent-farm/utils/harness.d.ts +3 -3
  110. package/dist/agent-farm/utils/harness.js +1 -1
  111. package/dist/agent-farm/utils/message-format.d.ts +67 -3
  112. package/dist/agent-farm/utils/message-format.d.ts.map +1 -1
  113. package/dist/agent-farm/utils/message-format.js +127 -14
  114. package/dist/agent-farm/utils/message-format.js.map +1 -1
  115. package/dist/agent-farm/utils/server-utils.d.ts +86 -4
  116. package/dist/agent-farm/utils/server-utils.d.ts.map +1 -1
  117. package/dist/agent-farm/utils/server-utils.js +303 -5
  118. package/dist/agent-farm/utils/server-utils.js.map +1 -1
  119. package/dist/commands/porch/context-refresh.d.ts +181 -0
  120. package/dist/commands/porch/context-refresh.d.ts.map +1 -0
  121. package/dist/commands/porch/context-refresh.js +270 -0
  122. package/dist/commands/porch/context-refresh.js.map +1 -0
  123. package/dist/commands/porch/index.d.ts +6 -0
  124. package/dist/commands/porch/index.d.ts.map +1 -1
  125. package/dist/commands/porch/index.js +46 -0
  126. package/dist/commands/porch/index.js.map +1 -1
  127. package/dist/commands/porch/next.d.ts +0 -9
  128. package/dist/commands/porch/next.d.ts.map +1 -1
  129. package/dist/commands/porch/next.js +133 -4
  130. package/dist/commands/porch/next.js.map +1 -1
  131. package/dist/commands/porch/protocol.d.ts.map +1 -1
  132. package/dist/commands/porch/protocol.js +102 -0
  133. package/dist/commands/porch/protocol.js.map +1 -1
  134. package/dist/commands/porch/types.d.ts +61 -0
  135. package/dist/commands/porch/types.d.ts.map +1 -1
  136. package/dist/lib/forge-contracts.d.ts +9 -0
  137. package/dist/lib/forge-contracts.d.ts.map +1 -1
  138. package/dist/lib/forge-contracts.js.map +1 -1
  139. package/dist/lib/github.d.ts +15 -1
  140. package/dist/lib/github.d.ts.map +1 -1
  141. package/dist/lib/github.js +17 -0
  142. package/dist/lib/github.js.map +1 -1
  143. package/dist/lib/self-refresh-invocation.d.ts +60 -0
  144. package/dist/lib/self-refresh-invocation.d.ts.map +1 -0
  145. package/dist/lib/self-refresh-invocation.js +64 -0
  146. package/dist/lib/self-refresh-invocation.js.map +1 -0
  147. package/dist/lib/test-env.d.ts +50 -0
  148. package/dist/lib/test-env.d.ts.map +1 -1
  149. package/dist/lib/test-env.js +76 -0
  150. package/dist/lib/test-env.js.map +1 -1
  151. package/dist/terminal/attach-replay.d.ts +39 -0
  152. package/dist/terminal/attach-replay.d.ts.map +1 -0
  153. package/dist/terminal/attach-replay.js +76 -0
  154. package/dist/terminal/attach-replay.js.map +1 -0
  155. package/dist/terminal/pty-manager.d.ts.map +1 -1
  156. package/dist/terminal/pty-manager.js +35 -17
  157. package/dist/terminal/pty-manager.js.map +1 -1
  158. package/dist/terminal/pty-session.d.ts +81 -12
  159. package/dist/terminal/pty-session.d.ts.map +1 -1
  160. package/dist/terminal/pty-session.js +113 -25
  161. package/dist/terminal/pty-session.js.map +1 -1
  162. package/dist/terminal/session-screen.d.ts +25 -0
  163. package/dist/terminal/session-screen.d.ts.map +1 -1
  164. package/dist/terminal/session-screen.js +53 -8
  165. package/dist/terminal/session-screen.js.map +1 -1
  166. package/package.json +10 -7
  167. package/scripts/forge/gitea/pr-view.sh +4 -1
  168. package/scripts/forge/github/pr-view.sh +1 -1
  169. package/scripts/forge/gitlab/pr-view.sh +4 -1
  170. package/skeleton/.claude/skills/afx/SKILL.md +54 -0
  171. package/skeleton/.claude/skills/arch-save/SKILL.md +8 -2
  172. package/skeleton/.claude/skills/builder-refresh/SKILL.md +138 -0
  173. package/skeleton/.codex/skills/afx/SKILL.md +54 -0
  174. package/skeleton/.codex/skills/arch-save/SKILL.md +8 -2
  175. package/skeleton/.codex/skills/builder-refresh/SKILL.md +138 -0
  176. package/skeleton/protocol-schema.json +17 -0
  177. package/skeleton/protocols/air/protocol.json +1 -1
  178. package/skeleton/protocols/aspir/protocol.json +5 -1
  179. package/skeleton/protocols/bugfix/protocol.json +1 -1
  180. package/skeleton/protocols/experiment/protocol.json +1 -1
  181. package/skeleton/protocols/maintain/protocol.json +1 -1
  182. package/skeleton/protocols/pir/builder-prompt.md +3 -2
  183. package/skeleton/protocols/pir/prompts/implement.md +1 -1
  184. package/skeleton/protocols/pir/prompts/plan.md +1 -1
  185. package/skeleton/protocols/pir/prompts/review.md +1 -1
  186. package/skeleton/protocols/pir/protocol.json +1 -1
  187. package/skeleton/protocols/pir/protocol.md +4 -4
  188. package/skeleton/protocols/protocol-schema.json +17 -0
  189. package/skeleton/protocols/research/protocol.json +1 -1
  190. package/skeleton/protocols/spike/protocol.json +1 -1
  191. package/skeleton/protocols/spir/protocol.json +5 -1
  192. package/skeleton/resources/commands/agent-farm.md +14 -11
  193. package/skeleton/roles/builder.md +3 -4
  194. package/templates/3d-viewer.html +32 -4
  195. package/templates/open.html +58 -12
  196. package/templates/tower.html +26 -7
  197. package/templates/vendor/three-3MFLoader.js +1478 -0
  198. package/templates/vendor/three-STLLoader.js +410 -0
  199. package/templates/vendor/three-TrackballControls.js +828 -0
  200. package/templates/vendor/three-fflate.module.js +2474 -0
  201. package/templates/vendor/three.module.js +53044 -0
  202. package/dashboard-dist/assets/index-JCbkpAcV.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "../../protocol-schema.json",
2
+ "$schema": "../protocol-schema.json",
3
3
  "name": "air",
4
4
  "version": "1.0.0",
5
5
  "description": "AIR: Autonomous Implement & Review — lightweight protocol for small features from GitHub Issues",
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "../../protocol-schema.json",
2
+ "$schema": "../protocol-schema.json",
3
3
  "name": "aspir",
4
4
  "version": "1.0.0",
5
5
  "description": "ASPIR: Autonomous SPIR — Specify → Plan → Implement → Review without human approval gates",
@@ -8,6 +8,10 @@
8
8
  "required": false,
9
9
  "default_for": ["--project", "-p"]
10
10
  },
11
+ "context_refresh": {
12
+ "on_enter": ["plan", "implement", "review"],
13
+ "on_plan_phase_advance": true
14
+ },
11
15
  "phases": [
12
16
  {
13
17
  "id": "specify",
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "../../protocol-schema.json",
2
+ "$schema": "../protocol-schema.json",
3
3
  "name": "bugfix",
4
4
  "version": "1.2.0",
5
5
  "description": "Lightweight protocol for minor bugfixes using GitHub Issues",
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "../../protocol-schema.json",
2
+ "$schema": "../protocol-schema.json",
3
3
  "name": "experiment",
4
4
  "version": "1.0.0",
5
5
  "description": "Disciplined experimentation with hypothesis testing",
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "../../protocol-schema.json",
2
+ "$schema": "../protocol-schema.json",
3
3
  "name": "maintain",
4
4
  "alias": "maint",
5
5
  "version": "3.0.0",
@@ -42,8 +42,9 @@ the code directly in the worktree (you see it via `git diff`), typing into your
42
42
  `afx send <your-builder-id>` (queued — check next turn), or a comment on the GitHub issue
43
43
  (re-fetch with `gh issue view <N> --comments`).
44
44
 
45
- Revise, recommit, ask whether more remains. **The gate stays pending until the human runs
46
- `porch approve` — never call it yourself.**
45
+ Revise, recommit, ask whether more remains. **The gate stays pending until the human approves. When
46
+ the architect relays that approval, run `porch approve <id> <gate> --a-human-explicitly-approved-this`
47
+ to record it and continue — never on your own initiative.**
47
48
 
48
49
  ## Resumption After Crash
49
50
 
@@ -140,7 +140,7 @@ Then **stay in the interactive session**. Do not exit. Wait for the user's next
140
140
 
141
141
  - **Don't write `codev/reviews/<id>-<slug>.md` in this phase** — it's the next phase's artifact, with a different shape (retrospective with arch + lessons updates)
142
142
  - Don't add features not in the plan — scope creep is a `BLOCKED` signal, not a free expansion
143
- - Don't run `porch approve` yourself
143
+ - Don't run `porch approve` on your own initiative — only when the architect relays the human's approval
144
144
  - Don't push to the default branch — only to your builder branch
145
145
  - Don't squash commits — let the merge commit preserve history
146
146
  - Don't open the PR yet — that's the `review` phase
@@ -114,7 +114,7 @@ Then **stay in the interactive session**. Do not exit. Wait for the user's next
114
114
  ## What NOT to Do
115
115
 
116
116
  - Don't write code — that's the implement phase
117
- - Don't run `porch approve` yourself — only the human can approve the gate
117
+ - Don't run `porch approve` on your own initiative — only when the architect relays the human's approval
118
118
  - Don't post the plan content as a GitHub issue comment — the plan lives in the file, not the issue thread. A one-line pointer comment on the issue is fine if you think it helps the discussion.
119
119
  - Don't exit the interactive session at the gate
120
120
 
@@ -240,7 +240,7 @@ Together with the `--pr` record from step 4a and the `--merged` record from step
240
240
 
241
241
  - **Don't merge before the `pr` gate is approved** (steps 8–9). Neither a consultation APPROVE verdict nor user-in-pane prose ("looks good", "lgtm", "merge it") authorizes `gh pr merge` — only porch reporting `gate_status: approved` for the `pr` gate does.
242
242
  - Don't skip porch's PR/merge records (steps 4a, 9). The `--pr` record (step 4a) lets the gate-pending state link to the actual PR; the `--merged` record (step 9) closes the lifecycle in porch state. Skipping either leaves `history:` empty and downstream tooling blind.
243
- - Don't run `porch approve` for any gate yourself
243
+ - Don't run `porch approve` for any gate on your own initiative — only when the architect relays the human's approval
244
244
  - Don't push to the default branch — only merge via PR
245
245
  - Don't skip the Architecture Updates / Lessons Learned sections — porch checks enforce their presence (the section must exist; explaining "no changes needed" in one line is fine)
246
246
  - **Don't run `consult` commands yourself** — porch handles consultations via the `verify` block. Manually invoking `consult` causes the consultation to run twice.
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "../../protocol-schema.json",
2
+ "$schema": "../protocol-schema.json",
3
3
  "name": "pir",
4
4
  "alias": "plan-implement-review",
5
5
  "version": "1.0.0",
@@ -32,14 +32,14 @@ protocol is safe and needs no porch change.
32
32
  | `dev-approval` | pre-PR | **PIR's distinctive gate** — reviews the *running* worktree via `afx dev` |
33
33
  | `pr` | post-PR | Reviews on GitHub, then approves; porch wakes the builder to merge |
34
34
 
35
- The `pr` gate makes the merge trigger **structured porch state** rather than free text in the
36
- builder's pane — closing the self-merge class where a builder infers authorization from
37
- ambiguous prose.
35
+ The `pr` gate holds the merge behind porch state: the human approves, the architect relays that
36
+ approval, and the builder records it with `porch approve pr` before porch hands over the merge task.
38
37
 
39
38
  **Gates do not notify the architect automatically.** Porch broadcasts `overview-changed` over
40
39
  SSE; the VSCode Builders tree renders the blocked state with a bell and raises a toast. CLI
41
40
  users see it via the builder pane or `porch pending`. The builder's job at any gate is: write
42
- the artifact, commit, signal, wait — never to invoke `porch approve` itself.
41
+ the artifact, commit, signal, wait — then run `porch approve` when the architect relays the human's
42
+ approval, never on your own initiative.
43
43
 
44
44
  ## Rejection is iteration, not a command
45
45
 
@@ -33,6 +33,23 @@
33
33
  "hooks": {
34
34
  "$ref": "#/definitions/protocolHooks"
35
35
  },
36
+ "context_refresh": {
37
+ "type": "object",
38
+ "description": "Boundaries at which builders automatically refresh their context (Spec 1470). Absent means no refreshes.",
39
+ "additionalProperties": false,
40
+ "properties": {
41
+ "on_enter": {
42
+ "type": "array",
43
+ "description": "Protocol phase ids to refresh on entry to. Each must name a phase this protocol declares.",
44
+ "uniqueItems": true,
45
+ "items": { "type": "string" }
46
+ },
47
+ "on_plan_phase_advance": {
48
+ "type": "boolean",
49
+ "description": "Refresh when advancing from one plan phase to the next. Requires a 'per_plan_phase' phase. Excludes the first plan phase, which coincides with entering the phase itself."
50
+ }
51
+ }
52
+ },
36
53
  "signals": {
37
54
  "type": "object",
38
55
  "description": "Signal definitions for state transitions",
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "../../protocol-schema.json",
2
+ "$schema": "../protocol-schema.json",
3
3
  "name": "research",
4
4
  "version": "1.0.0",
5
5
  "description": "Multi-agent research with 3-way investigation, synthesis, and critique",
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "../../protocol-schema.json",
2
+ "$schema": "../protocol-schema.json",
3
3
  "name": "spike",
4
4
  "version": "1.0.0",
5
5
  "description": "Time-boxed technical feasibility exploration",
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "../../protocol-schema.json",
2
+ "$schema": "../protocol-schema.json",
3
3
  "name": "spir",
4
4
  "alias": "spider",
5
5
  "version": "2.2.0",
@@ -9,6 +9,10 @@
9
9
  "required": false,
10
10
  "default_for": ["--project", "-p"]
11
11
  },
12
+ "context_refresh": {
13
+ "on_enter": ["plan", "implement", "review"],
14
+ "on_plan_phase_advance": true
15
+ },
12
16
  "phases": [
13
17
  {
14
18
  "id": "specify",
@@ -501,12 +501,12 @@ afx send 0042 "That producer died — stop waiting and report."
501
501
 
502
502
  ---
503
503
 
504
- ### afx reset
504
+ ### afx refresh
505
505
 
506
- Reset a builder's context: have it save its working state, clear the conversation, then re-orient it.
506
+ Refresh a builder's context: have it save its working state, clear the conversation, then re-orient it.
507
507
 
508
508
  ```bash
509
- afx reset <builder> [options]
509
+ afx refresh <builder> [options]
510
510
  ```
511
511
 
512
512
  **Arguments:**
@@ -522,10 +522,13 @@ afx reset <builder> [options]
522
522
  - `--min-bytes <n>` - Minimum state-file size to accept as substantive (default 1000)
523
523
  - `--quiet-window <ms>` - Terminal silence that counts as turn-ended (default 1500)
524
524
 
525
+ **Deprecated alias:** `afx reset` still runs this command and prints a one-line notice to stderr.
526
+ It will be removed in a future release — use `afx refresh`.
527
+
525
528
  **Description:**
526
529
 
527
530
  Long-running builders exhaust their context window. `afx spawn --resume` reattaches the *same*
528
- conversation, so a deep session resumes deep — it does not give the builder a fresh window. `afx reset`
531
+ conversation, so a deep session resumes deep — it does not give the builder a fresh window. `afx refresh`
529
532
  does, without losing what the builder knows.
530
533
 
531
534
  The sequence:
@@ -533,7 +536,7 @@ The sequence:
533
536
  1. Assemble the re-orientation and write it to `.builder-reorient.md` in the worktree.
534
537
  2. Ask the builder to write its complete working state to `.builder-state.md`, stamped with a one-time
535
538
  nonce.
536
- 3. Wait for that file and **verify** it: correct nonce (not a stale file from an earlier reset),
539
+ 3. Wait for that file and **verify** it: correct nonce (not a stale file from an earlier refresh),
537
540
  substantive size, and stable across two observations (not still being written).
538
541
  4. Wait for the terminal to fall silent, so the clear is not typed mid-turn. If it does not settle, send
539
542
  **one** ESC and wait again.
@@ -556,19 +559,19 @@ then `afx spawn <id> --resume`).
556
559
 
557
560
  ```bash
558
561
  # See exactly what would be sent, without touching the builder
559
- afx reset 0042 --dry-run
562
+ afx refresh 0042 --dry-run
560
563
 
561
- # Standard reset
562
- afx reset 0042
564
+ # Standard refresh
565
+ afx refresh 0042
563
566
 
564
567
  # Add context that post-dates the builder's saved state
565
- afx reset 0042 --note "PR #90 merged while you were mid-phase. Rebase before continuing."
568
+ afx refresh 0042 --note "PR #90 merged while you were mid-phase. Rebase before continuing."
566
569
 
567
570
  # The builder is wedged mid-turn and not reading messages
568
- afx reset 0042 --interrupt-first
571
+ afx refresh 0042 --interrupt-first
569
572
 
570
573
  # A builder that legitimately needs longer to write its state
571
- afx reset 0042 --timeout 600
574
+ afx refresh 0042 --timeout 600
572
575
  ```
573
576
 
574
577
  ---
@@ -23,9 +23,8 @@ say so, **stop**, and wait.
23
23
  Never treat a porch gate as approved without an explicit human decision — a gate message is a notification to the human, not authorization.
24
24
 
25
25
  Approval reaches you as a message from the architect. Then *you* run
26
- `porch approve <id> <gate>`; the architect does not run it for you — **unless your protocol's
27
- prompts route that command to the human instead** (PIR's gates are typed by the human reviewer,
28
- via Cmd+K G or their own shell). Defer to your protocol's phase prompts on who types `porch approve`.
26
+ `porch approve <id> <gate>`; the architect does not run it for you. Defer to your protocol's phase
27
+ prompts on who types `porch approve`.
29
28
 
30
29
  ## Deliverables
31
30
 
@@ -72,7 +71,7 @@ nobody can redirect, and you will not notice, because from inside it everything
72
71
  Never chain foreground poll loops.
73
72
 
74
73
  If you are wedged anyway, the architect can end your turn with `afx interrupt <your-id>`, or
75
- `afx reset <your-id>` to have you save state and re-orient. Worth knowing so you can suggest
74
+ `afx refresh <your-id>` to have you save state and re-orient. Worth knowing so you can suggest
76
75
  them.
77
76
 
78
77
  ## PRs
@@ -218,12 +218,18 @@
218
218
  </div>
219
219
  </div>
220
220
 
221
- <!-- Three.js ES Modules via import map -->
221
+ <!-- Three.js ES Modules — vendored locally (three@0.160.0), served same-origin
222
+ from the annotator's vendor/ route. NO remote code runs in this page: it is
223
+ key-injected (advisory GHSA-xvjp-7748-v88v), so loading executable modules
224
+ from a CDN would expose window.__CODEV_TOWER_KEY__ to that CDN. -->
222
225
  <script type="importmap">
223
226
  {
224
227
  "imports": {
225
- "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
226
- "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
228
+ "three": "vendor/three.module.js",
229
+ "three/addons/loaders/STLLoader.js": "vendor/three-STLLoader.js",
230
+ "three/addons/loaders/3MFLoader.js": "vendor/three-3MFLoader.js",
231
+ "three/addons/controls/TrackballControls.js": "vendor/three-TrackballControls.js",
232
+ "fflate": "vendor/three-fflate.module.js"
227
233
  }
228
234
  }
229
235
  </script>
@@ -234,6 +240,24 @@
234
240
  import { ThreeMFLoader } from 'three/addons/loaders/3MFLoader.js';
235
241
  import { TrackballControls } from 'three/addons/controls/TrackballControls.js';
236
242
 
243
+ // Request authentication (advisory GHSA-xvjp-7748-v88v): Tower injects the
244
+ // shared local key into this page same-origin; model/mtime requests send it
245
+ // via the codev-tower-key header. Three.js loaders carry it through
246
+ // loader.setRequestHeader(authHeaders()).
247
+ function getWebKey() {
248
+ if (window.__CODEV_TOWER_KEY__) {
249
+ try { localStorage.setItem('codev-tower-key', window.__CODEV_TOWER_KEY__); } catch (e) {}
250
+ return window.__CODEV_TOWER_KEY__;
251
+ }
252
+ return localStorage.getItem('codev-tower-key');
253
+ }
254
+ function authHeaders(extra) {
255
+ const key = getWebKey();
256
+ const headers = extra ? Object.assign({}, extra) : {};
257
+ if (key) headers['codev-tower-key'] = key;
258
+ return headers;
259
+ }
260
+
237
261
  // Configuration (injected by server with proper escaping)
238
262
  const FILE_PATH = {{FILE_PATH_JSON}}; // JSON-encoded by server
239
263
  const FILE_NAME = '{{FILE}}';
@@ -370,6 +394,7 @@
370
394
 
371
395
  function loadSTL() {
372
396
  const loader = new STLLoader();
397
+ loader.setRequestHeader(authHeaders());
373
398
 
374
399
  loader.load(
375
400
  'api/model',
@@ -412,6 +437,7 @@
412
437
 
413
438
  function load3MF() {
414
439
  const loader = new ThreeMFLoader();
440
+ loader.setRequestHeader(authHeaders());
415
441
 
416
442
  loader.load(
417
443
  'api/model',
@@ -715,7 +741,7 @@
715
741
 
716
742
  async function checkForChanges() {
717
743
  try {
718
- const res = await fetch('api/mtime');
744
+ const res = await fetch('api/mtime', { headers: authHeaders() });
719
745
  if (res.ok) {
720
746
  const data = await res.json();
721
747
  if (lastMtime === null) {
@@ -769,6 +795,7 @@
769
795
 
770
796
  function reloadSTL() {
771
797
  const loader = new STLLoader();
798
+ loader.setRequestHeader(authHeaders());
772
799
  loader.load('api/model?t=' + Date.now(), (geometry) => {
773
800
  // Center geometry in XY
774
801
  geometry.computeBoundingBox();
@@ -811,6 +838,7 @@
811
838
 
812
839
  function reload3MF() {
813
840
  const loader = new ThreeMFLoader();
841
+ loader.setRequestHeader(authHeaders());
814
842
  loader.load('api/model?t=' + Date.now(), (group) => {
815
843
  // 3MF uses Z-up natively, which matches our coordinate system
816
844
 
@@ -569,6 +569,51 @@
569
569
  <script src="vendor/prism-markup.min.js"></script>
570
570
 
571
571
  <script>
572
+ // Request authentication (advisory GHSA-xvjp-7748-v88v): Tower injects the
573
+ // shared local key into this page same-origin; every data/media fetch sends
574
+ // it via the codev-tower-key header. Media is fetched as an authenticated blob
575
+ // (img/video/pdf src cannot carry a header) and displayed via an object URL,
576
+ // revoked when replaced or on unload to avoid blob leaks.
577
+ function getWebKey() {
578
+ if (window.__CODEV_TOWER_KEY__) {
579
+ try { localStorage.setItem('codev-tower-key', window.__CODEV_TOWER_KEY__); } catch (e) {}
580
+ return window.__CODEV_TOWER_KEY__;
581
+ }
582
+ return localStorage.getItem('codev-tower-key');
583
+ }
584
+ function authHeaders(extra) {
585
+ const key = getWebKey();
586
+ const headers = extra ? Object.assign({}, extra) : {};
587
+ if (key) headers['codev-tower-key'] = key;
588
+ return headers;
589
+ }
590
+ function setMediaSrc(el, objectUrl) {
591
+ if (el.__objUrl) URL.revokeObjectURL(el.__objUrl);
592
+ el.__objUrl = objectUrl;
593
+ el.src = objectUrl;
594
+ }
595
+ async function loadMedia(el, path) {
596
+ try {
597
+ const res = await fetch(path, { headers: authHeaders() });
598
+ if (!res.ok) throw new Error('status ' + res.status);
599
+ const blob = await res.blob();
600
+ setMediaSrc(el, URL.createObjectURL(blob));
601
+ } catch (e) {
602
+ if (typeof el.onerror === 'function') el.onerror();
603
+ }
604
+ }
605
+ function revokeAllMediaUrls() {
606
+ ['image-display', 'video-display', 'pdf-display'].forEach(function(id) {
607
+ const el = document.getElementById(id);
608
+ if (el && el.__objUrl) URL.revokeObjectURL(el.__objUrl);
609
+ });
610
+ }
611
+ // pagehide fires on mobile / bfcache navigations where beforeunload does not.
612
+ // Skip revocation when the page is going into the bfcache (persisted): the
613
+ // object URLs must survive so restored media still renders on pageshow.
614
+ window.addEventListener('pagehide', function(e) { if (!e.persisted) revokeAllMediaUrls(); });
615
+ window.addEventListener('beforeunload', revokeAllMediaUrls);
616
+
572
617
  // State
573
618
  let fileLines = [];
574
619
  let currentLine = null;
@@ -673,8 +718,8 @@
673
718
  img.style.display = 'none';
674
719
  };
675
720
 
676
- // Add cache-busting query param to allow reload
677
- img.src = 'api/image?t=' + Date.now();
721
+ // Fetch as an authenticated blob (img.src cannot carry the key header).
722
+ loadMedia(img, 'api/image?t=' + Date.now());
678
723
  }
679
724
 
680
725
  // Initialize video viewer
@@ -707,8 +752,8 @@
707
752
  video.style.display = 'none';
708
753
  };
709
754
 
710
- // Add cache-busting query param to allow reload
711
- video.src = 'api/video?t=' + Date.now();
755
+ // Fetch as an authenticated blob (video.src cannot carry the key header).
756
+ loadMedia(video, 'api/video?t=' + Date.now());
712
757
  }
713
758
 
714
759
  // Initialize PDF viewer
@@ -726,9 +771,10 @@
726
771
  const pdfViewer = document.getElementById('pdf-viewer');
727
772
  pdfViewer.style.display = 'block';
728
773
 
729
- // Load PDF via raw endpoint
774
+ // Load PDF via raw endpoint as an authenticated blob (embed.src cannot
775
+ // carry the key header).
730
776
  const embed = document.getElementById('pdf-display');
731
- embed.src = 'api/pdf?t=' + Date.now();
777
+ loadMedia(embed, 'api/pdf?t=' + Date.now());
732
778
  }
733
779
 
734
780
  // Update image info display (in both header and controls)
@@ -1339,7 +1385,7 @@
1339
1385
  try {
1340
1386
  const response = await fetch('save', {
1341
1387
  method: 'POST',
1342
- headers: { 'Content-Type': 'application/json' },
1388
+ headers: authHeaders({ 'Content-Type': 'application/json' }),
1343
1389
  body: JSON.stringify({
1344
1390
  file: filePath,
1345
1391
  content: fileLines.join('\n')
@@ -1457,7 +1503,7 @@
1457
1503
  try {
1458
1504
  const response = await fetch('save', {
1459
1505
  method: 'POST',
1460
- headers: { 'Content-Type': 'application/json' },
1506
+ headers: authHeaders({ 'Content-Type': 'application/json' }),
1461
1507
  body: JSON.stringify({ file: filePath, content })
1462
1508
  });
1463
1509
 
@@ -1778,7 +1824,7 @@
1778
1824
  if (hasUnsavedChanges || editMode) return;
1779
1825
 
1780
1826
  try {
1781
- const res = await fetch('api/mtime');
1827
+ const res = await fetch('api/mtime', { headers: authHeaders() });
1782
1828
  if (res.ok) {
1783
1829
  const data = await res.json();
1784
1830
  if (lastMtime === null) {
@@ -1797,21 +1843,21 @@
1797
1843
  // For images and videos, just reload the source
1798
1844
  if (isImageFile) {
1799
1845
  const img = document.getElementById('image-display');
1800
- img.src = 'api/image?t=' + Date.now();
1846
+ loadMedia(img, 'api/image?t=' + Date.now());
1801
1847
  showNotification('Image reloaded');
1802
1848
  return;
1803
1849
  }
1804
1850
 
1805
1851
  if (isVideoFile) {
1806
1852
  const video = document.getElementById('video-display');
1807
- video.src = 'api/video?t=' + Date.now();
1853
+ loadMedia(video, 'api/video?t=' + Date.now());
1808
1854
  showNotification('Video reloaded');
1809
1855
  return;
1810
1856
  }
1811
1857
 
1812
1858
  // For text files, fetch new content
1813
1859
  try {
1814
- const res = await fetch('file');
1860
+ const res = await fetch('file', { headers: authHeaders() });
1815
1861
  if (res.ok) {
1816
1862
  const content = await res.text();
1817
1863
  currentContent = content;
@@ -8,6 +8,7 @@
8
8
  <meta name="theme-color" content="#252525">
9
9
  <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🗼</text></svg>">
10
10
  <title>Agent Farm Tower</title>
11
+ <!-- CODEV_TOWER_KEY_INJECTION -->
11
12
  <style>
12
13
  * {
13
14
  box-sizing: border-box;
@@ -989,21 +990,39 @@
989
990
  let runningInstances = [];
990
991
  let recentInstances = [];
991
992
 
992
- // Auth helper: get headers with auth token if available
993
+ // Auth helper: resolve the shared local key. Prefer the value Tower injects
994
+ // same-origin at serve time (window.__CODEV_TOWER_KEY__); fall back to any
995
+ // previously stored value. The injected value is persisted so later loads
996
+ // and manual navigations keep working.
997
+ function getWebKey() {
998
+ if (window.__CODEV_TOWER_KEY__) {
999
+ try { localStorage.setItem('codev-tower-key', window.__CODEV_TOWER_KEY__); } catch (e) {}
1000
+ return window.__CODEV_TOWER_KEY__;
1001
+ }
1002
+ return localStorage.getItem('codev-tower-key');
1003
+ }
1004
+
1005
+ // Auth helper: get headers with the auth key if available
993
1006
  function getAuthHeaders() {
994
- const key = localStorage.getItem('codev-web-key');
995
- return key ? { 'Authorization': `Bearer ${key}` } : {};
1007
+ const key = getWebKey();
1008
+ return key ? { 'codev-tower-key': key } : {};
996
1009
  }
997
1010
 
998
1011
  // Auth helper: make authenticated fetch request
999
1012
  async function authFetch(url, options = {}) {
1013
+ const hadKey = !!getWebKey();
1000
1014
  const headers = { ...getAuthHeaders(), ...(options.headers || {}) };
1001
1015
  const response = await fetch(url, { ...options, headers });
1002
1016
 
1003
- // If 401, clear key and redirect to login
1017
+ // If 401, clear a now-stale key. Only reload when we actually sent a key:
1018
+ // a reload re-fetches the shell and picks up a freshly injected key. With
1019
+ // no key to begin with, the shell has none to inject, so reloading would
1020
+ // loop forever (advisory GHSA-xvjp-7748-v88v) — surface the error instead.
1004
1021
  if (response.status === 401) {
1005
- localStorage.removeItem('codev-web-key');
1006
- location.reload();
1022
+ localStorage.removeItem('codev-tower-key');
1023
+ if (hadKey) {
1024
+ location.reload();
1025
+ }
1007
1026
  throw new Error('Unauthorized');
1008
1027
  }
1009
1028
  return response;
@@ -1011,7 +1030,7 @@
1011
1030
 
1012
1031
  // Logout function
1013
1032
  function logout() {
1014
- localStorage.removeItem('codev-web-key');
1033
+ localStorage.removeItem('codev-tower-key');
1015
1034
  window.location.href = './';
1016
1035
  }
1017
1036