switchroom 0.14.1 → 0.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/switchroom.js
CHANGED
|
@@ -49278,8 +49278,8 @@ var {
|
|
|
49278
49278
|
} = import__.default;
|
|
49279
49279
|
|
|
49280
49280
|
// src/build-info.ts
|
|
49281
|
-
var VERSION = "0.14.
|
|
49282
|
-
var COMMIT_SHA = "
|
|
49281
|
+
var VERSION = "0.14.2";
|
|
49282
|
+
var COMMIT_SHA = "3c7d0238";
|
|
49283
49283
|
|
|
49284
49284
|
// src/cli/agent.ts
|
|
49285
49285
|
init_source();
|
package/package.json
CHANGED
|
@@ -31971,6 +31971,26 @@ function describeToolUse(toolName, input) {
|
|
|
31971
31971
|
return "Working\u2026";
|
|
31972
31972
|
}
|
|
31973
31973
|
}
|
|
31974
|
+
var MIRROR_MAX_LINES = 6;
|
|
31975
|
+
function appendActivityLine(lines, toolName, input) {
|
|
31976
|
+
const line = describeToolUse(toolName, input);
|
|
31977
|
+
if (line == null)
|
|
31978
|
+
return null;
|
|
31979
|
+
if (lines.length === 0 || lines[lines.length - 1] !== line) {
|
|
31980
|
+
lines.push(line);
|
|
31981
|
+
}
|
|
31982
|
+
return renderActivityFeed(lines);
|
|
31983
|
+
}
|
|
31984
|
+
function renderActivityFeed(lines) {
|
|
31985
|
+
if (lines.length === 0)
|
|
31986
|
+
return null;
|
|
31987
|
+
const shown = lines.slice(-MIRROR_MAX_LINES);
|
|
31988
|
+
const hidden = lines.length - shown.length;
|
|
31989
|
+
const body = shown.map((l) => `\u00b7 ${l}`).join(`
|
|
31990
|
+
`);
|
|
31991
|
+
return hidden > 0 ? `\u00b7 +${hidden} earlier\u2026
|
|
31992
|
+
${body}` : body;
|
|
31993
|
+
}
|
|
31974
31994
|
|
|
31975
31995
|
// tool-labels.ts
|
|
31976
31996
|
var MAX_LABEL_CHARS = 60;
|
|
@@ -50143,10 +50163,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
50143
50163
|
}
|
|
50144
50164
|
|
|
50145
50165
|
// ../src/build-info.ts
|
|
50146
|
-
var VERSION = "0.14.
|
|
50147
|
-
var COMMIT_SHA = "
|
|
50148
|
-
var COMMIT_DATE = "2026-05-28T07:
|
|
50149
|
-
var LATEST_PR =
|
|
50166
|
+
var VERSION = "0.14.2";
|
|
50167
|
+
var COMMIT_SHA = "3c7d0238";
|
|
50168
|
+
var COMMIT_DATE = "2026-05-28T07:47:55Z";
|
|
50169
|
+
var LATEST_PR = 1958;
|
|
50150
50170
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
50151
50171
|
|
|
50152
50172
|
// gateway/boot-version.ts
|
|
@@ -54223,6 +54243,7 @@ function handleSessionEvent(ev) {
|
|
|
54223
54243
|
activityInFlight: null,
|
|
54224
54244
|
activityPendingRender: null,
|
|
54225
54245
|
activityLastSentRender: null,
|
|
54246
|
+
mirrorLines: [],
|
|
54226
54247
|
answerStream: null,
|
|
54227
54248
|
isDm: isDmChatId(ev.chatId)
|
|
54228
54249
|
};
|
|
@@ -54293,7 +54314,7 @@ function handleSessionEvent(ev) {
|
|
|
54293
54314
|
}
|
|
54294
54315
|
}
|
|
54295
54316
|
if (!turn.replyCalled && !isTelegramSurfaceTool(name)) {
|
|
54296
|
-
const rendered = DRAFT_MIRROR_ENABLED ?
|
|
54317
|
+
const rendered = DRAFT_MIRROR_ENABLED ? appendActivityLine(turn.mirrorLines, name, ev.input) : registerAndRender(turn.toolActivity, name);
|
|
54297
54318
|
if (rendered != null) {
|
|
54298
54319
|
turn.activityPendingRender = rendered;
|
|
54299
54320
|
if (turn.activityInFlight == null) {
|
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
makeEmptyActivityState,
|
|
59
59
|
registerAndRender,
|
|
60
60
|
describeToolUse,
|
|
61
|
+
appendActivityLine,
|
|
61
62
|
type ActivityState,
|
|
62
63
|
} from '../tool-activity-summary.js'
|
|
63
64
|
import { toolLabel } from '../tool-labels.js'
|
|
@@ -1338,6 +1339,11 @@ type CurrentTurn = {
|
|
|
1338
1339
|
activityInFlight: Promise<void> | null
|
|
1339
1340
|
activityPendingRender: string | null
|
|
1340
1341
|
activityLastSentRender: string | null
|
|
1342
|
+
// Draft-mirror Phase 2: accumulating friendly-action feed for this turn
|
|
1343
|
+
// (DRAFT_MIRROR only). Each non-surface tool_use appends a line via
|
|
1344
|
+
// `appendActivityLine`; the feed renders as a capped chronological list
|
|
1345
|
+
// in the ephemeral draft and clears on reply. Reset per turn.
|
|
1346
|
+
mirrorLines: string[]
|
|
1341
1347
|
// Issue #195 — answer-lane streaming. Lazily created on the first text
|
|
1342
1348
|
// event of a turn (once enough text has accumulated, the stream itself
|
|
1343
1349
|
// gates on minInitialChars). Materialized and cleared at turn_end.
|
|
@@ -7001,6 +7007,7 @@ function handleSessionEvent(ev: SessionEvent): void {
|
|
|
7001
7007
|
activityInFlight: null,
|
|
7002
7008
|
activityPendingRender: null,
|
|
7003
7009
|
activityLastSentRender: null,
|
|
7010
|
+
mirrorLines: [],
|
|
7004
7011
|
answerStream: null,
|
|
7005
7012
|
isDm: isDmChatId(ev.chatId),
|
|
7006
7013
|
}
|
|
@@ -7146,11 +7153,12 @@ function handleSessionEvent(ev: SessionEvent): void {
|
|
|
7146
7153
|
// exactly once at a time and re-running until pending matches
|
|
7147
7154
|
// the last-sent. Captures `turn` so a late drain after turn-swap
|
|
7148
7155
|
// can't corrupt the next turn's atom.
|
|
7149
|
-
// DRAFT_MIRROR (RFC draft-mirror-preview):
|
|
7150
|
-
// human-friendly
|
|
7151
|
-
// descriptive field (Bash.description, Read/Edit file
|
|
7152
|
-
// hindsight→"Searching memory", etc. — see describeToolUse
|
|
7153
|
-
//
|
|
7156
|
+
// DRAFT_MIRROR (RFC draft-mirror-preview): accumulate each tool_use
|
|
7157
|
+
// into a human-friendly running feed in the live preview, using the
|
|
7158
|
+
// model-authored descriptive field (Bash.description, Read/Edit file
|
|
7159
|
+
// basename, hindsight→"Searching memory", etc. — see describeToolUse
|
|
7160
|
+
// / appendActivityLine). The draft shows the turn's actions as a
|
|
7161
|
+
// capped chronological list (Claude Code-style), clears on reply.
|
|
7154
7162
|
// Never surfaces raw shell/query syntax — option A, uniform across
|
|
7155
7163
|
// code + non-code agents.
|
|
7156
7164
|
//
|
|
@@ -7159,7 +7167,7 @@ function handleSessionEvent(ev: SessionEvent): void {
|
|
|
7159
7167
|
// pre-draft-mirror behavior.
|
|
7160
7168
|
if (!turn.replyCalled && !isTelegramSurfaceTool(name)) {
|
|
7161
7169
|
const rendered = DRAFT_MIRROR_ENABLED
|
|
7162
|
-
?
|
|
7170
|
+
? appendActivityLine(turn.mirrorLines, name, ev.input)
|
|
7163
7171
|
: registerAndRender(turn.toolActivity, name)
|
|
7164
7172
|
if (rendered != null) {
|
|
7165
7173
|
turn.activityPendingRender = rendered
|
|
@@ -6,6 +6,9 @@ import {
|
|
|
6
6
|
registerAndRender,
|
|
7
7
|
verbForTool,
|
|
8
8
|
describeToolUse,
|
|
9
|
+
appendActivityLine,
|
|
10
|
+
renderActivityFeed,
|
|
11
|
+
MIRROR_MAX_LINES,
|
|
9
12
|
} from "../tool-activity-summary.js";
|
|
10
13
|
|
|
11
14
|
describe("describeToolUse — friendly per-tool rendering (draft-mirror)", () => {
|
|
@@ -283,3 +286,45 @@ describe("registerAndRender — ergonomic full-pipeline call", () => {
|
|
|
283
286
|
expect(s.firstToolName).toBeNull();
|
|
284
287
|
});
|
|
285
288
|
});
|
|
289
|
+
|
|
290
|
+
describe("appendActivityLine + renderActivityFeed — accumulating draft feed", () => {
|
|
291
|
+
it("accumulates distinct actions chronologically (newest last)", () => {
|
|
292
|
+
const lines: string[] = [];
|
|
293
|
+
expect(appendActivityLine(lines, "Read", { file_path: "a/gateway.ts" })).toBe(
|
|
294
|
+
"· Reading gateway.ts",
|
|
295
|
+
);
|
|
296
|
+
expect(appendActivityLine(lines, "mcp__hindsight__reflect", { query: "x" })).toBe(
|
|
297
|
+
"· Reading gateway.ts\n· Searching memory",
|
|
298
|
+
);
|
|
299
|
+
expect(appendActivityLine(lines, "Bash", { command: "ls", description: "List workspace" })).toBe(
|
|
300
|
+
"· Reading gateway.ts\n· Searching memory\n· List workspace",
|
|
301
|
+
);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it("collapses consecutive exact-duplicate lines", () => {
|
|
305
|
+
const lines: string[] = [];
|
|
306
|
+
appendActivityLine(lines, "Read", { file_path: "a.ts" });
|
|
307
|
+
appendActivityLine(lines, "Read", { file_path: "a.ts" }); // dup → collapsed
|
|
308
|
+
expect(lines).toEqual(["Reading a.ts"]);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it("returns null (no feed update) for surface tools", () => {
|
|
312
|
+
const lines: string[] = [];
|
|
313
|
+
expect(appendActivityLine(lines, "mcp__switchroom-telegram__reply", { text: "hi" })).toBeNull();
|
|
314
|
+
expect(lines).toEqual([]);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it("caps to the last MIRROR_MAX_LINES with a '+N earlier' header", () => {
|
|
318
|
+
const lines = Array.from({ length: 9 }, (_, i) => `Action ${i + 1}`);
|
|
319
|
+
const out = renderActivityFeed(lines)!;
|
|
320
|
+
expect(out.startsWith("· +3 earlier…\n")).toBe(true);
|
|
321
|
+
// Only the last 6 actions are shown.
|
|
322
|
+
expect(out).toContain("· Action 4");
|
|
323
|
+
expect(out).toContain("· Action 9");
|
|
324
|
+
expect(out).not.toContain("· Action 3\n");
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it("renderActivityFeed returns null on empty", () => {
|
|
328
|
+
expect(renderActivityFeed([])).toBeNull();
|
|
329
|
+
});
|
|
330
|
+
});
|
|
@@ -335,3 +335,50 @@ export function describeToolUse(
|
|
|
335
335
|
return "Working…";
|
|
336
336
|
}
|
|
337
337
|
}
|
|
338
|
+
|
|
339
|
+
// ─── Accumulating activity feed (draft-mirror Phase 2) ──────────────────────
|
|
340
|
+
//
|
|
341
|
+
// Phase 1 showed only the latest action; this accumulates the turn's actions
|
|
342
|
+
// into a running feed — like Claude Code's own UI — streamed into the
|
|
343
|
+
// ephemeral draft and cleared on reply. Chronological (oldest first, newest
|
|
344
|
+
// last), consecutive exact-duplicates collapsed, capped to the most recent
|
|
345
|
+
// MIRROR_MAX_LINES with a "+N earlier" header so a heavy turn stays readable
|
|
346
|
+
// inside Telegram's compose-area draft.
|
|
347
|
+
|
|
348
|
+
export const MIRROR_MAX_LINES = 6;
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Append a tool_use's friendly line to the running feed (mutates `lines`)
|
|
352
|
+
* and return the rendered draft body — or null when the tool is a surface
|
|
353
|
+
* tool / produced no line (caller skips the draft update).
|
|
354
|
+
*
|
|
355
|
+
* Dedups only consecutive identical lines (e.g. a burst of parallel Reads of
|
|
356
|
+
* the same file) so distinct actions are all preserved.
|
|
357
|
+
*/
|
|
358
|
+
export function appendActivityLine(
|
|
359
|
+
lines: string[],
|
|
360
|
+
toolName: string,
|
|
361
|
+
input: Record<string, unknown> | undefined,
|
|
362
|
+
): string | null {
|
|
363
|
+
const line = describeToolUse(toolName, input);
|
|
364
|
+
if (line == null) return null;
|
|
365
|
+
if (lines.length === 0 || lines[lines.length - 1] !== line) {
|
|
366
|
+
lines.push(line);
|
|
367
|
+
}
|
|
368
|
+
return renderActivityFeed(lines);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Render the accumulated feed as a plain-text block (one action per line).
|
|
373
|
+
* The caller HTML-escapes + wraps it for Telegram. Returns null when empty.
|
|
374
|
+
*
|
|
375
|
+
* Newest-last chronological order; capped to the last MIRROR_MAX_LINES with a
|
|
376
|
+
* dim "+N earlier" header when the turn ran longer.
|
|
377
|
+
*/
|
|
378
|
+
export function renderActivityFeed(lines: string[]): string | null {
|
|
379
|
+
if (lines.length === 0) return null;
|
|
380
|
+
const shown = lines.slice(-MIRROR_MAX_LINES);
|
|
381
|
+
const hidden = lines.length - shown.length;
|
|
382
|
+
const body = shown.map((l) => `· ${l}`).join("\n");
|
|
383
|
+
return hidden > 0 ? `· +${hidden} earlier…\n${body}` : body;
|
|
384
|
+
}
|