switchroom 0.14.16 → 0.14.18
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 +3 -0
- package/dist/auth-broker/index.js +3 -0
- package/dist/cli/notion-write-pretool.mjs +3 -0
- package/dist/cli/switchroom.js +8 -2
- package/dist/host-control/main.js +3 -0
- package/dist/vault/approvals/kernel-server.js +3 -0
- package/dist/vault/broker/server.js +3 -0
- package/package.json +1 -1
- package/profiles/_shared/telegram-style.md.hbs +6 -5
- package/telegram-plugin/dist/gateway/gateway.js +178 -48
- package/telegram-plugin/gateway/gateway.ts +89 -56
- package/telegram-plugin/gateway/inbound-coalesce.ts +8 -7
- package/telegram-plugin/gateway/pending-inbound-buffer.ts +100 -9
- package/telegram-plugin/gateway/worker-feed-dispatch.ts +37 -0
- package/telegram-plugin/subagent-watcher.ts +10 -1
- package/telegram-plugin/tests/inbound-coalesce.test.ts +21 -0
- package/telegram-plugin/tests/pending-inbound-buffer.test.ts +285 -1
- package/telegram-plugin/tests/worker-feed-dispatch.test.ts +140 -0
|
@@ -11068,6 +11068,9 @@ var TelegramChannelSchema = exports_external.object({
|
|
|
11068
11068
|
short_name: exports_external.string().optional().describe("Telegraph account display name. Defaults to the agent's slug. Used at " + "first-publish to lazily create the account; cached thereafter."),
|
|
11069
11069
|
author_name: exports_external.string().optional().describe("Telegraph article byline. Defaults to soul.name when set.")
|
|
11070
11070
|
}).optional().describe("Long-reply publishing via Telegraph (#579). When enabled, replies " + "above the threshold publish as a Telegraph article rendered in " + "Telegram via native Instant View. Off by default — content " + "residency is real for some personas (lawyer, health-coach with PHI). " + "Cascades from defaults.channels.telegram.telegraph. " + "(Migrated from per-agent root in #596.)"),
|
|
11071
|
+
coalesce: exports_external.object({
|
|
11072
|
+
window_ms: exports_external.number().int().nonnegative().optional().describe("Sliding-window (ms) for merging consecutive inbound messages from " + "the same sender+topic into ONE Claude turn. Each new message resets " + "the timer; the turn starts once the sender pauses for this long. " + "Catches forwarded bursts, pasted text the Telegram client split " + "into several messages, and mixed text+media forwards. Default 500. " + "Set 0 to disable (every message becomes its own turn). Raise for " + "users who think in multiple short messages; the trade-off is the " + "single-message turn start is delayed by this much (the \uD83D\uDC40 ack still " + "fires immediately, so perceived latency is unchanged).")
|
|
11073
|
+
}).optional().describe("Inbound coalescing — how the gateway groups rapid consecutive messages " + "into a single turn so a forwarded album or split paste doesn't fan out " + "into N separate turns. Cascades from defaults.channels.telegram.coalesce."),
|
|
11071
11074
|
webhook_sources: exports_external.array(exports_external.enum(["github", "generic"])).optional().describe("External webhook sources allowed to ingest events into this agent's " + "log. POST /webhook/<agent>/<source> on the switchroom web server. " + "Each source has its own signature verification ('github' = " + "X-Hub-Signature-256 HMAC-SHA256, 'generic' = Bearer token). " + "Per-source secret read from ~/.switchroom/webhook-secrets.json " + "keyed by [agent][source]. Verified events append to " + "<agent>/telegram/webhook-events.jsonl for the agent to read on " + "demand. Off by default — webhook is the only untrusted-inbound " + "surface in the system, so opt-in is mandatory. " + "Cascades from defaults.channels.telegram.webhook_sources. " + "(Migrated from per-agent root in #596 — see #577.)"),
|
|
11072
11075
|
webhook_dispatch: exports_external.object({
|
|
11073
11076
|
github: exports_external.array(exports_external.object({
|
|
@@ -11068,6 +11068,9 @@ var TelegramChannelSchema = exports_external.object({
|
|
|
11068
11068
|
short_name: exports_external.string().optional().describe("Telegraph account display name. Defaults to the agent's slug. Used at " + "first-publish to lazily create the account; cached thereafter."),
|
|
11069
11069
|
author_name: exports_external.string().optional().describe("Telegraph article byline. Defaults to soul.name when set.")
|
|
11070
11070
|
}).optional().describe("Long-reply publishing via Telegraph (#579). When enabled, replies " + "above the threshold publish as a Telegraph article rendered in " + "Telegram via native Instant View. Off by default — content " + "residency is real for some personas (lawyer, health-coach with PHI). " + "Cascades from defaults.channels.telegram.telegraph. " + "(Migrated from per-agent root in #596.)"),
|
|
11071
|
+
coalesce: exports_external.object({
|
|
11072
|
+
window_ms: exports_external.number().int().nonnegative().optional().describe("Sliding-window (ms) for merging consecutive inbound messages from " + "the same sender+topic into ONE Claude turn. Each new message resets " + "the timer; the turn starts once the sender pauses for this long. " + "Catches forwarded bursts, pasted text the Telegram client split " + "into several messages, and mixed text+media forwards. Default 500. " + "Set 0 to disable (every message becomes its own turn). Raise for " + "users who think in multiple short messages; the trade-off is the " + "single-message turn start is delayed by this much (the \uD83D\uDC40 ack still " + "fires immediately, so perceived latency is unchanged).")
|
|
11073
|
+
}).optional().describe("Inbound coalescing — how the gateway groups rapid consecutive messages " + "into a single turn so a forwarded album or split paste doesn't fan out " + "into N separate turns. Cascades from defaults.channels.telegram.coalesce."),
|
|
11071
11074
|
webhook_sources: exports_external.array(exports_external.enum(["github", "generic"])).optional().describe("External webhook sources allowed to ingest events into this agent's " + "log. POST /webhook/<agent>/<source> on the switchroom web server. " + "Each source has its own signature verification ('github' = " + "X-Hub-Signature-256 HMAC-SHA256, 'generic' = Bearer token). " + "Per-source secret read from ~/.switchroom/webhook-secrets.json " + "keyed by [agent][source]. Verified events append to " + "<agent>/telegram/webhook-events.jsonl for the agent to read on " + "demand. Off by default — webhook is the only untrusted-inbound " + "surface in the system, so opt-in is mandatory. " + "Cascades from defaults.channels.telegram.webhook_sources. " + "(Migrated from per-agent root in #596 — see #577.)"),
|
|
11072
11075
|
webhook_dispatch: exports_external.object({
|
|
11073
11076
|
github: exports_external.array(exports_external.object({
|
|
@@ -11815,6 +11815,9 @@ var TelegramChannelSchema = exports_external.object({
|
|
|
11815
11815
|
short_name: exports_external.string().optional().describe("Telegraph account display name. Defaults to the agent's slug. Used at " + "first-publish to lazily create the account; cached thereafter."),
|
|
11816
11816
|
author_name: exports_external.string().optional().describe("Telegraph article byline. Defaults to soul.name when set.")
|
|
11817
11817
|
}).optional().describe("Long-reply publishing via Telegraph (#579). When enabled, replies " + "above the threshold publish as a Telegraph article rendered in " + "Telegram via native Instant View. Off by default \u2014 content " + "residency is real for some personas (lawyer, health-coach with PHI). " + "Cascades from defaults.channels.telegram.telegraph. " + "(Migrated from per-agent root in #596.)"),
|
|
11818
|
+
coalesce: exports_external.object({
|
|
11819
|
+
window_ms: exports_external.number().int().nonnegative().optional().describe("Sliding-window (ms) for merging consecutive inbound messages from " + "the same sender+topic into ONE Claude turn. Each new message resets " + "the timer; the turn starts once the sender pauses for this long. " + "Catches forwarded bursts, pasted text the Telegram client split " + "into several messages, and mixed text+media forwards. Default 500. " + "Set 0 to disable (every message becomes its own turn). Raise for " + "users who think in multiple short messages; the trade-off is the " + "single-message turn start is delayed by this much (the \uD83D\uDC40 ack still " + "fires immediately, so perceived latency is unchanged).")
|
|
11820
|
+
}).optional().describe("Inbound coalescing \u2014 how the gateway groups rapid consecutive messages " + "into a single turn so a forwarded album or split paste doesn't fan out " + "into N separate turns. Cascades from defaults.channels.telegram.coalesce."),
|
|
11818
11821
|
webhook_sources: exports_external.array(exports_external.enum(["github", "generic"])).optional().describe("External webhook sources allowed to ingest events into this agent's " + "log. POST /webhook/<agent>/<source> on the switchroom web server. " + "Each source has its own signature verification ('github' = " + "X-Hub-Signature-256 HMAC-SHA256, 'generic' = Bearer token). " + "Per-source secret read from ~/.switchroom/webhook-secrets.json " + "keyed by [agent][source]. Verified events append to " + "<agent>/telegram/webhook-events.jsonl for the agent to read on " + "demand. Off by default \u2014 webhook is the only untrusted-inbound " + "surface in the system, so opt-in is mandatory. " + "Cascades from defaults.channels.telegram.webhook_sources. " + "(Migrated from per-agent root in #596 \u2014 see #577.)"),
|
|
11819
11822
|
webhook_dispatch: exports_external.object({
|
|
11820
11823
|
github: exports_external.array(exports_external.object({
|
package/dist/cli/switchroom.js
CHANGED
|
@@ -13632,6 +13632,9 @@ var init_schema = __esm(() => {
|
|
|
13632
13632
|
short_name: exports_external.string().optional().describe("Telegraph account display name. Defaults to the agent's slug. Used at " + "first-publish to lazily create the account; cached thereafter."),
|
|
13633
13633
|
author_name: exports_external.string().optional().describe("Telegraph article byline. Defaults to soul.name when set.")
|
|
13634
13634
|
}).optional().describe("Long-reply publishing via Telegraph (#579). When enabled, replies " + "above the threshold publish as a Telegraph article rendered in " + "Telegram via native Instant View. Off by default \u2014 content " + "residency is real for some personas (lawyer, health-coach with PHI). " + "Cascades from defaults.channels.telegram.telegraph. " + "(Migrated from per-agent root in #596.)"),
|
|
13635
|
+
coalesce: exports_external.object({
|
|
13636
|
+
window_ms: exports_external.number().int().nonnegative().optional().describe("Sliding-window (ms) for merging consecutive inbound messages from " + "the same sender+topic into ONE Claude turn. Each new message resets " + "the timer; the turn starts once the sender pauses for this long. " + "Catches forwarded bursts, pasted text the Telegram client split " + "into several messages, and mixed text+media forwards. Default 500. " + "Set 0 to disable (every message becomes its own turn). Raise for " + "users who think in multiple short messages; the trade-off is the " + "single-message turn start is delayed by this much (the \uD83D\uDC40 ack still " + "fires immediately, so perceived latency is unchanged).")
|
|
13637
|
+
}).optional().describe("Inbound coalescing \u2014 how the gateway groups rapid consecutive messages " + "into a single turn so a forwarded album or split paste doesn't fan out " + "into N separate turns. Cascades from defaults.channels.telegram.coalesce."),
|
|
13635
13638
|
webhook_sources: exports_external.array(exports_external.enum(["github", "generic"])).optional().describe("External webhook sources allowed to ingest events into this agent's " + "log. POST /webhook/<agent>/<source> on the switchroom web server. " + "Each source has its own signature verification ('github' = " + "X-Hub-Signature-256 HMAC-SHA256, 'generic' = Bearer token). " + "Per-source secret read from ~/.switchroom/webhook-secrets.json " + "keyed by [agent][source]. Verified events append to " + "<agent>/telegram/webhook-events.jsonl for the agent to read on " + "demand. Off by default \u2014 webhook is the only untrusted-inbound " + "surface in the system, so opt-in is mandatory. " + "Cascades from defaults.channels.telegram.webhook_sources. " + "(Migrated from per-agent root in #596 \u2014 see #577.)"),
|
|
13636
13639
|
webhook_dispatch: exports_external.object({
|
|
13637
13640
|
github: exports_external.array(exports_external.object({
|
|
@@ -49413,8 +49416,8 @@ var {
|
|
|
49413
49416
|
} = import__.default;
|
|
49414
49417
|
|
|
49415
49418
|
// src/build-info.ts
|
|
49416
|
-
var VERSION = "0.14.
|
|
49417
|
-
var COMMIT_SHA = "
|
|
49419
|
+
var VERSION = "0.14.18";
|
|
49420
|
+
var COMMIT_SHA = "dddb8617";
|
|
49418
49421
|
|
|
49419
49422
|
// src/cli/agent.ts
|
|
49420
49423
|
init_source();
|
|
@@ -52681,6 +52684,9 @@ function buildAccessJson2(agentConfig, telegramConfig, resolvedTopicId, userId)
|
|
|
52681
52684
|
if (tg?.telegraph) {
|
|
52682
52685
|
access.telegraph = tg.telegraph;
|
|
52683
52686
|
}
|
|
52687
|
+
if (typeof tg?.coalesce?.window_ms === "number") {
|
|
52688
|
+
access.coalescingGapMs = tg.coalesce.window_ms;
|
|
52689
|
+
}
|
|
52684
52690
|
return JSON.stringify(access, null, 2) + `
|
|
52685
52691
|
`;
|
|
52686
52692
|
}
|
|
@@ -13803,6 +13803,9 @@ var TelegramChannelSchema = exports_external.object({
|
|
|
13803
13803
|
short_name: exports_external.string().optional().describe("Telegraph account display name. Defaults to the agent's slug. Used at " + "first-publish to lazily create the account; cached thereafter."),
|
|
13804
13804
|
author_name: exports_external.string().optional().describe("Telegraph article byline. Defaults to soul.name when set.")
|
|
13805
13805
|
}).optional().describe("Long-reply publishing via Telegraph (#579). When enabled, replies " + "above the threshold publish as a Telegraph article rendered in " + "Telegram via native Instant View. Off by default — content " + "residency is real for some personas (lawyer, health-coach with PHI). " + "Cascades from defaults.channels.telegram.telegraph. " + "(Migrated from per-agent root in #596.)"),
|
|
13806
|
+
coalesce: exports_external.object({
|
|
13807
|
+
window_ms: exports_external.number().int().nonnegative().optional().describe("Sliding-window (ms) for merging consecutive inbound messages from " + "the same sender+topic into ONE Claude turn. Each new message resets " + "the timer; the turn starts once the sender pauses for this long. " + "Catches forwarded bursts, pasted text the Telegram client split " + "into several messages, and mixed text+media forwards. Default 500. " + "Set 0 to disable (every message becomes its own turn). Raise for " + "users who think in multiple short messages; the trade-off is the " + "single-message turn start is delayed by this much (the \uD83D\uDC40 ack still " + "fires immediately, so perceived latency is unchanged).")
|
|
13808
|
+
}).optional().describe("Inbound coalescing — how the gateway groups rapid consecutive messages " + "into a single turn so a forwarded album or split paste doesn't fan out " + "into N separate turns. Cascades from defaults.channels.telegram.coalesce."),
|
|
13806
13809
|
webhook_sources: exports_external.array(exports_external.enum(["github", "generic"])).optional().describe("External webhook sources allowed to ingest events into this agent's " + "log. POST /webhook/<agent>/<source> on the switchroom web server. " + "Each source has its own signature verification ('github' = " + "X-Hub-Signature-256 HMAC-SHA256, 'generic' = Bearer token). " + "Per-source secret read from ~/.switchroom/webhook-secrets.json " + "keyed by [agent][source]. Verified events append to " + "<agent>/telegram/webhook-events.jsonl for the agent to read on " + "demand. Off by default — webhook is the only untrusted-inbound " + "surface in the system, so opt-in is mandatory. " + "Cascades from defaults.channels.telegram.webhook_sources. " + "(Migrated from per-agent root in #596 — see #577.)"),
|
|
13807
13810
|
webhook_dispatch: exports_external.object({
|
|
13808
13811
|
github: exports_external.array(exports_external.object({
|
|
@@ -11383,6 +11383,9 @@ var init_schema = __esm(() => {
|
|
|
11383
11383
|
short_name: exports_external.string().optional().describe("Telegraph account display name. Defaults to the agent's slug. Used at " + "first-publish to lazily create the account; cached thereafter."),
|
|
11384
11384
|
author_name: exports_external.string().optional().describe("Telegraph article byline. Defaults to soul.name when set.")
|
|
11385
11385
|
}).optional().describe("Long-reply publishing via Telegraph (#579). When enabled, replies " + "above the threshold publish as a Telegraph article rendered in " + "Telegram via native Instant View. Off by default — content " + "residency is real for some personas (lawyer, health-coach with PHI). " + "Cascades from defaults.channels.telegram.telegraph. " + "(Migrated from per-agent root in #596.)"),
|
|
11386
|
+
coalesce: exports_external.object({
|
|
11387
|
+
window_ms: exports_external.number().int().nonnegative().optional().describe("Sliding-window (ms) for merging consecutive inbound messages from " + "the same sender+topic into ONE Claude turn. Each new message resets " + "the timer; the turn starts once the sender pauses for this long. " + "Catches forwarded bursts, pasted text the Telegram client split " + "into several messages, and mixed text+media forwards. Default 500. " + "Set 0 to disable (every message becomes its own turn). Raise for " + "users who think in multiple short messages; the trade-off is the " + "single-message turn start is delayed by this much (the \uD83D\uDC40 ack still " + "fires immediately, so perceived latency is unchanged).")
|
|
11388
|
+
}).optional().describe("Inbound coalescing — how the gateway groups rapid consecutive messages " + "into a single turn so a forwarded album or split paste doesn't fan out " + "into N separate turns. Cascades from defaults.channels.telegram.coalesce."),
|
|
11386
11389
|
webhook_sources: exports_external.array(exports_external.enum(["github", "generic"])).optional().describe("External webhook sources allowed to ingest events into this agent's " + "log. POST /webhook/<agent>/<source> on the switchroom web server. " + "Each source has its own signature verification ('github' = " + "X-Hub-Signature-256 HMAC-SHA256, 'generic' = Bearer token). " + "Per-source secret read from ~/.switchroom/webhook-secrets.json " + "keyed by [agent][source]. Verified events append to " + "<agent>/telegram/webhook-events.jsonl for the agent to read on " + "demand. Off by default — webhook is the only untrusted-inbound " + "surface in the system, so opt-in is mandatory. " + "Cascades from defaults.channels.telegram.webhook_sources. " + "(Migrated from per-agent root in #596 — see #577.)"),
|
|
11387
11390
|
webhook_dispatch: exports_external.object({
|
|
11388
11391
|
github: exports_external.array(exports_external.object({
|
|
@@ -11383,6 +11383,9 @@ var init_schema = __esm(() => {
|
|
|
11383
11383
|
short_name: exports_external.string().optional().describe("Telegraph account display name. Defaults to the agent's slug. Used at " + "first-publish to lazily create the account; cached thereafter."),
|
|
11384
11384
|
author_name: exports_external.string().optional().describe("Telegraph article byline. Defaults to soul.name when set.")
|
|
11385
11385
|
}).optional().describe("Long-reply publishing via Telegraph (#579). When enabled, replies " + "above the threshold publish as a Telegraph article rendered in " + "Telegram via native Instant View. Off by default — content " + "residency is real for some personas (lawyer, health-coach with PHI). " + "Cascades from defaults.channels.telegram.telegraph. " + "(Migrated from per-agent root in #596.)"),
|
|
11386
|
+
coalesce: exports_external.object({
|
|
11387
|
+
window_ms: exports_external.number().int().nonnegative().optional().describe("Sliding-window (ms) for merging consecutive inbound messages from " + "the same sender+topic into ONE Claude turn. Each new message resets " + "the timer; the turn starts once the sender pauses for this long. " + "Catches forwarded bursts, pasted text the Telegram client split " + "into several messages, and mixed text+media forwards. Default 500. " + "Set 0 to disable (every message becomes its own turn). Raise for " + "users who think in multiple short messages; the trade-off is the " + "single-message turn start is delayed by this much (the \uD83D\uDC40 ack still " + "fires immediately, so perceived latency is unchanged).")
|
|
11388
|
+
}).optional().describe("Inbound coalescing — how the gateway groups rapid consecutive messages " + "into a single turn so a forwarded album or split paste doesn't fan out " + "into N separate turns. Cascades from defaults.channels.telegram.coalesce."),
|
|
11386
11389
|
webhook_sources: exports_external.array(exports_external.enum(["github", "generic"])).optional().describe("External webhook sources allowed to ingest events into this agent's " + "log. POST /webhook/<agent>/<source> on the switchroom web server. " + "Each source has its own signature verification ('github' = " + "X-Hub-Signature-256 HMAC-SHA256, 'generic' = Bearer token). " + "Per-source secret read from ~/.switchroom/webhook-secrets.json " + "keyed by [agent][source]. Verified events append to " + "<agent>/telegram/webhook-events.jsonl for the agent to read on " + "demand. Off by default — webhook is the only untrusted-inbound " + "surface in the system, so opt-in is mandatory. " + "Cascades from defaults.channels.telegram.webhook_sources. " + "(Migrated from per-agent root in #596 — see #577.)"),
|
|
11387
11390
|
webhook_dispatch: exports_external.object({
|
|
11388
11391
|
github: exports_external.array(exports_external.object({
|
package/package.json
CHANGED
|
@@ -34,13 +34,14 @@ If both `queued` and `steering` are somehow present, `steering` wins (explicit o
|
|
|
34
34
|
**Self-narrate the classification.** At the top of your reply for any `steering` or `queued` message, include a brief italic one-liner so the user can correct you — e.g. `_↪️ Treating as steer on the prior task_` or `_📥 Queued as a new task_`.
|
|
35
35
|
|
|
36
36
|
**Formatting** (Telegram HTML — `reply` and `stream_reply` default to `format: "html"` and convert markdown for you):
|
|
37
|
-
-
|
|
37
|
+
- In ordinary one-or-two-line conversational replies, keep **bold** light — emphasis on key facts only, never decoration.
|
|
38
|
+
- **A multi-section message needs visual hierarchy or it reads as a flat wall.** When a reply groups several blocks — a status update, a "where things stand", a message announcing you're dispatching work, a summary with distinct buckets — give each section a **bold label on its own line** and separate sections with **one blank line**. Bold the label (e.g. `**✅ Done / running**`, `**🔲 Remaining**`, `**Next**`); the markdown→HTML converter renders `**label**` as bold, so the eye can find the structure. An emoji prefix with no bold leaves every line at equal weight — that's the plain-text dump to avoid. Bullet lines under a label are fine (`•` or `-`, one point per line).
|
|
38
39
|
- Use `inline code` for filenames, commands, identifiers
|
|
39
40
|
- Use ```fenced code blocks``` for multi-line code
|
|
40
|
-
-
|
|
41
|
-
- Don't use markdown headings (`##`) in replies —
|
|
42
|
-
- Keep lines short — long unwrapped lines are hard to read on mobile
|
|
43
|
-
- One idea per message
|
|
41
|
+
- Nested lists are not supported (Telegram flattens them awkwardly) — keep bullets one level deep.
|
|
42
|
+
- Don't use markdown headings (`##`) in replies — bold the label instead (`**Blockers**`, not `## Blockers`).
|
|
43
|
+
- Keep lines short — long unwrapped lines are hard to read on mobile.
|
|
44
|
+
- One idea per message for a quick exchange; a structured update can carry several ideas, but only when each sits under its own bold label with blank-line spacing between them.
|
|
44
45
|
|
|
45
46
|
**Sound human, not AI.** The canonical list of AI-tells to avoid lives in `SOUL.md` under "Never". Apply those rules to every outbound message, not just long-form. For drafts above ~500 chars, or where you're unsure if the voice lands right, invoke the bundled `/humanizer` skill for a polish pass (it catalogues 29 patterns in detail). If `HUMANIZER_VOICE_FILE` is set and readable, treat its content as the user's personal voice template: match length, tone, vocabulary, and formatting habits described there. The user can generate one with `/humanizer-calibrate`.
|
|
46
47
|
|
|
@@ -23737,6 +23737,9 @@ var init_schema = __esm(() => {
|
|
|
23737
23737
|
short_name: exports_external.string().optional().describe("Telegraph account display name. Defaults to the agent's slug. Used at " + "first-publish to lazily create the account; cached thereafter."),
|
|
23738
23738
|
author_name: exports_external.string().optional().describe("Telegraph article byline. Defaults to soul.name when set.")
|
|
23739
23739
|
}).optional().describe("Long-reply publishing via Telegraph (#579). When enabled, replies " + "above the threshold publish as a Telegraph article rendered in " + "Telegram via native Instant View. Off by default \u2014 content " + "residency is real for some personas (lawyer, health-coach with PHI). " + "Cascades from defaults.channels.telegram.telegraph. " + "(Migrated from per-agent root in #596.)"),
|
|
23740
|
+
coalesce: exports_external.object({
|
|
23741
|
+
window_ms: exports_external.number().int().nonnegative().optional().describe("Sliding-window (ms) for merging consecutive inbound messages from " + "the same sender+topic into ONE Claude turn. Each new message resets " + "the timer; the turn starts once the sender pauses for this long. " + "Catches forwarded bursts, pasted text the Telegram client split " + "into several messages, and mixed text+media forwards. Default 500. " + "Set 0 to disable (every message becomes its own turn). Raise for " + "users who think in multiple short messages; the trade-off is the " + "single-message turn start is delayed by this much (the \uD83D\uDC40 ack still " + "fires immediately, so perceived latency is unchanged).")
|
|
23742
|
+
}).optional().describe("Inbound coalescing \u2014 how the gateway groups rapid consecutive messages " + "into a single turn so a forwarded album or split paste doesn't fan out " + "into N separate turns. Cascades from defaults.channels.telegram.coalesce."),
|
|
23740
23743
|
webhook_sources: exports_external.array(exports_external.enum(["github", "generic"])).optional().describe("External webhook sources allowed to ingest events into this agent's " + "log. POST /webhook/<agent>/<source> on the switchroom web server. " + "Each source has its own signature verification ('github' = " + "X-Hub-Signature-256 HMAC-SHA256, 'generic' = Bearer token). " + "Per-source secret read from ~/.switchroom/webhook-secrets.json " + "keyed by [agent][source]. Verified events append to " + "<agent>/telegram/webhook-events.jsonl for the agent to read on " + "demand. Off by default \u2014 webhook is the only untrusted-inbound " + "surface in the system, so opt-in is mandatory. " + "Cascades from defaults.channels.telegram.webhook_sources. " + "(Migrated from per-agent root in #596 \u2014 see #577.)"),
|
|
23741
23744
|
webhook_dispatch: exports_external.object({
|
|
23742
23745
|
github: exports_external.array(exports_external.object({
|
|
@@ -46341,23 +46344,70 @@ function redeliverBufferedInbound(buffer, agent, send, spool) {
|
|
|
46341
46344
|
const pending = buffer.drain(agent);
|
|
46342
46345
|
let redelivered = 0;
|
|
46343
46346
|
let rebuffered = 0;
|
|
46344
|
-
for (const
|
|
46347
|
+
for (const { merged, originals } of planBufferedRedelivery(pending)) {
|
|
46345
46348
|
let delivered = false;
|
|
46346
46349
|
try {
|
|
46347
|
-
delivered = send(
|
|
46350
|
+
delivered = send(merged);
|
|
46348
46351
|
} catch {
|
|
46349
46352
|
delivered = false;
|
|
46350
46353
|
}
|
|
46351
46354
|
if (delivered) {
|
|
46352
|
-
|
|
46353
|
-
|
|
46355
|
+
for (const o of originals)
|
|
46356
|
+
spool?.ack(o);
|
|
46357
|
+
redelivered += originals.length;
|
|
46354
46358
|
} else {
|
|
46355
|
-
|
|
46356
|
-
|
|
46359
|
+
for (const o of originals)
|
|
46360
|
+
buffer.push(agent, o);
|
|
46361
|
+
rebuffered += originals.length;
|
|
46357
46362
|
}
|
|
46358
46363
|
}
|
|
46359
46364
|
return { drained: pending.length, redelivered, rebuffered };
|
|
46360
46365
|
}
|
|
46366
|
+
function isMergeableUserInbound(msg) {
|
|
46367
|
+
return msg.type === "inbound" && (msg.meta == null || msg.meta.source == null);
|
|
46368
|
+
}
|
|
46369
|
+
function inboundHasMedia(msg) {
|
|
46370
|
+
return msg.imagePath != null || msg.attachment != null;
|
|
46371
|
+
}
|
|
46372
|
+
function planBufferedRedelivery(pending) {
|
|
46373
|
+
const out = [];
|
|
46374
|
+
let run2 = [];
|
|
46375
|
+
let runHasMedia = false;
|
|
46376
|
+
const sameTarget = (a, b) => a.chatId === b.chatId && (a.threadId ?? null) === (b.threadId ?? null) && a.userId === b.userId;
|
|
46377
|
+
const flush = () => {
|
|
46378
|
+
if (run2.length === 0)
|
|
46379
|
+
return;
|
|
46380
|
+
out.push({ merged: run2.length === 1 ? run2[0] : mergeRun(run2), originals: run2 });
|
|
46381
|
+
run2 = [];
|
|
46382
|
+
runHasMedia = false;
|
|
46383
|
+
};
|
|
46384
|
+
for (const msg of pending) {
|
|
46385
|
+
const msgHasMedia = inboundHasMedia(msg);
|
|
46386
|
+
const canJoin = run2.length > 0 && isMergeableUserInbound(msg) && isMergeableUserInbound(run2[run2.length - 1]) && sameTarget(run2[run2.length - 1], msg) && !(runHasMedia && msgHasMedia);
|
|
46387
|
+
if (!canJoin)
|
|
46388
|
+
flush();
|
|
46389
|
+
run2.push(msg);
|
|
46390
|
+
runHasMedia = runHasMedia || msgHasMedia;
|
|
46391
|
+
}
|
|
46392
|
+
flush();
|
|
46393
|
+
return out;
|
|
46394
|
+
}
|
|
46395
|
+
function mergeRun(run2) {
|
|
46396
|
+
const last = run2[run2.length - 1];
|
|
46397
|
+
const mediaEntry = run2.find(inboundHasMedia);
|
|
46398
|
+
const merged = {
|
|
46399
|
+
...last,
|
|
46400
|
+
text: run2.map((m) => m.text).join(`
|
|
46401
|
+
`)
|
|
46402
|
+
};
|
|
46403
|
+
delete merged.imagePath;
|
|
46404
|
+
delete merged.attachment;
|
|
46405
|
+
if (mediaEntry?.imagePath != null)
|
|
46406
|
+
merged.imagePath = mediaEntry.imagePath;
|
|
46407
|
+
if (mediaEntry?.attachment != null)
|
|
46408
|
+
merged.attachment = mediaEntry.attachment;
|
|
46409
|
+
return merged;
|
|
46410
|
+
}
|
|
46361
46411
|
function idleDrainTick(buffer, agent, isBridgeAlive, send, spool) {
|
|
46362
46412
|
if (!agent)
|
|
46363
46413
|
return null;
|
|
@@ -46932,23 +46982,70 @@ function redeliverBufferedInbound2(buffer, agent, send, spool) {
|
|
|
46932
46982
|
const pending = buffer.drain(agent);
|
|
46933
46983
|
let redelivered = 0;
|
|
46934
46984
|
let rebuffered = 0;
|
|
46935
|
-
for (const
|
|
46985
|
+
for (const { merged, originals } of planBufferedRedelivery2(pending)) {
|
|
46936
46986
|
let delivered = false;
|
|
46937
46987
|
try {
|
|
46938
|
-
delivered = send(
|
|
46988
|
+
delivered = send(merged);
|
|
46939
46989
|
} catch {
|
|
46940
46990
|
delivered = false;
|
|
46941
46991
|
}
|
|
46942
46992
|
if (delivered) {
|
|
46943
|
-
|
|
46944
|
-
|
|
46993
|
+
for (const o of originals)
|
|
46994
|
+
spool?.ack(o);
|
|
46995
|
+
redelivered += originals.length;
|
|
46945
46996
|
} else {
|
|
46946
|
-
|
|
46947
|
-
|
|
46997
|
+
for (const o of originals)
|
|
46998
|
+
buffer.push(agent, o);
|
|
46999
|
+
rebuffered += originals.length;
|
|
46948
47000
|
}
|
|
46949
47001
|
}
|
|
46950
47002
|
return { drained: pending.length, redelivered, rebuffered };
|
|
46951
47003
|
}
|
|
47004
|
+
function isMergeableUserInbound2(msg) {
|
|
47005
|
+
return msg.type === "inbound" && (msg.meta == null || msg.meta.source == null);
|
|
47006
|
+
}
|
|
47007
|
+
function inboundHasMedia2(msg) {
|
|
47008
|
+
return msg.imagePath != null || msg.attachment != null;
|
|
47009
|
+
}
|
|
47010
|
+
function planBufferedRedelivery2(pending) {
|
|
47011
|
+
const out = [];
|
|
47012
|
+
let run2 = [];
|
|
47013
|
+
let runHasMedia = false;
|
|
47014
|
+
const sameTarget = (a, b) => a.chatId === b.chatId && (a.threadId ?? null) === (b.threadId ?? null) && a.userId === b.userId;
|
|
47015
|
+
const flush = () => {
|
|
47016
|
+
if (run2.length === 0)
|
|
47017
|
+
return;
|
|
47018
|
+
out.push({ merged: run2.length === 1 ? run2[0] : mergeRun2(run2), originals: run2 });
|
|
47019
|
+
run2 = [];
|
|
47020
|
+
runHasMedia = false;
|
|
47021
|
+
};
|
|
47022
|
+
for (const msg of pending) {
|
|
47023
|
+
const msgHasMedia = inboundHasMedia2(msg);
|
|
47024
|
+
const canJoin = run2.length > 0 && isMergeableUserInbound2(msg) && isMergeableUserInbound2(run2[run2.length - 1]) && sameTarget(run2[run2.length - 1], msg) && !(runHasMedia && msgHasMedia);
|
|
47025
|
+
if (!canJoin)
|
|
47026
|
+
flush();
|
|
47027
|
+
run2.push(msg);
|
|
47028
|
+
runHasMedia = runHasMedia || msgHasMedia;
|
|
47029
|
+
}
|
|
47030
|
+
flush();
|
|
47031
|
+
return out;
|
|
47032
|
+
}
|
|
47033
|
+
function mergeRun2(run2) {
|
|
47034
|
+
const last = run2[run2.length - 1];
|
|
47035
|
+
const mediaEntry = run2.find(inboundHasMedia2);
|
|
47036
|
+
const merged = {
|
|
47037
|
+
...last,
|
|
47038
|
+
text: run2.map((m) => m.text).join(`
|
|
47039
|
+
`)
|
|
47040
|
+
};
|
|
47041
|
+
delete merged.imagePath;
|
|
47042
|
+
delete merged.attachment;
|
|
47043
|
+
if (mediaEntry?.imagePath != null)
|
|
47044
|
+
merged.imagePath = mediaEntry.imagePath;
|
|
47045
|
+
if (mediaEntry?.attachment != null)
|
|
47046
|
+
merged.attachment = mediaEntry.attachment;
|
|
47047
|
+
return merged;
|
|
47048
|
+
}
|
|
46952
47049
|
|
|
46953
47050
|
// gateway/inbound-delivery-machine-dispatch.ts
|
|
46954
47051
|
var enabled6 = process.env.SWITCHROOM_DELIVERY_MACHINE_CUTOVER !== "0";
|
|
@@ -51140,10 +51237,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
51140
51237
|
}
|
|
51141
51238
|
|
|
51142
51239
|
// ../src/build-info.ts
|
|
51143
|
-
var VERSION = "0.14.
|
|
51144
|
-
var COMMIT_SHA = "
|
|
51145
|
-
var COMMIT_DATE = "2026-05-
|
|
51146
|
-
var LATEST_PR =
|
|
51240
|
+
var VERSION = "0.14.18";
|
|
51241
|
+
var COMMIT_SHA = "dddb8617";
|
|
51242
|
+
var COMMIT_DATE = "2026-05-30T23:35:26Z";
|
|
51243
|
+
var LATEST_PR = 2010;
|
|
51147
51244
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
51148
51245
|
|
|
51149
51246
|
// gateway/boot-version.ts
|
|
@@ -51605,6 +51702,34 @@ function applySubagentsSchema(db2) {
|
|
|
51605
51702
|
}
|
|
51606
51703
|
db2.exec("CREATE INDEX IF NOT EXISTS subagents_jsonl_id ON subagents(jsonl_agent_id)");
|
|
51607
51704
|
}
|
|
51705
|
+
function mapSubagentRow(row) {
|
|
51706
|
+
return {
|
|
51707
|
+
id: row.id,
|
|
51708
|
+
parent_session_id: row.parent_session_id,
|
|
51709
|
+
parent_turn_key: row.parent_turn_key,
|
|
51710
|
+
agent_type: row.agent_type,
|
|
51711
|
+
description: row.description,
|
|
51712
|
+
background: row.background !== 0,
|
|
51713
|
+
started_at: row.started_at,
|
|
51714
|
+
last_activity_at: row.last_activity_at,
|
|
51715
|
+
ended_at: row.ended_at,
|
|
51716
|
+
status: row.status,
|
|
51717
|
+
result_summary: row.result_summary,
|
|
51718
|
+
jsonl_agent_id: row.jsonl_agent_id
|
|
51719
|
+
};
|
|
51720
|
+
}
|
|
51721
|
+
function getSubagentByJsonlId(db2, jsonlAgentId) {
|
|
51722
|
+
const row = db2.prepare("SELECT * FROM subagents WHERE jsonl_agent_id = ?").get(jsonlAgentId);
|
|
51723
|
+
return row ? mapSubagentRow(row) : null;
|
|
51724
|
+
}
|
|
51725
|
+
|
|
51726
|
+
// gateway/worker-feed-dispatch.ts
|
|
51727
|
+
function resolveWorkerFeedDispatch(sub, watcherDescription) {
|
|
51728
|
+
return {
|
|
51729
|
+
isBackground: sub?.background ?? false,
|
|
51730
|
+
feedDescription: (sub?.description ?? "") || watcherDescription
|
|
51731
|
+
};
|
|
51732
|
+
}
|
|
51608
51733
|
|
|
51609
51734
|
// gateway/resolve-calling-subagent.ts
|
|
51610
51735
|
function resolveCallingSubagent(opts) {
|
|
@@ -52824,19 +52949,22 @@ function looksLikeAuthCode(text) {
|
|
|
52824
52949
|
return true;
|
|
52825
52950
|
return false;
|
|
52826
52951
|
}
|
|
52952
|
+
var bufferedAttachmentKeys = new Set;
|
|
52827
52953
|
var inboundCoalescer = createInboundCoalescer({
|
|
52828
52954
|
gapMs: () => loadAccess().coalescingGapMs ?? 500,
|
|
52829
52955
|
merge: (entries) => {
|
|
52830
52956
|
const last = entries[entries.length - 1];
|
|
52957
|
+
const withAttachment = entries.find((e) => e.downloadImage != null || e.attachment != null);
|
|
52831
52958
|
return {
|
|
52832
52959
|
text: entries.map((e) => e.text).join(`
|
|
52833
52960
|
`),
|
|
52834
52961
|
ctx: last.ctx,
|
|
52835
|
-
downloadImage:
|
|
52836
|
-
attachment:
|
|
52962
|
+
downloadImage: withAttachment?.downloadImage,
|
|
52963
|
+
attachment: withAttachment?.attachment
|
|
52837
52964
|
};
|
|
52838
52965
|
},
|
|
52839
|
-
onFlush: (
|
|
52966
|
+
onFlush: (key, merged) => {
|
|
52967
|
+
bufferedAttachmentKeys.delete(key);
|
|
52840
52968
|
handleInbound(merged.ctx, merged.text, merged.downloadImage, merged.attachment);
|
|
52841
52969
|
}
|
|
52842
52970
|
});
|
|
@@ -55992,19 +56120,29 @@ function safeName(s) {
|
|
|
55992
56120
|
return s?.replace(/[<>\[\]\r\n;]/g, "_");
|
|
55993
56121
|
}
|
|
55994
56122
|
async function handleInboundCoalesced(ctx, text, downloadImage, attachment) {
|
|
55995
|
-
if (downloadImage || attachment)
|
|
55996
|
-
return handleInbound(ctx, text, downloadImage, attachment);
|
|
55997
56123
|
if (parseInterruptMarker(text).isInterrupt) {
|
|
55998
|
-
return handleInbound(ctx, text,
|
|
56124
|
+
return handleInbound(ctx, text, downloadImage, attachment);
|
|
56125
|
+
}
|
|
56126
|
+
const hasAttachment = downloadImage != null || attachment != null;
|
|
56127
|
+
if (hasAttachment && ctx.message?.media_group_id != null) {
|
|
56128
|
+
return handleInbound(ctx, text, downloadImage, attachment);
|
|
55999
56129
|
}
|
|
56000
56130
|
const from = ctx.from;
|
|
56001
56131
|
if (!from)
|
|
56002
56132
|
return;
|
|
56133
|
+
if (hasAttachment) {
|
|
56134
|
+
const probeKey = inboundCoalesceKey(String(ctx.chat.id), ctx.message?.message_thread_id, String(from.id));
|
|
56135
|
+
if (bufferedAttachmentKeys.has(probeKey)) {
|
|
56136
|
+
return handleInbound(ctx, text, downloadImage, attachment);
|
|
56137
|
+
}
|
|
56138
|
+
}
|
|
56003
56139
|
maybeEarlyAckReaction(ctx, from);
|
|
56004
56140
|
const key = inboundCoalesceKey(String(ctx.chat.id), ctx.message?.message_thread_id, String(from.id));
|
|
56005
56141
|
const result = inboundCoalescer.enqueue(key, { text, ctx, downloadImage, attachment });
|
|
56006
56142
|
if (result.bypass)
|
|
56007
|
-
return handleInbound(ctx, text,
|
|
56143
|
+
return handleInbound(ctx, text, downloadImage, attachment);
|
|
56144
|
+
if (hasAttachment)
|
|
56145
|
+
bufferedAttachmentKeys.add(key);
|
|
56008
56146
|
}
|
|
56009
56147
|
function maybeEarlyAckReaction(ctx, from) {
|
|
56010
56148
|
const msgId = ctx.message?.message_id;
|
|
@@ -60270,7 +60408,7 @@ bot.on("message:text", async (ctx) => {
|
|
|
60270
60408
|
});
|
|
60271
60409
|
bot.on("message:photo", async (ctx) => {
|
|
60272
60410
|
const caption = ctx.message.caption ?? "(photo)";
|
|
60273
|
-
await
|
|
60411
|
+
await handleInboundCoalesced(ctx, caption, async () => {
|
|
60274
60412
|
const photos = ctx.message.photo;
|
|
60275
60413
|
const best = photos[photos.length - 1];
|
|
60276
60414
|
try {
|
|
@@ -60306,7 +60444,7 @@ bot.on("message:photo", async (ctx) => {
|
|
|
60306
60444
|
bot.on("message:document", async (ctx) => {
|
|
60307
60445
|
const doc = ctx.message.document;
|
|
60308
60446
|
const name = safeName(doc.file_name);
|
|
60309
|
-
await
|
|
60447
|
+
await handleInboundCoalesced(ctx, ctx.message.caption ?? `(document: ${name ?? "file"})`, undefined, { kind: "document", file_id: doc.file_id, size: doc.file_size, mime: doc.mime_type, name });
|
|
60310
60448
|
});
|
|
60311
60449
|
bot.on("message:voice", async (ctx) => {
|
|
60312
60450
|
const voice = ctx.message.voice;
|
|
@@ -60318,7 +60456,7 @@ bot.on("message:voice", async (ctx) => {
|
|
|
60318
60456
|
const text = ctx.message.caption ? `${ctx.message.caption}
|
|
60319
60457
|
|
|
60320
60458
|
[voice transcript] ${transcript}` : `[voice transcript] ${transcript}`;
|
|
60321
|
-
await
|
|
60459
|
+
await handleInboundCoalesced(ctx, text, undefined, {
|
|
60322
60460
|
kind: "voice",
|
|
60323
60461
|
file_id: voice.file_id,
|
|
60324
60462
|
size: voice.file_size,
|
|
@@ -60327,7 +60465,7 @@ bot.on("message:voice", async (ctx) => {
|
|
|
60327
60465
|
return;
|
|
60328
60466
|
}
|
|
60329
60467
|
}
|
|
60330
|
-
await
|
|
60468
|
+
await handleInboundCoalesced(ctx, ctx.message.caption ?? "(voice message)", undefined, { kind: "voice", file_id: voice.file_id, size: voice.file_size, mime: voice.mime_type });
|
|
60331
60469
|
});
|
|
60332
60470
|
async function maybeTranscribeVoice(fileId, mimeType, language) {
|
|
60333
60471
|
let apiKey = null;
|
|
@@ -60387,15 +60525,15 @@ async function maybeTranscribeVoice(fileId, mimeType, language) {
|
|
|
60387
60525
|
bot.on("message:audio", async (ctx) => {
|
|
60388
60526
|
const audio = ctx.message.audio;
|
|
60389
60527
|
const name = safeName(audio.file_name);
|
|
60390
|
-
await
|
|
60528
|
+
await handleInboundCoalesced(ctx, ctx.message.caption ?? `(audio: ${safeName(audio.title) ?? name ?? "audio"})`, undefined, { kind: "audio", file_id: audio.file_id, size: audio.file_size, mime: audio.mime_type, name });
|
|
60391
60529
|
});
|
|
60392
60530
|
bot.on("message:video", async (ctx) => {
|
|
60393
60531
|
const video = ctx.message.video;
|
|
60394
|
-
await
|
|
60532
|
+
await handleInboundCoalesced(ctx, ctx.message.caption ?? "(video)", undefined, { kind: "video", file_id: video.file_id, size: video.file_size, mime: video.mime_type, name: safeName(video.file_name) });
|
|
60395
60533
|
});
|
|
60396
60534
|
bot.on("message:video_note", async (ctx) => {
|
|
60397
60535
|
const vn = ctx.message.video_note;
|
|
60398
|
-
await
|
|
60536
|
+
await handleInboundCoalesced(ctx, "(video note)", undefined, { kind: "video_note", file_id: vn.file_id, size: vn.file_size });
|
|
60399
60537
|
});
|
|
60400
60538
|
bot.on("message:sticker", async (ctx) => {
|
|
60401
60539
|
const sticker = ctx.message.sticker;
|
|
@@ -60405,13 +60543,13 @@ bot.on("message:sticker", async (ctx) => {
|
|
|
60405
60543
|
if (sticker.set_name)
|
|
60406
60544
|
parts.push(`from "${sticker.set_name}"`);
|
|
60407
60545
|
const text = parts.length > 0 ? `(sticker \u2014 ${parts.join(" ")})` : "(sticker)";
|
|
60408
|
-
await
|
|
60546
|
+
await handleInboundCoalesced(ctx, text, undefined, { kind: "sticker", file_id: sticker.file_id, size: sticker.file_size });
|
|
60409
60547
|
});
|
|
60410
60548
|
bot.on("message:animation", async (ctx) => {
|
|
60411
60549
|
const animation = ctx.message.animation;
|
|
60412
60550
|
const caption = ctx.message.caption;
|
|
60413
60551
|
const text = caption ? `(gif) ${caption}` : "(gif)";
|
|
60414
|
-
await
|
|
60552
|
+
await handleInboundCoalesced(ctx, text, undefined, {
|
|
60415
60553
|
kind: "animation",
|
|
60416
60554
|
file_id: animation.file_id,
|
|
60417
60555
|
size: animation.file_size,
|
|
@@ -61332,8 +61470,6 @@ var didOneTimeSetup = false;
|
|
|
61332
61470
|
onFinish: ({ agentId, outcome, description, resultText, toolCount, durationMs }) => {
|
|
61333
61471
|
deferredDoneReactions.promote();
|
|
61334
61472
|
let fleetChatId = "";
|
|
61335
|
-
let isBackground = false;
|
|
61336
|
-
let dispatchDesc = "";
|
|
61337
61473
|
try {
|
|
61338
61474
|
const fleets = progressDriver?.peekAllFleets() ?? [];
|
|
61339
61475
|
for (const f of fleets) {
|
|
@@ -61343,18 +61479,16 @@ var didOneTimeSetup = false;
|
|
|
61343
61479
|
}
|
|
61344
61480
|
}
|
|
61345
61481
|
} catch {}
|
|
61482
|
+
let dispatch = resolveWorkerFeedDispatch(null, description);
|
|
61346
61483
|
if (turnsDb != null) {
|
|
61347
61484
|
try {
|
|
61348
|
-
|
|
61349
|
-
if (row != null) {
|
|
61350
|
-
isBackground = row.background === 1;
|
|
61351
|
-
dispatchDesc = row.description ?? "";
|
|
61352
|
-
}
|
|
61485
|
+
dispatch = resolveWorkerFeedDispatch(getSubagentByJsonlId(turnsDb, agentId), description);
|
|
61353
61486
|
} catch {}
|
|
61354
61487
|
}
|
|
61488
|
+
const isBackground = dispatch.isBackground;
|
|
61355
61489
|
if (workerFeedEnabled) {
|
|
61356
61490
|
workerActivityFeed.finish(agentId, {
|
|
61357
|
-
description:
|
|
61491
|
+
description: dispatch.feedDescription,
|
|
61358
61492
|
lastTool: null,
|
|
61359
61493
|
toolCount,
|
|
61360
61494
|
latestSummary: resultText,
|
|
@@ -61396,8 +61530,6 @@ var didOneTimeSetup = false;
|
|
|
61396
61530
|
},
|
|
61397
61531
|
onProgress: ({ agentId, description, latestSummary, elapsedMs, prevBucketIdx, setBucketIdx, lastTool, toolCount }) => {
|
|
61398
61532
|
let fleetChatId = "";
|
|
61399
|
-
let isBackground = false;
|
|
61400
|
-
let dispatchDesc = "";
|
|
61401
61533
|
try {
|
|
61402
61534
|
const fleets = progressDriver?.peekAllFleets() ?? [];
|
|
61403
61535
|
for (const f of fleets) {
|
|
@@ -61407,20 +61539,18 @@ var didOneTimeSetup = false;
|
|
|
61407
61539
|
}
|
|
61408
61540
|
}
|
|
61409
61541
|
} catch {}
|
|
61542
|
+
let dispatch = resolveWorkerFeedDispatch(null, description);
|
|
61410
61543
|
if (turnsDb != null) {
|
|
61411
61544
|
try {
|
|
61412
|
-
|
|
61413
|
-
if (row != null) {
|
|
61414
|
-
isBackground = row.background === 1;
|
|
61415
|
-
dispatchDesc = row.description ?? "";
|
|
61416
|
-
}
|
|
61545
|
+
dispatch = resolveWorkerFeedDispatch(getSubagentByJsonlId(turnsDb, agentId), description);
|
|
61417
61546
|
} catch {}
|
|
61418
61547
|
}
|
|
61548
|
+
const isBackground = dispatch.isBackground;
|
|
61419
61549
|
if (!isBackground)
|
|
61420
61550
|
return;
|
|
61421
61551
|
if (workerFeedEnabled) {
|
|
61422
61552
|
workerActivityFeed.update(agentId, fleetChatId || (loadAccess().allowFrom[0] ?? ""), {
|
|
61423
|
-
description:
|
|
61553
|
+
description: dispatch.feedDescription,
|
|
61424
61554
|
lastTool,
|
|
61425
61555
|
toolCount,
|
|
61426
61556
|
latestSummary,
|