switchroom 0.14.19 → 0.14.20
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/dist/agent-scheduler/index.js +6 -1
- package/dist/auth-broker/index.js +6 -1
- package/dist/cli/notion-write-pretool.mjs +6 -1
- package/dist/cli/switchroom.js +17 -3
- package/dist/host-control/main.js +6 -1
- package/dist/vault/approvals/kernel-server.js +6 -1
- package/dist/vault/broker/server.js +6 -1
- package/package.json +1 -1
- package/telegram-plugin/README.md +7 -3
- package/telegram-plugin/bridge/bridge.ts +1 -1
- package/telegram-plugin/dist/bridge/bridge.js +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +357 -152
- package/telegram-plugin/dist/server.js +1 -1
- package/telegram-plugin/gateway/coalesce-attachments.ts +70 -0
- package/telegram-plugin/gateway/gateway.ts +246 -36
- package/telegram-plugin/gateway/interrupt-defer.ts +100 -0
- package/telegram-plugin/gateway/pending-inbound-buffer.ts +21 -4
- package/telegram-plugin/tests/coalesce-attachments.test.ts +152 -0
- package/telegram-plugin/tests/interrupt-defer.test.ts +147 -0
- package/telegram-plugin/tests/pending-inbound-buffer.test.ts +36 -0
- package/telegram-plugin/tests/worker-activity-feed.test.ts +113 -0
- package/telegram-plugin/uat/scenarios/jtbd-forwarded-burst-dm.test.ts +158 -0
- package/telegram-plugin/worker-activity-feed.ts +54 -4
|
@@ -46,6 +46,14 @@ export interface WorkerActivityView {
|
|
|
46
46
|
toolCount: number
|
|
47
47
|
/** The worker's latest narrative line, if any (already capped upstream). */
|
|
48
48
|
latestSummary: string
|
|
49
|
+
/**
|
|
50
|
+
* Accumulated narrative lines, oldest→newest, already deduped + capped by
|
|
51
|
+
* the feed manager. When present and non-empty, the render grows a block
|
|
52
|
+
* of `↳` lines (mirroring the main agent's live answer) instead of
|
|
53
|
+
* collapsing to the single `latestSummary` line. Absent/empty → the
|
|
54
|
+
* single-line fallback (back-compat for direct render callers).
|
|
55
|
+
*/
|
|
56
|
+
narrativeLines?: string[]
|
|
49
57
|
/** Wall-clock since dispatch, ms. */
|
|
50
58
|
elapsedMs: number
|
|
51
59
|
state: WorkerActivityState
|
|
@@ -68,6 +76,13 @@ export interface BotApiForWorkerFeed {
|
|
|
68
76
|
const DESC_MAX = 80
|
|
69
77
|
const TOOL_ARG_MAX = 64
|
|
70
78
|
const SUMMARY_MAX = 100
|
|
79
|
+
/**
|
|
80
|
+
* How many trailing narrative lines the live feed keeps visible. The feed
|
|
81
|
+
* grows like the main agent's answer but can't grow unbounded — Telegram
|
|
82
|
+
* caps message length and a wall of stale lines buries the live one. Six
|
|
83
|
+
* keeps recent context without dominating the chat.
|
|
84
|
+
*/
|
|
85
|
+
const NARRATIVE_MAX_LINES = 6
|
|
71
86
|
|
|
72
87
|
/**
|
|
73
88
|
* Render the worker-activity message body as Telegram HTML.
|
|
@@ -105,10 +120,23 @@ export function renderWorkerActivity(v: WorkerActivityView): string {
|
|
|
105
120
|
activity = `<i>starting… (${elapsed})</i>`
|
|
106
121
|
}
|
|
107
122
|
|
|
108
|
-
const summary = v.latestSummary.trim()
|
|
109
123
|
const lines = [header, activity]
|
|
110
|
-
|
|
111
|
-
|
|
124
|
+
|
|
125
|
+
// Growing narrative block when the manager has accumulated lines; the feed
|
|
126
|
+
// reads like the main agent's live answer rather than a single replaced
|
|
127
|
+
// status line. Fall back to the single latestSummary line otherwise.
|
|
128
|
+
const narrative = (v.narrativeLines ?? [])
|
|
129
|
+
.map((s) => s.trim())
|
|
130
|
+
.filter((s) => s.length > 0)
|
|
131
|
+
if (narrative.length > 0) {
|
|
132
|
+
for (const line of narrative) {
|
|
133
|
+
lines.push(` ↳ <i>${escapeHtml(truncate(line, SUMMARY_MAX))}</i>`)
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
const summary = v.latestSummary.trim()
|
|
137
|
+
if (summary.length > 0) {
|
|
138
|
+
lines.push(` ↳ <i>${escapeHtml(truncate(summary, SUMMARY_MAX))}</i>`)
|
|
139
|
+
}
|
|
112
140
|
}
|
|
113
141
|
return lines.join('\n')
|
|
114
142
|
}
|
|
@@ -142,6 +170,12 @@ interface WorkerHandle {
|
|
|
142
170
|
lastBody: string | null
|
|
143
171
|
lastEditAt: number
|
|
144
172
|
cooldownUntil: number
|
|
173
|
+
/**
|
|
174
|
+
* Accumulated narrative lines (oldest→newest), deduped against the
|
|
175
|
+
* immediately-preceding line and capped to NARRATIVE_MAX_LINES. Grows the
|
|
176
|
+
* live render so the feed reads like the main agent's answer.
|
|
177
|
+
*/
|
|
178
|
+
narrative: string[]
|
|
145
179
|
/** Per-worker serialization chain so ticks can't interleave sends. */
|
|
146
180
|
chain: Promise<void>
|
|
147
181
|
}
|
|
@@ -203,9 +237,24 @@ export function createWorkerActivityFeed(opts: WorkerActivityFeedOpts): WorkerAc
|
|
|
203
237
|
log(`worker-feed: ${label} 429 — backing off ${retryAfter}s`)
|
|
204
238
|
}
|
|
205
239
|
|
|
240
|
+
function accumulateNarrative(h: WorkerHandle, view: WorkerActivityView): void {
|
|
241
|
+
const line = view.latestSummary.trim()
|
|
242
|
+
if (line.length === 0) return
|
|
243
|
+
// Dedup against the immediately-preceding line — the watcher re-emits the
|
|
244
|
+
// same narrative across ticks while a tool runs; we only grow on change.
|
|
245
|
+
if (h.narrative[h.narrative.length - 1] === line) return
|
|
246
|
+
h.narrative.push(line)
|
|
247
|
+
if (h.narrative.length > NARRATIVE_MAX_LINES) {
|
|
248
|
+
h.narrative.splice(0, h.narrative.length - NARRATIVE_MAX_LINES)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
206
252
|
async function doUpdate(h: WorkerHandle, view: WorkerActivityView): Promise<void> {
|
|
253
|
+
// Accumulate before any gate so a throttled/cooled-down tick still grows
|
|
254
|
+
// the narrative — the line surfaces on the next edit that does fire.
|
|
255
|
+
accumulateNarrative(h, view)
|
|
207
256
|
if (nowFn() < h.cooldownUntil) return
|
|
208
|
-
const body = renderWorkerActivity(view)
|
|
257
|
+
const body = renderWorkerActivity({ ...view, narrativeLines: h.narrative })
|
|
209
258
|
|
|
210
259
|
// First paint: hold off until the worker has run long enough to be
|
|
211
260
|
// worth a message; trivial workers stay silent (handback covers them).
|
|
@@ -284,6 +333,7 @@ export function createWorkerActivityFeed(opts: WorkerActivityFeedOpts): WorkerAc
|
|
|
284
333
|
lastBody: null,
|
|
285
334
|
lastEditAt: 0,
|
|
286
335
|
cooldownUntil: 0,
|
|
336
|
+
narrative: [],
|
|
287
337
|
chain: Promise.resolve(),
|
|
288
338
|
}
|
|
289
339
|
handles.set(agentId, h)
|