switchroom 0.19.26 → 0.19.28
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 +60 -2
- package/dist/auth-broker/index.js +244 -13
- package/dist/cli/autoaccept-poll.js +225 -17
- package/dist/cli/notion-write-pretool.mjs +60 -2
- package/dist/cli/switchroom.js +2843 -1220
- package/dist/host-control/main.js +245 -14
- package/dist/vault/approvals/kernel-server.js +242 -13
- package/dist/vault/broker/server.js +242 -13
- package/package.json +7 -2
- package/profiles/_base/cron-session.sh.hbs +8 -0
- package/profiles/_base/start.sh.hbs +175 -15
- package/telegram-plugin/card-layout.ts +328 -0
- package/telegram-plugin/dist/bridge/bridge.js +94 -1
- package/telegram-plugin/dist/gateway/gateway.js +2544 -1182
- package/telegram-plugin/dist/server.js +97 -1
- package/telegram-plugin/edit-flood-fuse.ts +841 -57
- package/telegram-plugin/flood-429-ledger.ts +526 -0
- package/telegram-plugin/flood-circuit-breaker.ts +18 -0
- package/telegram-plugin/gateway/callback-query-handlers.ts +6 -0
- package/telegram-plugin/gateway/flood-reply-queue.ts +168 -0
- package/telegram-plugin/gateway/gateway.ts +67 -70
- package/telegram-plugin/gateway/mcp-failure-hook.ts +74 -0
- 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/pinned-message-handler.ts +12 -16
- package/telegram-plugin/gateway/status-pin-retarget.ts +72 -36
- package/telegram-plugin/gateway/status-pin-store.ts +58 -9
- package/telegram-plugin/gateway/worker-pin-reaper.ts +56 -7
- package/telegram-plugin/inline-keyboard-callbacks.ts +202 -21
- package/telegram-plugin/llm-error-present.ts +61 -2
- package/telegram-plugin/mcp-credential-failure.ts +459 -0
- package/telegram-plugin/model-unavailable.ts +8 -0
- package/telegram-plugin/operator-events.ts +110 -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 +10 -48
- package/telegram-plugin/status-pin-driver.ts +33 -45
- package/telegram-plugin/status-pin.ts +18 -1
- 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 +187 -164
- 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 +373 -0
- package/telegram-plugin/tests/edit-flood-fuse-default-deny.test.ts +319 -0
- package/telegram-plugin/tests/edit-flood-fuse-reply-reserve.test.ts +340 -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/finalize-callback-flood-policy.test.ts +298 -0
- package/telegram-plugin/tests/finalize-callback.test.ts +41 -8
- 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/mcp-credential-failure.test.ts +310 -0
- package/telegram-plugin/tests/outbox-sweep-flood-breaker.test.ts +221 -0
- package/telegram-plugin/tests/pinned-card-collapse.test.ts +19 -24
- 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 +90 -62
- package/telegram-plugin/tests/status-pin-service-message-suppression.test.ts +7 -3
- package/telegram-plugin/tests/status-pin-store.test.ts +109 -60
- package/telegram-plugin/tests/status-pin.test.ts +56 -5
- 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 +23 -29
- 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/tool-activity-summary.ts +239 -365
- package/telegram-plugin/uat/assertions.ts +22 -11
- package/telegram-plugin/uat/feed-matcher.test.ts +24 -17
- package/telegram-plugin/worker-activity-feed.ts +105 -47
- package/vendor/hindsight-memory/CLAUDE.md +45 -0
- package/vendor/hindsight-memory/scripts/drain_pending.py +433 -11
- package/vendor/hindsight-memory/scripts/lib/config.py +33 -0
- package/vendor/hindsight-memory/scripts/lib/pending.py +193 -28
- 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_drain_circuit_breaker.py +401 -0
- package/vendor/hindsight-memory/scripts/tests/test_drain_serialisation.py +286 -0
- package/vendor/hindsight-memory/scripts/tests/test_pending_drops.py +817 -8
- package/vendor/hindsight-memory/scripts/tests/test_recall_min_score.py +464 -0
- package/vendor/hindsight-memory/settings.json +1 -1
- package/vendor/hindsight-memory/tests/test_hooks.py +11 -2
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* #
|
|
3
|
-
*
|
|
2
|
+
* #3842 — every status card is FLUSH at the left margin, and the only
|
|
3
|
+
* indentation any card carries is the step indent that separates one worker's
|
|
4
|
+
* steps from the next worker's on the combined (2+ worker) card.
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* #3820/#3821 had made the worker card structurally subordinate to the 🤖 agent
|
|
7
|
+
* card: line 1 prefixed with `└─ ` and every later line indented by a U+2800
|
|
8
|
+
* run. Ken rejected that shape — it burns a level of horizontal space on a
|
|
9
|
+
* phone, and the worker card does not always sit below the agent card, so a
|
|
10
|
+
* card-LEVEL subordination marker asserts a relationship that is not always
|
|
11
|
+
* true. Net effect of this file's expectations: everything shifted left by one
|
|
12
|
+
* level; worker-vs-worker separation is carried by the surviving step indent
|
|
13
|
+
* plus the numbered row headers.
|
|
12
14
|
*
|
|
13
|
-
* These tests assert the RENDERED OUTPUT
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* - every later worker line carries the subordinate indent;
|
|
17
|
-
* - with identical step text and identical stats, NO line of one card equals
|
|
18
|
-
* any line of the other.
|
|
15
|
+
* These tests assert the RENDERED OUTPUT of the real renderers, and they fail
|
|
16
|
+
* on the pre-#3842 shape (which put `└─ ` on line 1 and U+2800 on every other
|
|
17
|
+
* line of both worker surfaces).
|
|
19
18
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
19
|
+
* What is NOT asserted here any more, deliberately: that no rendered line is
|
|
20
|
+
* shared between the agent card and the worker card. With whole-card nesting
|
|
21
|
+
* gone and identical step text, the two cards' BODY lines coincide byte for
|
|
22
|
+
* byte — see the explicit characterization test at the end. Line 1 (🤖 `Agent`
|
|
23
|
+
* vs 🛠 `WORKER`) is now the only type cue, which is the accepted trade.
|
|
24
24
|
*/
|
|
25
25
|
import { describe, it, expect } from 'vitest'
|
|
26
26
|
import {
|
|
@@ -29,23 +29,20 @@ import {
|
|
|
29
29
|
renderCombinedWorkerFeed,
|
|
30
30
|
} from '../tool-activity-summary.js'
|
|
31
31
|
import { renderWorkerActivity, type WorkerActivityView } from '../worker-activity-feed.js'
|
|
32
|
-
import {
|
|
33
|
-
SUBORDINATE_HEADER_PREFIX,
|
|
34
|
-
SUBORDINATE_LINE_INDENT,
|
|
35
|
-
WORKER_STEP_INDENT,
|
|
36
|
-
STATUS_CARD_CHAR_BUDGET,
|
|
37
|
-
nestSubordinateCardLines,
|
|
38
|
-
} from '../status-no-truncate.js'
|
|
32
|
+
import { WORKER_STEP_INDENT, STATUS_CARD_CHAR_BUDGET } from '../status-no-truncate.js'
|
|
39
33
|
import { richMessage } from '../rich-send.js'
|
|
40
34
|
|
|
41
35
|
/** Split a rendered card into display lines, dropping the hard-break padding. */
|
|
42
36
|
function lines(card: string): string[] {
|
|
43
|
-
return card.split('\n').map((l) => l.replace(/[\
|
|
37
|
+
return card.split('\n').map((l) => l.replace(/[ \t\r]+$/, ''))
|
|
44
38
|
}
|
|
45
39
|
|
|
46
|
-
/** The exact scenario from
|
|
40
|
+
/** The exact scenario from #3820: parent narrating the task it delegated. */
|
|
47
41
|
const STEPS = ['PR 2502 details', 'Commits since v0.8.5']
|
|
48
42
|
|
|
43
|
+
/** The card-level chrome #3842 removed. Asserted absent, never re-emitted. */
|
|
44
|
+
const REMOVED_HEADER_PREFIX = '└─ '
|
|
45
|
+
|
|
49
46
|
function agentCard(): string {
|
|
50
47
|
const out = renderActivityFeed(STEPS, false, '', undefined, {
|
|
51
48
|
label: 'Agent',
|
|
@@ -73,116 +70,145 @@ function workerView(over: Partial<WorkerActivityView> = {}): WorkerActivityView
|
|
|
73
70
|
} as WorkerActivityView
|
|
74
71
|
}
|
|
75
72
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
function combinedCard(): string {
|
|
74
|
+
const card = renderCombinedWorkerFeed(
|
|
75
|
+
[
|
|
76
|
+
{
|
|
77
|
+
description: 'Check upstream hindsight changes',
|
|
78
|
+
elapsedMs: 55_000,
|
|
79
|
+
toolCount: 9,
|
|
80
|
+
currentStep: 'Commits since v0.8.5',
|
|
81
|
+
historyLines: STEPS,
|
|
82
|
+
ordinal: 1,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
description: 'Fix the card renderer',
|
|
86
|
+
elapsedMs: 30_000,
|
|
87
|
+
toolCount: 4,
|
|
88
|
+
currentStep: 'Reading render.ts',
|
|
89
|
+
historyLines: ['Reading render.ts'],
|
|
90
|
+
ordinal: 2,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
{ maxRows: 4 },
|
|
94
|
+
)
|
|
95
|
+
expect(card).not.toBeNull()
|
|
96
|
+
return card as string
|
|
97
|
+
}
|
|
80
98
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
99
|
+
/** Every worker surface, in each of its render states, as rendered strings. */
|
|
100
|
+
function everyWorkerSurface(): Array<{ name: string; card: string }> {
|
|
101
|
+
return [
|
|
102
|
+
{ name: 'running', card: renderWorkerActivity(workerView()) },
|
|
103
|
+
{
|
|
104
|
+
name: 'just dispatched',
|
|
105
|
+
card: renderWorkerActivity(workerView({ narrativeLines: [], latestSummary: '' })),
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'done + result block',
|
|
109
|
+
card: renderWorkerActivity(
|
|
110
|
+
workerView({ state: 'done', latestSummary: 'Rebased and opened the PR.' }),
|
|
111
|
+
),
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: 'failed + result block',
|
|
115
|
+
card: renderWorkerActivity(
|
|
116
|
+
workerView({ state: 'failed', latestSummary: 'Scoped tests went red.' }),
|
|
117
|
+
),
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'incomplete',
|
|
121
|
+
card: renderWorkerActivity(workerView({ state: 'incomplete', latestSummary: '' })),
|
|
122
|
+
},
|
|
123
|
+
{ name: 'combined (2 workers)', card: combinedCard() },
|
|
124
|
+
]
|
|
125
|
+
}
|
|
90
126
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
expect(l.startsWith(SUBORDINATE_LINE_INDENT)).toBe(true)
|
|
127
|
+
describe('#3842 every card is flush at the left margin', () => {
|
|
128
|
+
it('emits no `└─ ` header prefix on any surface', () => {
|
|
129
|
+
for (const { name, card } of [{ name: 'agent', card: agentCard() }, ...everyWorkerSurface()]) {
|
|
130
|
+
expect(card.includes(REMOVED_HEADER_PREFIX), `${name} card carries └─`).toBe(false)
|
|
96
131
|
}
|
|
97
132
|
})
|
|
98
133
|
|
|
99
|
-
it('
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
134
|
+
it('leaves every line of the SINGLE-worker card unindented', () => {
|
|
135
|
+
// Pre-#3842 line 1 started `└─ ` and lines 2..n started with a U+2800 run.
|
|
136
|
+
const surfaces = everyWorkerSurface()
|
|
137
|
+
for (const name of [
|
|
138
|
+
'running',
|
|
139
|
+
'just dispatched',
|
|
140
|
+
'done + result block',
|
|
141
|
+
'failed + result block',
|
|
142
|
+
'incomplete',
|
|
143
|
+
]) {
|
|
144
|
+
const card = surfaces.find((s) => s.name === name)?.card as string
|
|
145
|
+
const l = lines(card)
|
|
146
|
+
expect(l.length).toBeGreaterThan(1)
|
|
147
|
+
for (const line of l) {
|
|
148
|
+
expect(line.startsWith(WORKER_STEP_INDENT), `${name}: "${line}"`).toBe(false)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
107
151
|
})
|
|
108
152
|
|
|
109
|
-
it('
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
expect(
|
|
114
|
-
expect(
|
|
153
|
+
it('keeps the just-dispatched `starting…` tail flush too', () => {
|
|
154
|
+
// The one render state a user always sees first takes a hand-rolled append
|
|
155
|
+
// path outside renderStatusCard — the place a shape change silently misses.
|
|
156
|
+
const l = lines(renderWorkerActivity(workerView({ narrativeLines: [], latestSummary: '' })))
|
|
157
|
+
expect(l[0].startsWith('🛠 **WORKER**')).toBe(true)
|
|
158
|
+
expect(l[l.length - 1]).toBe('_starting…_')
|
|
115
159
|
})
|
|
116
160
|
|
|
117
|
-
it('keeps the terminal
|
|
118
|
-
const
|
|
161
|
+
it('keeps the terminal result block flush', () => {
|
|
162
|
+
const l = lines(
|
|
119
163
|
renderWorkerActivity(
|
|
120
164
|
workerView({ state: 'done', latestSummary: 'Rebased and opened the PR.' }),
|
|
121
165
|
),
|
|
122
166
|
)
|
|
123
|
-
expect(
|
|
124
|
-
|
|
125
|
-
expect(l.startsWith(SUBORDINATE_LINE_INDENT)).toBe(true)
|
|
126
|
-
}
|
|
127
|
-
// The result block is present and nested, not floated back to the margin.
|
|
128
|
-
expect(worker.some((l) => l.startsWith(`${SUBORDINATE_LINE_INDENT}✅ `))).toBe(true)
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it('keeps the just-dispatched (starting…) worker render subordinate', () => {
|
|
132
|
-
// The first state a user ever sees for a worker takes a hand-rolled append
|
|
133
|
-
// path outside renderStatusCard — the one place a nesting fix can silently
|
|
134
|
-
// miss.
|
|
135
|
-
const worker = lines(
|
|
136
|
-
renderWorkerActivity(workerView({ narrativeLines: [], latestSummary: '' })),
|
|
137
|
-
)
|
|
138
|
-
expect(worker[0].startsWith(SUBORDINATE_HEADER_PREFIX)).toBe(true)
|
|
139
|
-
expect(worker[worker.length - 1]).toBe(`${SUBORDINATE_LINE_INDENT}_starting…_`)
|
|
167
|
+
expect(l.some((x) => x.startsWith('✅ '))).toBe(true)
|
|
168
|
+
expect(l.some((x) => x.startsWith(`${WORKER_STEP_INDENT}✅ `))).toBe(false)
|
|
140
169
|
})
|
|
170
|
+
})
|
|
141
171
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
description: 'Check upstream hindsight changes',
|
|
147
|
-
elapsedMs: 55_000,
|
|
148
|
-
toolCount: 9,
|
|
149
|
-
currentStep: 'Commits since v0.8.5',
|
|
150
|
-
historyLines: STEPS,
|
|
151
|
-
ordinal: 1,
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
description: 'Fix the card renderer',
|
|
155
|
-
elapsedMs: 30_000,
|
|
156
|
-
toolCount: 4,
|
|
157
|
-
currentStep: 'Reading render.ts',
|
|
158
|
-
historyLines: ['Reading render.ts'],
|
|
159
|
-
ordinal: 2,
|
|
160
|
-
},
|
|
161
|
-
],
|
|
162
|
-
{ maxRows: 4 },
|
|
163
|
-
)
|
|
164
|
-
expect(card).not.toBeNull()
|
|
165
|
-
const l = lines(card as string)
|
|
166
|
-
expect(l[0]).toBe(`${SUBORDINATE_HEADER_PREFIX}🛠 **WORKERS** · _2 running · oldest 55s · 13 tools_`)
|
|
167
|
-
// Row headers sit at one level; their steps stay one level deeper, so the
|
|
168
|
-
// card's own parent/child structure survives the shift right.
|
|
172
|
+
describe('#3842 the combined card keeps exactly ONE level of indent', () => {
|
|
173
|
+
it('puts the glance line and row headers flush', () => {
|
|
174
|
+
const l = lines(combinedCard())
|
|
175
|
+
expect(l[0]).toBe('🛠 **WORKERS** · _2 running · oldest 55s · 13 tools_')
|
|
169
176
|
const rowHeaders = l.filter((x) => x.includes('**1. ') || x.includes('**2. '))
|
|
170
177
|
expect(rowHeaders.length).toBe(2)
|
|
171
|
-
for (const h of rowHeaders)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
178
|
+
for (const h of rowHeaders) expect(h.startsWith(WORKER_STEP_INDENT)).toBe(false)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('indents step lines exactly one level — never two', () => {
|
|
182
|
+
const l = lines(combinedCard())
|
|
175
183
|
const stepLines = l.filter((x) => x.includes('→ ') || x.includes('✓ '))
|
|
176
184
|
expect(stepLines.length).toBeGreaterThan(0)
|
|
177
185
|
for (const s of stepLines) {
|
|
178
|
-
expect(s.startsWith(
|
|
186
|
+
expect(s.startsWith(WORKER_STEP_INDENT)).toBe(true)
|
|
187
|
+
// The pre-#3842 double indent (SUBORDINATE_LINE_INDENT + WORKER_STEP_INDENT).
|
|
188
|
+
expect(s.startsWith(WORKER_STEP_INDENT + WORKER_STEP_INDENT)).toBe(false)
|
|
179
189
|
}
|
|
180
190
|
})
|
|
181
191
|
|
|
182
|
-
it('
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
192
|
+
it('puts the `+M more working…` spill line flush', () => {
|
|
193
|
+
const rows = Array.from({ length: 5 }, (_, i) => ({
|
|
194
|
+
description: `task ${i + 1}`,
|
|
195
|
+
elapsedMs: 10_000,
|
|
196
|
+
toolCount: 1,
|
|
197
|
+
currentStep: 'working',
|
|
198
|
+
historyLines: ['working'],
|
|
199
|
+
ordinal: i + 1,
|
|
200
|
+
}))
|
|
201
|
+
const card = renderCombinedWorkerFeed(rows, { maxRows: 2 }) as string
|
|
202
|
+
const spill = lines(card).find((x) => x.includes('more working…'))
|
|
203
|
+
expect(spill).toBe('_+3 more working…_')
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
describe('#3842 char-budget accounting matches the flush shape', () => {
|
|
208
|
+
it('keeps an oversized worker card under the wire budget', () => {
|
|
209
|
+
// fitCardToBudget has its own line assembly + arithmetic. With the per-line
|
|
210
|
+
// nest cost removed it must still land under the cap, and must not emit any
|
|
211
|
+
// chrome the card no longer carries.
|
|
186
212
|
const huge = Array.from({ length: 500 }, (_, i) => `${i} `.repeat(100))
|
|
187
213
|
const card = renderStatusCard({
|
|
188
214
|
header: {
|
|
@@ -194,41 +220,60 @@ describe('#3820 agent vs worker card distinguishability', () => {
|
|
|
194
220
|
state: 'running',
|
|
195
221
|
},
|
|
196
222
|
steps: huge,
|
|
197
|
-
|
|
223
|
+
historyWindow: 6,
|
|
198
224
|
})
|
|
199
225
|
expect(card).not.toBeNull()
|
|
200
|
-
|
|
201
|
-
expect(
|
|
226
|
+
const s = card as string
|
|
227
|
+
expect(s.length).toBeLessThanOrEqual(STATUS_CARD_CHAR_BUDGET)
|
|
228
|
+
expect(s.includes(REMOVED_HEADER_PREFIX)).toBe(false)
|
|
229
|
+
for (const l of lines(s)) expect(l.startsWith(WORKER_STEP_INDENT)).toBe(false)
|
|
202
230
|
})
|
|
203
231
|
|
|
204
|
-
it('
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
expect(renderStatusCard({ ...opts, subordinate: false })).toBe(renderStatusCard(opts))
|
|
232
|
+
it('keeps an oversized combined card under the wire budget', () => {
|
|
233
|
+
const rows = Array.from({ length: 6 }, (_, i) => ({
|
|
234
|
+
description: 'D'.repeat(72),
|
|
235
|
+
elapsedMs: 10_000,
|
|
236
|
+
toolCount: 3,
|
|
237
|
+
currentStep: 'x',
|
|
238
|
+
historyLines: Array.from({ length: 6 }, (_, k) => `${'s'.repeat(180)}-${i}-${k}`),
|
|
239
|
+
ordinal: i + 1,
|
|
240
|
+
}))
|
|
241
|
+
const card = renderCombinedWorkerFeed(rows, { maxRows: 6 }) as string
|
|
242
|
+
expect(card.length).toBeLessThanOrEqual(STATUS_CARD_CHAR_BUDGET)
|
|
216
243
|
})
|
|
217
244
|
})
|
|
218
245
|
|
|
219
|
-
describe('#
|
|
220
|
-
it('
|
|
246
|
+
describe('#3842 the surviving type cues', () => {
|
|
247
|
+
it('uses a high-contrast caps type label on the worker card only', () => {
|
|
248
|
+
const agent = agentCard()
|
|
249
|
+
const worker = renderWorkerActivity(workerView())
|
|
250
|
+
expect(worker).toContain('🛠 **WORKER**')
|
|
251
|
+
expect(agent).toContain('🤖 **Agent**')
|
|
252
|
+
expect(agent).not.toContain('WORKER')
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
it('distinguishes the two cards on LINE 1 — the only remaining cue', () => {
|
|
256
|
+
// Characterization, not aspiration: with whole-card nesting gone and
|
|
257
|
+
// identical step text + stats, the two cards' BODY lines coincide byte for
|
|
258
|
+
// byte. Line 1 is doing all of the work, so it must never converge.
|
|
259
|
+
const agent = lines(agentCard())
|
|
260
|
+
const worker = lines(renderWorkerActivity(workerView()))
|
|
261
|
+
expect(agent[0]).not.toBe(worker[0])
|
|
262
|
+
expect(agent[0].startsWith('🤖 ')).toBe(true)
|
|
263
|
+
expect(worker[0].startsWith('🛠 ')).toBe(true)
|
|
264
|
+
// The documented cost of #3842, pinned so a future change to it is visible:
|
|
265
|
+
// every line BELOW line 1 is now shared between the two cards.
|
|
266
|
+
const overlap = agent.filter((a) => worker.includes(a))
|
|
267
|
+
expect(overlap).toEqual(agent.slice(1))
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
describe('#3842 the surviving step indent survives Telegram reality', () => {
|
|
272
|
+
it('passes every card through the real outbound markdown guard byte-identical', () => {
|
|
221
273
|
// richMessage() is the ONE adapter every `{ markdown }` wire send funnels
|
|
222
274
|
// through (rich-send.ts) — it runs the line-start / heading / dollar-math
|
|
223
|
-
// guards.
|
|
224
|
-
for (const card of [
|
|
225
|
-
agentCard(),
|
|
226
|
-
renderWorkerActivity(workerView()),
|
|
227
|
-
renderCombinedWorkerFeed(
|
|
228
|
-
[{ description: 'a', elapsedMs: 1, toolCount: 1, currentStep: 'b', ordinal: 1 }],
|
|
229
|
-
{ maxRows: 4 },
|
|
230
|
-
) as string,
|
|
231
|
-
]) {
|
|
275
|
+
// guards. Chrome that got escaped there would render as literal markup.
|
|
276
|
+
for (const { card } of [{ card: agentCard() }, ...everyWorkerSurface()]) {
|
|
232
277
|
expect(richMessage(card).markdown).toBe(card)
|
|
233
278
|
}
|
|
234
279
|
})
|
|
@@ -237,32 +282,10 @@ describe('#3820 subordinate prefixes survive Telegram reality', () => {
|
|
|
237
282
|
// U+00A0 shipped in #3662 and was INERT: Telegram left-trims a leading
|
|
238
283
|
// Unicode-whitespace run, and U+00A0 is category Zs. Assert the PROPERTY,
|
|
239
284
|
// not the bytes — a byte-only assertion is what let #3662 ship green.
|
|
240
|
-
for (const ch of Array.from(
|
|
285
|
+
for (const ch of Array.from(WORKER_STEP_INDENT)) {
|
|
241
286
|
expect(/\s/.test(ch)).toBe(false)
|
|
242
287
|
expect(/\p{White_Space}/u.test(ch)).toBe(false)
|
|
243
288
|
expect(/\p{Zs}/u.test(ch)).toBe(false)
|
|
244
289
|
}
|
|
245
290
|
})
|
|
246
|
-
|
|
247
|
-
it('uses a header prefix that is ink, not a line-start block trigger', () => {
|
|
248
|
-
const first = SUBORDINATE_HEADER_PREFIX[0]
|
|
249
|
-
// Not whitespace (would be trimmed), and not one of the GFM line-start
|
|
250
|
-
// block-construct triggers guarded in render/line-start-guard.ts.
|
|
251
|
-
expect(/\s/.test(first)).toBe(false)
|
|
252
|
-
expect('#>-+*'.includes(first)).toBe(false)
|
|
253
|
-
expect(/^\d+[.)]/.test(SUBORDINATE_HEADER_PREFIX)).toBe(false)
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
it('charges one flat per-line cost: header prefix and indent are equal length', () => {
|
|
257
|
-
// fitCardToBudget's arithmetic depends on this invariant.
|
|
258
|
-
expect(SUBORDINATE_HEADER_PREFIX.length).toBe(SUBORDINATE_LINE_INDENT.length)
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
it('nestSubordinateCardLines is pure and handles the empty case', () => {
|
|
262
|
-
expect(nestSubordinateCardLines([])).toEqual([])
|
|
263
|
-
expect(nestSubordinateCardLines(['a', 'b'])).toEqual([
|
|
264
|
-
`${SUBORDINATE_HEADER_PREFIX}a`,
|
|
265
|
-
`${SUBORDINATE_LINE_INDENT}b`,
|
|
266
|
-
])
|
|
267
|
-
})
|
|
268
291
|
})
|
|
@@ -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**
|