switchroom 0.19.25 → 0.19.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/git-agent-attribution-hook.sh +144 -0
- package/dist/agent-scheduler/index.js +61 -2
- package/dist/auth-broker/index.js +125 -8
- package/dist/cli/notion-write-pretool.mjs +61 -2
- package/dist/cli/switchroom.js +2347 -1104
- package/dist/host-control/main.js +126 -9
- package/dist/vault/approvals/kernel-server.js +124 -8
- package/dist/vault/broker/server.js +124 -8
- package/package.json +6 -2
- package/profiles/_base/cron-session.sh.hbs +14 -0
- package/profiles/_base/start.sh.hbs +145 -4
- package/telegram-plugin/card-layout.ts +328 -0
- package/telegram-plugin/dist/bridge/bridge.js +93 -1
- package/telegram-plugin/dist/gateway/gateway.js +2213 -1204
- package/telegram-plugin/dist/server.js +96 -1
- package/telegram-plugin/edit-flood-fuse.ts +637 -56
- package/telegram-plugin/flood-429-ledger.ts +526 -0
- package/telegram-plugin/flood-circuit-breaker.ts +18 -0
- package/telegram-plugin/gateway/flood-reply-queue.ts +168 -0
- package/telegram-plugin/gateway/gateway.ts +103 -112
- package/telegram-plugin/gateway/narrative-lane.ts +14 -0
- package/telegram-plugin/gateway/outbound-send-path.ts +36 -0
- package/telegram-plugin/gateway/outbox-sweep.ts +183 -6
- package/telegram-plugin/gateway/periodic-sweep-guard.ts +86 -0
- package/telegram-plugin/gateway/pinned-message-handler.ts +12 -16
- package/telegram-plugin/gateway/status-pin-retarget.ts +180 -0
- package/telegram-plugin/gateway/status-pin-store.ts +58 -9
- package/telegram-plugin/gateway/worker-pin-reaper.ts +56 -7
- package/telegram-plugin/llm-error-present.ts +61 -2
- package/telegram-plugin/model-unavailable.ts +8 -0
- package/telegram-plugin/operator-events.ts +72 -5
- package/telegram-plugin/outbound-class.ts +81 -0
- package/telegram-plugin/provider-credit.ts +237 -0
- package/telegram-plugin/scripts/bun-test-ci.sh +36 -6
- package/telegram-plugin/send-gate.ts +24 -2
- package/telegram-plugin/status-no-truncate.ts +11 -0
- package/telegram-plugin/status-pin-driver.ts +33 -17
- package/telegram-plugin/status-pin.ts +51 -5
- package/telegram-plugin/tests/card-golden.test.ts +69 -0
- package/telegram-plugin/tests/card-lifecycle-render.test.ts +362 -0
- package/telegram-plugin/tests/card-type-distinguishability.test.ts +291 -0
- package/telegram-plugin/tests/card-variants.golden.txt +211 -0
- package/telegram-plugin/tests/card-variants.ts +366 -0
- package/telegram-plugin/tests/edit-flood-fuse-ban-awareness.test.ts +316 -0
- package/telegram-plugin/tests/edit-flood-fuse-default-deny.test.ts +319 -0
- package/telegram-plugin/tests/edit-flood-fuse.test.ts +11 -2
- package/telegram-plugin/tests/feed-edit-rate-ceiling.test.ts +462 -0
- package/telegram-plugin/tests/fixtures/real-429-stream.ts +220 -0
- package/telegram-plugin/tests/flood-429-ledger.test.ts +278 -0
- package/telegram-plugin/tests/flood-429-recorder-wiring.test.ts +128 -0
- package/telegram-plugin/tests/flood-reply-queue.test.ts +418 -0
- package/telegram-plugin/tests/outbox-sweep-flood-breaker.test.ts +221 -0
- package/telegram-plugin/tests/periodic-sweep-guard.test.ts +151 -0
- package/telegram-plugin/tests/pinned-card-collapse.test.ts +24 -18
- package/telegram-plugin/tests/pinned-message-handler.test.ts +15 -15
- package/telegram-plugin/tests/provider-credit-402.test.ts +243 -0
- package/telegram-plugin/tests/status-pin-api.test.ts +11 -11
- package/telegram-plugin/tests/status-pin-boot-recovery.test.ts +36 -37
- package/telegram-plugin/tests/status-pin-lifecycle.test.ts +602 -0
- package/telegram-plugin/tests/status-pin-retarget.test.ts +244 -0
- package/telegram-plugin/tests/status-pin-service-message-suppression.test.ts +7 -3
- package/telegram-plugin/tests/status-pin-shutdown-wiring.test.ts +94 -0
- package/telegram-plugin/tests/status-pin-store.test.ts +179 -64
- package/telegram-plugin/tests/status-pin.test.ts +184 -7
- package/telegram-plugin/tests/test-runner-coverage.test.ts +133 -0
- package/telegram-plugin/tests/worker-activity-feed.test.ts +12 -10
- package/telegram-plugin/tests/worker-feed-coalesce.test.ts +29 -19
- package/telegram-plugin/tests/worker-feed-pin-persistence.test.ts +56 -59
- package/telegram-plugin/tests/worker-feed-terminal-edit-class.test.ts +335 -0
- package/telegram-plugin/tests/worker-visibility-prose-silent-harness.test.ts +1 -1
- package/telegram-plugin/tier-downgrade.ts +3 -2
- package/telegram-plugin/tool-activity-summary.ts +239 -322
- package/telegram-plugin/uat/assertions.ts +33 -3
- package/telegram-plugin/uat/feed-matcher.test.ts +36 -0
- package/telegram-plugin/uat/scenarios/jtbd-liveness-narration-channel.test.ts +9 -2
- package/telegram-plugin/uat/scenarios/jtbd-liveness-narration-dm.test.ts +9 -2
- package/telegram-plugin/worker-activity-feed.ts +109 -30
- package/vendor/hindsight-memory/CLAUDE.md +45 -0
- package/vendor/hindsight-memory/scripts/lib/config.py +33 -0
- package/vendor/hindsight-memory/scripts/recall.py +176 -7
- package/vendor/hindsight-memory/scripts/tests/test_config_recall_passthrough_env.py +170 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_min_score.py +464 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_request_timeout.py +241 -0
- package/vendor/hindsight-memory/settings.json +1 -1
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
|
|
2
|
+
===== CARD ===== Agent card — running
|
|
3
|
+
🤖 **Agent**
|
|
4
|
+
_1m35s · 12 tools · 41.2k tok · opus 4.8_
|
|
5
|
+
~~_✓ Searching memory for card render paths_~~
|
|
6
|
+
~~_✓ Reading telegram-plugin/tool-activity-summary.ts_~~
|
|
7
|
+
~~_✓ Reading telegram-plugin/worker-activity-feed.ts_~~
|
|
8
|
+
~~_✓ Grepping for renderStatusCard callers_~~
|
|
9
|
+
**→ Drafting the inventory table**
|
|
10
|
+
|
|
11
|
+
===== CARD ===== Agent card — final (turn complete)
|
|
12
|
+
🤖 **Agent**
|
|
13
|
+
_done · 12 tools · 41.2k tok · 3m34s · opus 4.8_
|
|
14
|
+
~~_✓ Searching memory for card render paths_~~
|
|
15
|
+
~~_✓ Reading telegram-plugin/tool-activity-summary.ts_~~
|
|
16
|
+
~~_✓ Reading telegram-plugin/worker-activity-feed.ts_~~
|
|
17
|
+
~~_✓ Grepping for renderStatusCard callers_~~
|
|
18
|
+
~~_✓ Drafting the inventory table_~~
|
|
19
|
+
_✓ 17 steps_
|
|
20
|
+
|
|
21
|
+
===== CARD ===== Agent card — steps only (no header)
|
|
22
|
+
~~_✓ Searching memory for card render paths_~~
|
|
23
|
+
~~_✓ Reading telegram-plugin/tool-activity-summary.ts_~~
|
|
24
|
+
**→ Reading telegram-plugin/worker-activity-feed.ts**
|
|
25
|
+
|
|
26
|
+
===== CARD ===== Agent card — with nested foreground sub-agent
|
|
27
|
+
🤖 **Agent**
|
|
28
|
+
_1m35s · 12 tools · 41.2k tok · opus 4.8_
|
|
29
|
+
~~_✓ Searching memory for card render paths_~~
|
|
30
|
+
~~_✓ Reading telegram-plugin/tool-activity-summary.ts_~~
|
|
31
|
+
~~_✓ Reading telegram-plugin/worker-activity-feed.ts_~~
|
|
32
|
+
↳ ~~_Reading the repo conventions_~~
|
|
33
|
+
↳ ~~_Running the scoped test suite_~~
|
|
34
|
+
↳ **→ Summarising findings · 24s**
|
|
35
|
+
|
|
36
|
+
===== CARD ===== Agent card — liveness early-open (no tools yet)
|
|
37
|
+
🤖 **Agent**
|
|
38
|
+
_32s · 0 tools · opus 4.8_
|
|
39
|
+
**→ Working… · 32s**
|
|
40
|
+
|
|
41
|
+
===== CARD ===== Agent card — liveness finalised
|
|
42
|
+
🤖 **Agent**
|
|
43
|
+
_done · 0 tools · 1m28s · opus 4.8_
|
|
44
|
+
~~_✓ Working…_~~
|
|
45
|
+
|
|
46
|
+
===== CARD ===== Agent card — post-answer background activity
|
|
47
|
+
🤖 **Agent**
|
|
48
|
+
_2m10s · 3 tools · opus 4.8_
|
|
49
|
+
**→ Working in background… · 1m10s**
|
|
50
|
+
|
|
51
|
+
===== CARD ===== Agent card — over-long steps (per-line clip)
|
|
52
|
+
🤖 **Agent**
|
|
53
|
+
_1m35s · 12 tools · 41.2k tok · opus 4.8_
|
|
54
|
+
~~_✓ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA…_~~
|
|
55
|
+
~~_✓ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA…_~~
|
|
56
|
+
~~_✓ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA…_~~
|
|
57
|
+
**→ Finally a short newest step**
|
|
58
|
+
|
|
59
|
+
===== CARD ===== Worker card — just dispatched (no steps yet)
|
|
60
|
+
🛠 **WORKER** · _Audit every card render path and de-indent the worker card_
|
|
61
|
+
_1s · 0 tools · 88.4k tok · opus 4.8_
|
|
62
|
+
_starting…_
|
|
63
|
+
|
|
64
|
+
===== CARD ===== Worker card — single worker running
|
|
65
|
+
🛠 **WORKER** · _Audit every card render path and de-indent the worker card_
|
|
66
|
+
_2m23s · 31 tools · 88.4k tok · opus 4.8_
|
|
67
|
+
~~_✓ Fetching origin and branching off main_~~
|
|
68
|
+
~~_✓ Reading telegram-plugin/status-no-truncate.ts_~~
|
|
69
|
+
~~_✓ Mapping renderStatusCard callers_~~
|
|
70
|
+
**→ Writing the preview harness · 18s**
|
|
71
|
+
|
|
72
|
+
===== CARD ===== Worker card — done (with result recap)
|
|
73
|
+
🛠 **WORKER** · _Audit every card render path and de-indent the worker card_
|
|
74
|
+
_done · 31 tools · 88.4k tok · 15m02s · opus 4.8_
|
|
75
|
+
~~_✓ Fetching origin and branching off main_~~
|
|
76
|
+
~~_✓ Reading telegram-plugin/status-no-truncate.ts_~~
|
|
77
|
+
~~_✓ Mapping renderStatusCard callers_~~
|
|
78
|
+
~~_✓ Writing the preview harness_~~
|
|
79
|
+
─────
|
|
80
|
+
✅ _Inventoried 14 card variants, found renderCombinedWorkerFeed forks the header + budget logic, and de-indented the worker card. PR #0000._
|
|
81
|
+
|
|
82
|
+
===== CARD ===== Worker card — failed
|
|
83
|
+
🛠 **WORKER** · _Audit every card render path and de-indent the worker card_
|
|
84
|
+
_failed · 31 tools · 88.4k tok · 1m01s · opus 4.8_
|
|
85
|
+
~~_✓ Fetching origin and branching off main_~~
|
|
86
|
+
~~_✓ Reading telegram-plugin/status-no-truncate.ts_~~
|
|
87
|
+
~~_✓ Mapping renderStatusCard callers_~~
|
|
88
|
+
~~_✓ Writing the preview harness_~~
|
|
89
|
+
─────
|
|
90
|
+
⚠️ _Scoped test run failed: 3 assertions in card-type-distinguishability.test.ts._
|
|
91
|
+
|
|
92
|
+
===== CARD ===== Worker card — incomplete (killed / timed out)
|
|
93
|
+
🛠 **WORKER** · _Audit every card render path and de-indent the worker card_
|
|
94
|
+
_incomplete · 31 tools · 88.4k tok · 30m05s · opus 4.8_
|
|
95
|
+
~~_✓ Fetching origin and branching off main_~~
|
|
96
|
+
~~_✓ Reading telegram-plugin/status-no-truncate.ts_~~
|
|
97
|
+
~~_✓ Mapping renderStatusCard callers_~~
|
|
98
|
+
~~_✓ Writing the preview harness_~~
|
|
99
|
+
|
|
100
|
+
===== CARD ===== WORKERS card — 2 workers
|
|
101
|
+
🛠 **WORKERS** · _2 running · oldest 2m15s · 21 tools · 60.0k tok_
|
|
102
|
+
**1. Audit every card render path** _· 1m15s · 7 tools · 20.0k tok · opus 4.8_
|
|
103
|
+
⠀⠀⠀~~_✓ audit step 1_~~
|
|
104
|
+
⠀⠀⠀~~_✓ audit step 2_~~
|
|
105
|
+
⠀⠀⠀~~_✓ audit step 3_~~
|
|
106
|
+
⠀⠀⠀~~_✓ audit step 4_~~
|
|
107
|
+
⠀⠀⠀**→ audit step 5**
|
|
108
|
+
**2. Fix the hindsight recall floor** _· 2m15s · 14 tools · 40.0k tok · opus 4.8_
|
|
109
|
+
⠀⠀⠀~~_✓ recall step 1_~~
|
|
110
|
+
⠀⠀⠀~~_✓ recall step 2_~~
|
|
111
|
+
⠀⠀⠀~~_✓ recall step 3_~~
|
|
112
|
+
⠀⠀⠀~~_✓ recall step 4_~~
|
|
113
|
+
⠀⠀⠀**→ recall step 5**
|
|
114
|
+
|
|
115
|
+
===== CARD ===== WORKERS card — 4 workers
|
|
116
|
+
🛠 **WORKERS** · _4 running · oldest 4m15s · 70 tools · 200.0k tok_
|
|
117
|
+
**1. Audit every card render path** _· 1m15s · 7 tools · 20.0k tok · opus 4.8_
|
|
118
|
+
⠀⠀⠀~~_✓ audit step 2_~~
|
|
119
|
+
⠀⠀⠀~~_✓ audit step 3_~~
|
|
120
|
+
⠀⠀⠀**→ audit step 4**
|
|
121
|
+
**2. Fix the hindsight recall floor** _· 2m15s · 14 tools · 40.0k tok · opus 4.8_
|
|
122
|
+
⠀⠀⠀~~_✓ recall step 2_~~
|
|
123
|
+
⠀⠀⠀~~_✓ recall step 3_~~
|
|
124
|
+
⠀⠀⠀**→ recall step 4**
|
|
125
|
+
**3. Release v0.19.24** _· 3m15s · 21 tools · 60.0k tok · opus 4.8_
|
|
126
|
+
⠀⠀⠀~~_✓ release step 2_~~
|
|
127
|
+
⠀⠀⠀~~_✓ release step 3_~~
|
|
128
|
+
⠀⠀⠀**→ release step 4**
|
|
129
|
+
**4. Investigate the flaky UAT scenario** _· 4m15s · 28 tools · 80.0k tok · opus 4.8_
|
|
130
|
+
⠀⠀⠀~~_✓ uat step 2_~~
|
|
131
|
+
⠀⠀⠀~~_✓ uat step 3_~~
|
|
132
|
+
⠀⠀⠀**→ uat step 4**
|
|
133
|
+
|
|
134
|
+
===== CARD ===== WORKERS card — 8 workers (spill)
|
|
135
|
+
🛠 **WORKERS** · _8 running · oldest 8m15s · 252 tools · 720.0k tok_
|
|
136
|
+
**1. Background task number 1** _· 1m15s · 7 tools · 20.0k tok · opus 4.8_
|
|
137
|
+
⠀⠀⠀~~_✓ t1 step 1_~~
|
|
138
|
+
⠀⠀⠀~~_✓ t1 step 2_~~
|
|
139
|
+
⠀⠀⠀**→ t1 step 3**
|
|
140
|
+
**2. Background task number 2** _· 2m15s · 14 tools · 40.0k tok · opus 4.8_
|
|
141
|
+
⠀⠀⠀~~_✓ t2 step 1_~~
|
|
142
|
+
⠀⠀⠀~~_✓ t2 step 2_~~
|
|
143
|
+
⠀⠀⠀**→ t2 step 3**
|
|
144
|
+
**3. Background task number 3** _· 3m15s · 21 tools · 60.0k tok · opus 4.8_
|
|
145
|
+
⠀⠀⠀~~_✓ t3 step 1_~~
|
|
146
|
+
⠀⠀⠀~~_✓ t3 step 2_~~
|
|
147
|
+
⠀⠀⠀**→ t3 step 3**
|
|
148
|
+
**4. Background task number 4** _· 4m15s · 28 tools · 80.0k tok · opus 4.8_
|
|
149
|
+
⠀⠀⠀~~_✓ t4 step 1_~~
|
|
150
|
+
⠀⠀⠀~~_✓ t4 step 2_~~
|
|
151
|
+
⠀⠀⠀**→ t4 step 3**
|
|
152
|
+
**5. Background task number 5** _· 5m15s · 35 tools · 100.0k tok · opus 4.8_
|
|
153
|
+
⠀⠀⠀~~_✓ t5 step 1_~~
|
|
154
|
+
⠀⠀⠀~~_✓ t5 step 2_~~
|
|
155
|
+
⠀⠀⠀**→ t5 step 3**
|
|
156
|
+
**6. Background task number 6** _· 6m15s · 42 tools · 120.0k tok · opus 4.8_
|
|
157
|
+
⠀⠀⠀~~_✓ t6 step 1_~~
|
|
158
|
+
⠀⠀⠀~~_✓ t6 step 2_~~
|
|
159
|
+
⠀⠀⠀**→ t6 step 3**
|
|
160
|
+
_+2 more working…_
|
|
161
|
+
|
|
162
|
+
===== CARD ===== WORKERS card — survivors keep their ordinals
|
|
163
|
+
🛠 **WORKERS** · _2 running · oldest 3m15s · 35 tools · 100.0k tok_
|
|
164
|
+
**2. Fix the hindsight recall floor** _· 2m15s · 14 tools · 40.0k tok · opus 4.8_
|
|
165
|
+
⠀⠀⠀~~_✓ recall step 1_~~
|
|
166
|
+
⠀⠀⠀~~_✓ recall step 2_~~
|
|
167
|
+
⠀⠀⠀~~_✓ recall step 3_~~
|
|
168
|
+
⠀⠀⠀**→ recall step 4**
|
|
169
|
+
**3. Release v0.19.24** _· 3m15s · 21 tools · 60.0k tok · opus 4.8_
|
|
170
|
+
⠀⠀⠀~~_✓ release step 1_~~
|
|
171
|
+
⠀⠀⠀~~_✓ release step 2_~~
|
|
172
|
+
⠀⠀⠀~~_✓ release step 3_~~
|
|
173
|
+
⠀⠀⠀**→ release step 4**
|
|
174
|
+
|
|
175
|
+
===== CARD ===== WORKERS card — a worker with no history yet
|
|
176
|
+
🛠 **WORKERS** · _2 running · oldest 2m15s · 21 tools · 60.0k tok_
|
|
177
|
+
**1. Audit every card render path** _· 1m15s · 7 tools · 20.0k tok · opus 4.8_
|
|
178
|
+
⠀⠀⠀~~_✓ audit step 1_~~
|
|
179
|
+
⠀⠀⠀~~_✓ audit step 2_~~
|
|
180
|
+
⠀⠀⠀~~_✓ audit step 3_~~
|
|
181
|
+
⠀⠀⠀~~_✓ audit step 4_~~
|
|
182
|
+
⠀⠀⠀**→ audit step 5**
|
|
183
|
+
**2. Freshly dispatched worker** _· 2m15s · 14 tools · 40.0k tok · opus 4.8_
|
|
184
|
+
⠀⠀⠀→ _starting…_
|
|
185
|
+
|
|
186
|
+
===== CARD ===== Worker card — superseded (group message rotated)
|
|
187
|
+
🛠 **WORKER** · _continued_
|
|
188
|
+
|
|
189
|
+
_Live progress moved to a fresh card to stay pinned._
|
|
190
|
+
|
|
191
|
+
===== CARD ===== Agent card — rolling-window overflow
|
|
192
|
+
🤖 **Agent**
|
|
193
|
+
_1m35s · 12 tools · 41.2k tok · opus 4.8_
|
|
194
|
+
_✓ +9 earlier…_
|
|
195
|
+
~~_✓ Doing thing number 10_~~
|
|
196
|
+
~~_✓ Doing thing number 11_~~
|
|
197
|
+
~~_✓ Doing thing number 12_~~
|
|
198
|
+
~~_✓ Doing thing number 13_~~
|
|
199
|
+
**→ Doing thing number 14**
|
|
200
|
+
|
|
201
|
+
===== CARD ===== Agent card — nested sub-agent with child overflow
|
|
202
|
+
🤖 **Agent**
|
|
203
|
+
_1m35s · 12 tools · 41.2k tok · opus 4.8_
|
|
204
|
+
~~_✓ Searching memory for card render paths_~~
|
|
205
|
+
~~_✓ Reading telegram-plugin/tool-activity-summary.ts_~~
|
|
206
|
+
↳ _+6 earlier…_
|
|
207
|
+
↳ ~~_Child step 7_~~
|
|
208
|
+
↳ ~~_Child step 8_~~
|
|
209
|
+
↳ ~~_Child step 9_~~
|
|
210
|
+
↳ ~~_Child step 10_~~
|
|
211
|
+
↳ **→ Child step 11**
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The catalogue of EVERY status/progress card variant the gateway can paint,
|
|
3
|
+
* each rendered through the REAL renderer with realistic inputs.
|
|
4
|
+
*
|
|
5
|
+
* One catalogue, two consumers, so they can never disagree:
|
|
6
|
+
* - `telegram-plugin/tests/card-golden.test.ts` — the golden suite. Every
|
|
7
|
+
* variant's exact wire text is pinned in `card-variants.golden.txt`, so a
|
|
8
|
+
* change to the shared card layout that alters ANY card fails loudly.
|
|
9
|
+
* - `scripts/card-previews.ts` — the human-readable preview doc.
|
|
10
|
+
*
|
|
11
|
+
* Adding a card variant means adding it HERE (and regenerating the golden with
|
|
12
|
+
* `UPDATE_CARD_GOLDEN=1 npx vitest run telegram-plugin/tests/card-golden.test.ts`),
|
|
13
|
+
* which is what keeps the golden suite exhaustive rather than a sample.
|
|
14
|
+
*
|
|
15
|
+
* These are RENDERER-level fixtures. The lifecycle behaviour that decides WHICH
|
|
16
|
+
* variant gets painted (dispatch → running → 2 workers → spill → done → failed
|
|
17
|
+
* → superseded) is covered end-to-end from the feed manager in
|
|
18
|
+
* `card-lifecycle-render.test.ts`; the two suites are complementary, not
|
|
19
|
+
* duplicates.
|
|
20
|
+
*/
|
|
21
|
+
import {
|
|
22
|
+
renderActivityFeed,
|
|
23
|
+
renderActivityFeedWithNested,
|
|
24
|
+
renderCombinedWorkerFeed,
|
|
25
|
+
type CombinedWorkerRow,
|
|
26
|
+
type SessionActivityHeader,
|
|
27
|
+
} from '../tool-activity-summary.js'
|
|
28
|
+
import {
|
|
29
|
+
renderWorkerActivity,
|
|
30
|
+
WORKER_CARD_SUPERSEDED_BODY,
|
|
31
|
+
type WorkerActivityView,
|
|
32
|
+
} from '../worker-activity-feed.js'
|
|
33
|
+
|
|
34
|
+
/** Which renderer path a variant exercises, for the preview doc's inventory. */
|
|
35
|
+
export interface CardVariant {
|
|
36
|
+
/** Stable identity — the golden file keys on this, so renaming rewrites it. */
|
|
37
|
+
name: string
|
|
38
|
+
/** The call path, for the preview doc. */
|
|
39
|
+
fn: string
|
|
40
|
+
/** When a user actually sees this card. */
|
|
41
|
+
when: string
|
|
42
|
+
/**
|
|
43
|
+
* How the variant reaches the wire:
|
|
44
|
+
* - `shared` — composed by the one card layout core (`card-layout.ts`).
|
|
45
|
+
* - `constant` — a static notice with no live state to render (see the
|
|
46
|
+
* superseded card); its title line still comes from the shared composer.
|
|
47
|
+
*/
|
|
48
|
+
layout?: 'shared' | 'constant'
|
|
49
|
+
/** The real render. `null` means the renderer declined to emit anything. */
|
|
50
|
+
render: () => string | null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const agentHeader = (over: Partial<SessionActivityHeader> = {}): SessionActivityHeader => ({
|
|
54
|
+
label: 'Agent',
|
|
55
|
+
elapsedMs: 95_000,
|
|
56
|
+
toolCount: 12,
|
|
57
|
+
state: 'running',
|
|
58
|
+
model: 'claude-opus-4-8',
|
|
59
|
+
totalTokens: 41_200,
|
|
60
|
+
...over,
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const AGENT_STEPS = [
|
|
64
|
+
'Searching memory for card render paths',
|
|
65
|
+
'Reading telegram-plugin/tool-activity-summary.ts',
|
|
66
|
+
'Reading telegram-plugin/worker-activity-feed.ts',
|
|
67
|
+
'Grepping for renderStatusCard callers',
|
|
68
|
+
'Drafting the inventory table',
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
const workerView = (over: Partial<WorkerActivityView> = {}): WorkerActivityView => ({
|
|
72
|
+
description: 'Audit every card render path and de-indent the worker card',
|
|
73
|
+
state: 'running',
|
|
74
|
+
elapsedMs: 143_000,
|
|
75
|
+
toolCount: 31,
|
|
76
|
+
lastTool: null,
|
|
77
|
+
latestSummary: '',
|
|
78
|
+
narrativeLines: [
|
|
79
|
+
'Fetching origin and branching off main',
|
|
80
|
+
'Reading telegram-plugin/status-no-truncate.ts',
|
|
81
|
+
'Mapping renderStatusCard callers',
|
|
82
|
+
'Writing the preview harness',
|
|
83
|
+
],
|
|
84
|
+
model: 'claude-opus-4-8',
|
|
85
|
+
totalTokens: 88_400,
|
|
86
|
+
...over,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const row = (
|
|
90
|
+
n: number,
|
|
91
|
+
desc: string,
|
|
92
|
+
hist: string[],
|
|
93
|
+
over: Partial<CombinedWorkerRow> = {},
|
|
94
|
+
): CombinedWorkerRow => ({
|
|
95
|
+
ordinal: n,
|
|
96
|
+
description: desc,
|
|
97
|
+
elapsedMs: 60_000 * n + 15_000,
|
|
98
|
+
toolCount: 7 * n,
|
|
99
|
+
totalTokens: 20_000 * n,
|
|
100
|
+
currentStep: hist[hist.length - 1] ?? 'starting…',
|
|
101
|
+
historyLines: hist,
|
|
102
|
+
model: 'claude-opus-4-8',
|
|
103
|
+
...over,
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const H = (k: number, tag: string): string[] =>
|
|
107
|
+
Array.from({ length: k }, (_, i) => `${tag} step ${i + 1}`)
|
|
108
|
+
|
|
109
|
+
/** A 700-char step — long enough to blow the per-line clip and force the backstop. */
|
|
110
|
+
const LONG = 'A'.repeat(700)
|
|
111
|
+
|
|
112
|
+
export const CARD_VARIANTS: CardVariant[] = [
|
|
113
|
+
{
|
|
114
|
+
name: 'Agent card — running',
|
|
115
|
+
fn: 'renderActivityFeed → renderStatusCard',
|
|
116
|
+
when: 'Every turn that labels a tool. Pinned in the session chat, edited in place.',
|
|
117
|
+
render: () => renderActivityFeed(AGENT_STEPS, false, '', undefined, agentHeader()),
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'Agent card — final (turn complete)',
|
|
121
|
+
fn: 'renderActivityFeed → renderStatusCard',
|
|
122
|
+
when: 'The terminal edit when the turn ends: every step struck, `✓ N steps` footer.',
|
|
123
|
+
render: () =>
|
|
124
|
+
renderActivityFeed(AGENT_STEPS, true, '', 17, agentHeader({ state: 'done', elapsedMs: 214_000 })),
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: 'Agent card — steps only (no header)',
|
|
128
|
+
fn: 'renderActivityFeed → renderStatusCard (header omitted)',
|
|
129
|
+
when: 'Legacy/direct callers (`appendActivityLine`, `appendActivityLabel`) that pass no header.',
|
|
130
|
+
render: () => renderActivityFeed(AGENT_STEPS.slice(0, 3)),
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: 'Agent card — with nested foreground sub-agent',
|
|
134
|
+
fn: 'renderActivityFeedWithNested → renderStatusCard (childSteps)',
|
|
135
|
+
when: 'A foreground Task/Agent runs inside the turn: parent lines all struck, the live `→` sits in the `↳` child block.',
|
|
136
|
+
render: () =>
|
|
137
|
+
renderActivityFeedWithNested(
|
|
138
|
+
AGENT_STEPS.slice(0, 3),
|
|
139
|
+
['Reading the repo conventions', 'Running the scoped test suite', 'Summarising findings'],
|
|
140
|
+
false,
|
|
141
|
+
' · 24s',
|
|
142
|
+
undefined,
|
|
143
|
+
agentHeader(),
|
|
144
|
+
),
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: 'Agent card — liveness early-open (no tools yet)',
|
|
148
|
+
fn: 'narrative-lane.ts → renderActivityFeedWithNested → renderStatusCard',
|
|
149
|
+
when: 'A silent turn passes the liveness threshold before any tool is labelled — the card opens on `Working…`.',
|
|
150
|
+
render: () =>
|
|
151
|
+
renderActivityFeedWithNested(['Working…'], [], false, ' · 32s', undefined, {
|
|
152
|
+
label: 'Agent',
|
|
153
|
+
elapsedMs: 32_000,
|
|
154
|
+
toolCount: 0,
|
|
155
|
+
state: 'running',
|
|
156
|
+
model: 'claude-opus-4-8',
|
|
157
|
+
}),
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'Agent card — liveness finalised',
|
|
161
|
+
fn: 'narrative-lane.ts → renderActivityFeedWithNested → renderStatusCard',
|
|
162
|
+
when: 'The liveness-only card at turn end: `→ Working…` becomes a done record instead of freezing live.',
|
|
163
|
+
render: () =>
|
|
164
|
+
renderActivityFeedWithNested(['Working…'], [], true, '', undefined, {
|
|
165
|
+
label: 'Agent',
|
|
166
|
+
elapsedMs: 88_000,
|
|
167
|
+
toolCount: 0,
|
|
168
|
+
state: 'done',
|
|
169
|
+
model: 'claude-opus-4-8',
|
|
170
|
+
}),
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: 'Agent card — post-answer background activity',
|
|
174
|
+
fn: 'narrative-lane.ts → renderActivityFeedWithNested → renderStatusCard',
|
|
175
|
+
when: 'The reply already went out and a background sub-agent is still running — the card surfaces below the reply.',
|
|
176
|
+
render: () =>
|
|
177
|
+
renderActivityFeedWithNested(['Working in background…'], [], false, ' · 1m10s', undefined, {
|
|
178
|
+
label: 'Agent',
|
|
179
|
+
elapsedMs: 130_000,
|
|
180
|
+
toolCount: 3,
|
|
181
|
+
state: 'running',
|
|
182
|
+
model: 'claude-opus-4-8',
|
|
183
|
+
}),
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: 'Agent card — over-long steps (per-line clip)',
|
|
187
|
+
fn: 'renderStatusCard → cleanStepLine (STATUS_LINE_MAX)',
|
|
188
|
+
when: 'A step line far exceeds STATUS_LINE_MAX: each is clipped to 200 chars + `…` before escaping. (This is also why the card-wide `fitCardToBudget` backstop is unreachable on the agent card — 5 clipped lines cannot approach 32768. The shrink levels are genuinely exercised by the WORKERS spill variant.)',
|
|
189
|
+
render: () =>
|
|
190
|
+
renderActivityFeed(
|
|
191
|
+
[LONG, LONG, LONG, 'Finally a short newest step'],
|
|
192
|
+
false,
|
|
193
|
+
'',
|
|
194
|
+
undefined,
|
|
195
|
+
agentHeader(),
|
|
196
|
+
),
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: 'Worker card — just dispatched (no steps yet)',
|
|
200
|
+
fn: 'renderWorkerActivity → renderStatusCard (emptyPlaceholder)',
|
|
201
|
+
when: 'The first paint of a background worker, before it has produced a narrative line.',
|
|
202
|
+
render: () =>
|
|
203
|
+
renderWorkerActivity(
|
|
204
|
+
workerView({ narrativeLines: [], latestSummary: '', elapsedMs: 1_200, toolCount: 0 }),
|
|
205
|
+
),
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: 'Worker card — single worker running',
|
|
209
|
+
fn: 'renderWorkerActivity → renderStatusCard',
|
|
210
|
+
when: 'Exactly one background worker live in the chat. Pinned; edited in place.',
|
|
211
|
+
render: () => renderWorkerActivity(workerView(), ' · 18s'),
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
name: 'Worker card — done (with result recap)',
|
|
215
|
+
fn: 'renderWorkerActivity → renderStatusCard (result block)',
|
|
216
|
+
when: 'The worker finished successfully: feed all-struck, rule, then the ✅ result paragraph.',
|
|
217
|
+
render: () =>
|
|
218
|
+
renderWorkerActivity(
|
|
219
|
+
workerView({
|
|
220
|
+
state: 'done',
|
|
221
|
+
elapsedMs: 902_000,
|
|
222
|
+
latestSummary:
|
|
223
|
+
'Inventoried 14 card variants, found renderCombinedWorkerFeed forks the header + budget logic, and de-indented the worker card. PR #0000.',
|
|
224
|
+
}),
|
|
225
|
+
),
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: 'Worker card — failed',
|
|
229
|
+
fn: 'renderWorkerActivity → renderStatusCard (result block, ⚠️)',
|
|
230
|
+
when: 'The worker errored: header line 2 reads `failed`, result block carries the error recap.',
|
|
231
|
+
render: () =>
|
|
232
|
+
renderWorkerActivity(
|
|
233
|
+
workerView({
|
|
234
|
+
state: 'failed',
|
|
235
|
+
elapsedMs: 61_000,
|
|
236
|
+
latestSummary: 'Scoped test run failed: 3 assertions in card-type-distinguishability.test.ts.',
|
|
237
|
+
}),
|
|
238
|
+
),
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: 'Worker card — incomplete (killed / timed out)',
|
|
242
|
+
fn: 'renderWorkerActivity → renderStatusCard (result suppressed by invariant)',
|
|
243
|
+
when: 'The worker was terminated before producing a result — the renderer refuses to fabricate a result block.',
|
|
244
|
+
render: () =>
|
|
245
|
+
renderWorkerActivity(
|
|
246
|
+
workerView({
|
|
247
|
+
state: 'incomplete',
|
|
248
|
+
elapsedMs: 1_805_000,
|
|
249
|
+
latestSummary: 'stray text that must not render',
|
|
250
|
+
}),
|
|
251
|
+
),
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
name: 'WORKERS card — 2 workers',
|
|
255
|
+
fn: 'renderCombinedWorkerFeed → the shared card spec',
|
|
256
|
+
when: '2+ background workers live in the same chat/thread coalesce into ONE message. Depth = 5 each.',
|
|
257
|
+
render: () =>
|
|
258
|
+
renderCombinedWorkerFeed(
|
|
259
|
+
[
|
|
260
|
+
row(1, 'Audit every card render path', H(5, 'audit')),
|
|
261
|
+
row(2, 'Fix the hindsight recall floor', H(5, 'recall')),
|
|
262
|
+
],
|
|
263
|
+
{ maxRows: 6 },
|
|
264
|
+
),
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: 'WORKERS card — 4 workers',
|
|
268
|
+
fn: 'renderCombinedWorkerFeed → the shared card spec',
|
|
269
|
+
when: "Depth degrades to the floor of Ken's 6/5/4/3 curve.",
|
|
270
|
+
render: () =>
|
|
271
|
+
renderCombinedWorkerFeed(
|
|
272
|
+
[
|
|
273
|
+
row(1, 'Audit every card render path', H(4, 'audit')),
|
|
274
|
+
row(2, 'Fix the hindsight recall floor', H(4, 'recall')),
|
|
275
|
+
row(3, 'Release v0.19.24', H(4, 'release')),
|
|
276
|
+
row(4, 'Investigate the flaky UAT scenario', H(4, 'uat')),
|
|
277
|
+
],
|
|
278
|
+
{ maxRows: 6 },
|
|
279
|
+
),
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: 'WORKERS card — 8 workers (spill)',
|
|
283
|
+
fn: 'renderCombinedWorkerFeed → fitCardToBudget (row-count shrink levels)',
|
|
284
|
+
when: 'Beyond the design fan-out the NEWEST rows collapse into a single `+M more working…` line.',
|
|
285
|
+
render: () =>
|
|
286
|
+
renderCombinedWorkerFeed(
|
|
287
|
+
Array.from({ length: 8 }, (_, i) => row(i + 1, `Background task number ${i + 1}`, H(3, `t${i + 1}`))),
|
|
288
|
+
{ maxRows: 6 },
|
|
289
|
+
),
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: 'WORKERS card — survivors keep their ordinals',
|
|
293
|
+
fn: 'renderCombinedWorkerFeed → the shared card spec',
|
|
294
|
+
when: 'Worker 1 finished; 2 and 3 keep their numbers, so the card shows `2.`/`3.` with no `1.`',
|
|
295
|
+
render: () =>
|
|
296
|
+
renderCombinedWorkerFeed(
|
|
297
|
+
[
|
|
298
|
+
row(2, 'Fix the hindsight recall floor', H(4, 'recall')),
|
|
299
|
+
row(3, 'Release v0.19.24', H(4, 'release')),
|
|
300
|
+
],
|
|
301
|
+
{ maxRows: 6 },
|
|
302
|
+
),
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: 'WORKERS card — a worker with no history yet',
|
|
306
|
+
fn: 'renderCombinedWorkerFeed → section placeholder',
|
|
307
|
+
when: 'A freshly dispatched worker joins a group that already has a live card.',
|
|
308
|
+
render: () =>
|
|
309
|
+
renderCombinedWorkerFeed(
|
|
310
|
+
[
|
|
311
|
+
row(1, 'Audit every card render path', H(5, 'audit')),
|
|
312
|
+
row(2, 'Freshly dispatched worker', [], { currentStep: '', historyLines: undefined }),
|
|
313
|
+
],
|
|
314
|
+
{ maxRows: 6 },
|
|
315
|
+
),
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
name: 'Worker card — superseded (group message rotated)',
|
|
319
|
+
layout: 'constant',
|
|
320
|
+
fn: 'WORKER_CARD_SUPERSEDED_BODY (static notice; title line via renderCardTitleLine)',
|
|
321
|
+
when: 'The shared worker message hit its lifetime cap and a fresh card was opened; the retired message is finalised to this static note so it does not read as a stuck worker.',
|
|
322
|
+
render: () => WORKER_CARD_SUPERSEDED_BODY,
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
name: 'Agent card — rolling-window overflow',
|
|
326
|
+
fn: 'renderActivityFeed → renderStatusCard → emitSection',
|
|
327
|
+
when: 'More steps than the rolling window: the oldest collapse to a `✓ +N earlier…` header.',
|
|
328
|
+
render: () =>
|
|
329
|
+
renderActivityFeed(
|
|
330
|
+
Array.from({ length: 14 }, (_, i) => `Doing thing number ${i + 1}`),
|
|
331
|
+
false,
|
|
332
|
+
'',
|
|
333
|
+
undefined,
|
|
334
|
+
agentHeader(),
|
|
335
|
+
),
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
name: 'Agent card — nested sub-agent with child overflow',
|
|
339
|
+
fn: 'renderActivityFeedWithNested → renderStatusCard',
|
|
340
|
+
when: 'The foreground sub-agent has produced more steps than the window: a `↳ +N earlier…` header appears in the child block.',
|
|
341
|
+
render: () =>
|
|
342
|
+
renderActivityFeedWithNested(
|
|
343
|
+
AGENT_STEPS.slice(0, 2),
|
|
344
|
+
Array.from({ length: 11 }, (_, i) => `Child step ${i + 1}`),
|
|
345
|
+
false,
|
|
346
|
+
'',
|
|
347
|
+
undefined,
|
|
348
|
+
agentHeader(),
|
|
349
|
+
),
|
|
350
|
+
},
|
|
351
|
+
]
|
|
352
|
+
|
|
353
|
+
/** Section delimiter in the golden file. Chosen so no card body can contain it. */
|
|
354
|
+
export const GOLDEN_DELIMITER = '\n===== CARD ====='
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* The whole catalogue as ONE deterministic string — what the golden file holds.
|
|
358
|
+
* Every variant contributes its name and its exact rendered bytes.
|
|
359
|
+
*/
|
|
360
|
+
export function renderGoldenDocument(): string {
|
|
361
|
+
return (
|
|
362
|
+
CARD_VARIANTS.map(
|
|
363
|
+
(v) => `${GOLDEN_DELIMITER} ${v.name}\n${v.render() ?? '(null — renderer returned nothing)'}`,
|
|
364
|
+
).join('\n') + '\n'
|
|
365
|
+
)
|
|
366
|
+
}
|