switchroom 0.16.20 → 0.16.22
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
|
@@ -51692,8 +51692,8 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
51692
51692
|
import { dirname, join } from "node:path";
|
|
51693
51693
|
|
|
51694
51694
|
// src/build-info.ts
|
|
51695
|
-
var VERSION = "0.16.
|
|
51696
|
-
var COMMIT_SHA = "
|
|
51695
|
+
var VERSION = "0.16.22";
|
|
51696
|
+
var COMMIT_SHA = "ea287bf4";
|
|
51697
51697
|
|
|
51698
51698
|
// src/cli/resolve-version.ts
|
|
51699
51699
|
function readPackageVersion() {
|
|
@@ -77500,6 +77500,7 @@ function resolveChannelTarget(config, agentName) {
|
|
|
77500
77500
|
}
|
|
77501
77501
|
|
|
77502
77502
|
// src/web/hermes-adapter.ts
|
|
77503
|
+
init_inject();
|
|
77503
77504
|
function rpcOk(id, result) {
|
|
77504
77505
|
return { jsonrpc: "2.0", id, result };
|
|
77505
77506
|
}
|
|
@@ -77607,7 +77608,127 @@ async function injectInbound(agentsDir, agentName, chatId, threadId, text, promp
|
|
|
77607
77608
|
});
|
|
77608
77609
|
});
|
|
77609
77610
|
}
|
|
77610
|
-
|
|
77611
|
+
function cronJobId(agent, index) {
|
|
77612
|
+
return `${agent}~${index}`;
|
|
77613
|
+
}
|
|
77614
|
+
function scheduleToCronJobs(schedule) {
|
|
77615
|
+
return schedule.entries.map((entry) => {
|
|
77616
|
+
const id = cronJobId(entry.agent, entry.scheduleIndex);
|
|
77617
|
+
const fires = schedule.recentByAgent[entry.agent] ?? [];
|
|
77618
|
+
const entryFires = fires.filter((f) => f.scheduleIndex === entry.scheduleIndex);
|
|
77619
|
+
const lastFire = entryFires[entryFires.length - 1];
|
|
77620
|
+
return {
|
|
77621
|
+
id,
|
|
77622
|
+
enabled: true,
|
|
77623
|
+
name: entry.name ?? null,
|
|
77624
|
+
prompt: entry.prompt ?? null,
|
|
77625
|
+
schedule: { expr: entry.cron, display: entry.cron, kind: entry.kind ?? "prompt" },
|
|
77626
|
+
schedule_display: entry.cron,
|
|
77627
|
+
last_run_at: lastFire ? new Date(lastFire.finishedAt).toISOString() : null,
|
|
77628
|
+
last_error: lastFire?.exitCode !== 0 ? lastFire?.outputSummary ?? null : null,
|
|
77629
|
+
state: lastFire ? lastFire.exitCode === 0 ? "success" : "error" : null
|
|
77630
|
+
};
|
|
77631
|
+
});
|
|
77632
|
+
}
|
|
77633
|
+
function cronJobRuns(schedule, jobId) {
|
|
77634
|
+
const tilde = jobId.lastIndexOf("~");
|
|
77635
|
+
if (tilde === -1)
|
|
77636
|
+
return [];
|
|
77637
|
+
const agent = jobId.slice(0, tilde);
|
|
77638
|
+
const index = parseInt(jobId.slice(tilde + 1), 10);
|
|
77639
|
+
const fires = schedule.recentByAgent[agent] ?? [];
|
|
77640
|
+
return fires.filter((f) => f.scheduleIndex === index).map((f) => ({
|
|
77641
|
+
id: `${jobId}/${f.startedAt}`,
|
|
77642
|
+
job_id: jobId,
|
|
77643
|
+
started_at: new Date(f.startedAt).toISOString(),
|
|
77644
|
+
finished_at: new Date(f.finishedAt).toISOString(),
|
|
77645
|
+
exit_code: f.exitCode,
|
|
77646
|
+
output: f.outputSummary,
|
|
77647
|
+
status: f.exitCode === 0 ? "success" : "error"
|
|
77648
|
+
}));
|
|
77649
|
+
}
|
|
77650
|
+
function turnsToMessages(turns) {
|
|
77651
|
+
const messages = [];
|
|
77652
|
+
for (const t of turns) {
|
|
77653
|
+
if (t.user_prompt_preview) {
|
|
77654
|
+
messages.push({
|
|
77655
|
+
role: "user",
|
|
77656
|
+
content: t.user_prompt_preview,
|
|
77657
|
+
timestamp: Math.floor(t.started_at / 1000)
|
|
77658
|
+
});
|
|
77659
|
+
}
|
|
77660
|
+
if (t.assistant_reply_preview) {
|
|
77661
|
+
messages.push({
|
|
77662
|
+
role: "assistant",
|
|
77663
|
+
content: t.assistant_reply_preview,
|
|
77664
|
+
timestamp: t.ended_at != null ? Math.floor(t.ended_at / 1000) : Math.floor(t.started_at / 1000)
|
|
77665
|
+
});
|
|
77666
|
+
}
|
|
77667
|
+
}
|
|
77668
|
+
return messages;
|
|
77669
|
+
}
|
|
77670
|
+
var SWITCHROOM_MODELS = [
|
|
77671
|
+
"claude-sonnet-4-6",
|
|
77672
|
+
"claude-opus-4-8",
|
|
77673
|
+
"claude-haiku-4-5-20251001",
|
|
77674
|
+
"claude-fable-5"
|
|
77675
|
+
];
|
|
77676
|
+
function switchroomModelOptions() {
|
|
77677
|
+
return {
|
|
77678
|
+
model: "claude-sonnet-4-6",
|
|
77679
|
+
provider: "switchroom",
|
|
77680
|
+
providers: [
|
|
77681
|
+
{
|
|
77682
|
+
slug: "switchroom",
|
|
77683
|
+
name: "Switchroom (Claude)",
|
|
77684
|
+
is_current: true,
|
|
77685
|
+
authenticated: true,
|
|
77686
|
+
models: SWITCHROOM_MODELS,
|
|
77687
|
+
total_models: SWITCHROOM_MODELS.length
|
|
77688
|
+
}
|
|
77689
|
+
]
|
|
77690
|
+
};
|
|
77691
|
+
}
|
|
77692
|
+
function switchroomCommandsCatalog() {
|
|
77693
|
+
return {
|
|
77694
|
+
categories: [
|
|
77695
|
+
{
|
|
77696
|
+
name: "Session",
|
|
77697
|
+
pairs: [
|
|
77698
|
+
["/compact", "Compact context (summarize, keep the thread)"],
|
|
77699
|
+
["/clear", "Clear context (fresh slate; memory in Hindsight)"],
|
|
77700
|
+
["/model", "Show or switch the Claude model"],
|
|
77701
|
+
["/status", "Agent, model, auth status"]
|
|
77702
|
+
]
|
|
77703
|
+
},
|
|
77704
|
+
{
|
|
77705
|
+
name: "Memory & knowledge",
|
|
77706
|
+
pairs: [
|
|
77707
|
+
["/memory", "List, search, or clear Hindsight memory"]
|
|
77708
|
+
]
|
|
77709
|
+
},
|
|
77710
|
+
{
|
|
77711
|
+
name: "Vault & auth",
|
|
77712
|
+
pairs: [
|
|
77713
|
+
["/vault", "Manage vault secrets + capability grants"],
|
|
77714
|
+
["/auth", "Auth dashboard \u2014 accounts, quota, reauth, switch primary"],
|
|
77715
|
+
["/whoami", "This agent's sandbox: tools, MCP, vault key-names"]
|
|
77716
|
+
]
|
|
77717
|
+
},
|
|
77718
|
+
{
|
|
77719
|
+
name: "Diagnostics",
|
|
77720
|
+
pairs: [
|
|
77721
|
+
["/doctor", "Health check (deps, services, MCP)"],
|
|
77722
|
+
["/logs", "Show recent agent logs"],
|
|
77723
|
+
["/usage", "Pro/Max plan quota (5h + 7d windows)"],
|
|
77724
|
+
["/version", "Show version + running agent health"],
|
|
77725
|
+
["/commands", "Full command list"]
|
|
77726
|
+
]
|
|
77727
|
+
}
|
|
77728
|
+
]
|
|
77729
|
+
};
|
|
77730
|
+
}
|
|
77731
|
+
async function handleHermesRest(method, pathname, config, search = "") {
|
|
77611
77732
|
if (method === "GET" && (pathname === "/api/sessions" || pathname === "/api/profiles/sessions")) {
|
|
77612
77733
|
const agents = await handleGetAgents(config);
|
|
77613
77734
|
const sessions = agents.map((a) => toHermesSession(a, agentLiveness(config, a.name)));
|
|
@@ -77633,15 +77754,16 @@ async function handleHermesRest(method, pathname, config) {
|
|
|
77633
77754
|
return { status: 404, body: { error: "Unknown session" } };
|
|
77634
77755
|
return { status: 200, body: { session: toHermesSession(agent, agentLiveness(config, id)) } };
|
|
77635
77756
|
}
|
|
77636
|
-
const
|
|
77637
|
-
if (method === "GET" &&
|
|
77638
|
-
const id = decodeURIComponent(
|
|
77757
|
+
const messagesMatch = pathname.match(/^\/api\/sessions\/([^/]+)\/messages$/);
|
|
77758
|
+
if (method === "GET" && messagesMatch) {
|
|
77759
|
+
const id = decodeURIComponent(messagesMatch[1]);
|
|
77639
77760
|
if (!config.agents?.[id])
|
|
77640
77761
|
return { status: 404, body: { error: "Unknown session" } };
|
|
77641
|
-
const result = handleGetTurns(config, id,
|
|
77762
|
+
const result = handleGetTurns(config, id, 100);
|
|
77642
77763
|
if (!result.ok)
|
|
77643
77764
|
return { status: 500, body: { error: result.error } };
|
|
77644
|
-
|
|
77765
|
+
const messages = turnsToMessages(result.turns ?? []);
|
|
77766
|
+
return { status: 200, body: { session_id: id, messages } };
|
|
77645
77767
|
}
|
|
77646
77768
|
if (method === "GET" && pathname === "/api/status") {
|
|
77647
77769
|
return {
|
|
@@ -77683,7 +77805,7 @@ async function handleHermesRest(method, pathname, config) {
|
|
|
77683
77805
|
return {
|
|
77684
77806
|
status: 200,
|
|
77685
77807
|
body: {
|
|
77686
|
-
model: "claude",
|
|
77808
|
+
model: "claude-sonnet-4-6",
|
|
77687
77809
|
provider: "switchroom",
|
|
77688
77810
|
capabilities: {}
|
|
77689
77811
|
}
|
|
@@ -77692,13 +77814,32 @@ async function handleHermesRest(method, pathname, config) {
|
|
|
77692
77814
|
if (method === "GET" && pathname.startsWith("/api/logs")) {
|
|
77693
77815
|
return { status: 200, body: { file: "gateway.log", lines: [] } };
|
|
77694
77816
|
}
|
|
77695
|
-
if (
|
|
77696
|
-
if (pathname === "/api/cron/jobs")
|
|
77697
|
-
|
|
77698
|
-
|
|
77699
|
-
return { status: 200, body:
|
|
77700
|
-
|
|
77701
|
-
|
|
77817
|
+
if (pathname.startsWith("/api/cron")) {
|
|
77818
|
+
if (method === "GET" && pathname === "/api/cron/jobs") {
|
|
77819
|
+
const schedule = await handleGetSchedule(config);
|
|
77820
|
+
const jobs = scheduleToCronJobs(schedule);
|
|
77821
|
+
return { status: 200, body: jobs };
|
|
77822
|
+
}
|
|
77823
|
+
const jobRunsMatch = pathname.match(/^\/api\/cron\/jobs\/([^/]+)\/runs$/);
|
|
77824
|
+
if (method === "GET" && jobRunsMatch) {
|
|
77825
|
+
const jobId = decodeURIComponent(jobRunsMatch[1]);
|
|
77826
|
+
const schedule = await handleGetSchedule(config);
|
|
77827
|
+
const runs = cronJobRuns(schedule, jobId);
|
|
77828
|
+
const limitParam = new URLSearchParams(search).get("limit");
|
|
77829
|
+
const limit = limitParam ? parseInt(limitParam, 10) : 20;
|
|
77830
|
+
return { status: 200, body: { runs: runs.slice(0, limit) } };
|
|
77831
|
+
}
|
|
77832
|
+
const jobMatch = pathname.match(/^\/api\/cron\/jobs\/([^/]+)$/);
|
|
77833
|
+
if (method === "GET" && jobMatch) {
|
|
77834
|
+
const jobId = decodeURIComponent(jobMatch[1]);
|
|
77835
|
+
const schedule = await handleGetSchedule(config);
|
|
77836
|
+
const job = scheduleToCronJobs(schedule).find((j) => j.id === jobId);
|
|
77837
|
+
if (!job)
|
|
77838
|
+
return { status: 404, body: { error: "Not found" } };
|
|
77839
|
+
return { status: 200, body: job };
|
|
77840
|
+
}
|
|
77841
|
+
if (method === "POST" || method === "PATCH" || method === "DELETE") {
|
|
77842
|
+
return { status: 422, body: { error: "Schedules are managed via switchroom YAML config \u2014 edit the agent config file and apply." } };
|
|
77702
77843
|
}
|
|
77703
77844
|
return { status: 200, body: {} };
|
|
77704
77845
|
}
|
|
@@ -77711,6 +77852,12 @@ async function handleHermesRest(method, pathname, config) {
|
|
|
77711
77852
|
if (pathname.includes("sessions")) {
|
|
77712
77853
|
return { status: 200, body: { sessions: [], total: 0, limit: 0, offset: 0 } };
|
|
77713
77854
|
}
|
|
77855
|
+
if (pathname === "/api/profiles") {
|
|
77856
|
+
return { status: 200, body: { profiles: [] } };
|
|
77857
|
+
}
|
|
77858
|
+
if (pathname.endsWith("/soul")) {
|
|
77859
|
+
return { status: 200, body: { content: "", exists: false } };
|
|
77860
|
+
}
|
|
77714
77861
|
return { status: 200, body: {} };
|
|
77715
77862
|
}
|
|
77716
77863
|
if (method === "GET" && pathname === "/api/memory/providers") {
|
|
@@ -77719,6 +77866,21 @@ async function handleHermesRest(method, pathname, config) {
|
|
|
77719
77866
|
if (method === "GET" && pathname === "/api/fs/default-cwd") {
|
|
77720
77867
|
return { status: 200, body: { cwd: null, branch: null } };
|
|
77721
77868
|
}
|
|
77869
|
+
if (method === "GET" && pathname === "/api/env") {
|
|
77870
|
+
return { status: 200, body: {} };
|
|
77871
|
+
}
|
|
77872
|
+
if (method === "POST" && pathname === "/api/providers/validate") {
|
|
77873
|
+
return { status: 200, body: { ok: true, model: null } };
|
|
77874
|
+
}
|
|
77875
|
+
if (method === "GET" && pathname === "/api/auth/providers") {
|
|
77876
|
+
return { status: 200, body: { providers: [] } };
|
|
77877
|
+
}
|
|
77878
|
+
if (method === "GET" && pathname.startsWith("/api/model/options")) {
|
|
77879
|
+
return { status: 200, body: switchroomModelOptions() };
|
|
77880
|
+
}
|
|
77881
|
+
if (method === "POST" && pathname === "/api/model/set") {
|
|
77882
|
+
return { status: 200, body: { ok: true } };
|
|
77883
|
+
}
|
|
77722
77884
|
return null;
|
|
77723
77885
|
}
|
|
77724
77886
|
function sendEvent(ctx, type, sessionId, payload) {
|
|
@@ -77834,7 +77996,7 @@ async function onHermesMessage(ctx, raw) {
|
|
|
77834
77996
|
sendResponse(ctx, rpcErr(id, -32603, result.error ?? "Failed to read history"));
|
|
77835
77997
|
break;
|
|
77836
77998
|
}
|
|
77837
|
-
sendResponse(ctx, rpcOk(id, {
|
|
77999
|
+
sendResponse(ctx, rpcOk(id, { session_id: sessionId, messages: turnsToMessages(result.turns ?? []) }));
|
|
77838
78000
|
break;
|
|
77839
78001
|
}
|
|
77840
78002
|
case "session.usage": {
|
|
@@ -77917,6 +78079,82 @@ async function onHermesMessage(ctx, raw) {
|
|
|
77917
78079
|
sendResponse(ctx, rpcOk(id, { ok: true }));
|
|
77918
78080
|
break;
|
|
77919
78081
|
}
|
|
78082
|
+
case "model.options": {
|
|
78083
|
+
sendResponse(ctx, rpcOk(id, switchroomModelOptions()));
|
|
78084
|
+
break;
|
|
78085
|
+
}
|
|
78086
|
+
case "model.info": {
|
|
78087
|
+
sendResponse(ctx, rpcOk(id, { model: "claude-sonnet-4-6", provider: "switchroom" }));
|
|
78088
|
+
break;
|
|
78089
|
+
}
|
|
78090
|
+
case "config.set": {
|
|
78091
|
+
const sessionId = String(params.session_id ?? ctx.activeSessionId ?? "");
|
|
78092
|
+
const key = String(params.key ?? "");
|
|
78093
|
+
const value = String(params.value ?? "");
|
|
78094
|
+
if (key === "model" && sessionId && config.agents?.[sessionId]) {
|
|
78095
|
+
const modelName = value.replace(/\s+--provider\s+\S+$/, "").trim();
|
|
78096
|
+
if (modelName) {
|
|
78097
|
+
const agentsDir = resolveAgentsDir(config);
|
|
78098
|
+
const chat = resolveAgentChat(config, sessionId, agentsDir);
|
|
78099
|
+
if (chat) {
|
|
78100
|
+
const promptKey = `model-switch-${Date.now()}`;
|
|
78101
|
+
injectInbound(agentsDir, sessionId, chat.chatId, chat.threadId, `/model ${modelName}`, promptKey);
|
|
78102
|
+
}
|
|
78103
|
+
}
|
|
78104
|
+
}
|
|
78105
|
+
sendResponse(ctx, rpcOk(id, { ok: true }));
|
|
78106
|
+
break;
|
|
78107
|
+
}
|
|
78108
|
+
case "commands.catalog": {
|
|
78109
|
+
sendResponse(ctx, rpcOk(id, switchroomCommandsCatalog()));
|
|
78110
|
+
break;
|
|
78111
|
+
}
|
|
78112
|
+
case "slash.exec": {
|
|
78113
|
+
const sessionId = String(params.session_id ?? ctx.activeSessionId ?? "");
|
|
78114
|
+
const bare = String(params.command ?? "").replace(/^\/+/, "");
|
|
78115
|
+
const arg = params.arg != null ? String(params.arg) : "";
|
|
78116
|
+
if (!sessionId || !config.agents?.[sessionId]) {
|
|
78117
|
+
sendResponse(ctx, rpcErr(id, -32602, `Unknown session: ${sessionId}`));
|
|
78118
|
+
break;
|
|
78119
|
+
}
|
|
78120
|
+
if (!bare) {
|
|
78121
|
+
sendResponse(ctx, rpcErr(id, -32602, "command is required"));
|
|
78122
|
+
break;
|
|
78123
|
+
}
|
|
78124
|
+
const fullCommand = arg ? `/${bare} ${arg}` : `/${bare}`;
|
|
78125
|
+
const verb = `/${bare}`;
|
|
78126
|
+
if (INJECT_BLOCKLIST.has(verb)) {
|
|
78127
|
+
sendResponse(ctx, rpcErr(id, -32602, `/${bare} cannot be executed remotely`));
|
|
78128
|
+
break;
|
|
78129
|
+
}
|
|
78130
|
+
if (INJECT_ALLOWLIST.has(verb)) {
|
|
78131
|
+
let injectResult;
|
|
78132
|
+
try {
|
|
78133
|
+
injectResult = await injectSlashCommand(sessionId, fullCommand);
|
|
78134
|
+
} catch (err) {
|
|
78135
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
78136
|
+
sendResponse(ctx, rpcErr(id, -32603, msg));
|
|
78137
|
+
break;
|
|
78138
|
+
}
|
|
78139
|
+
const output = injectResult.outcome === "ok" ? injectResult.output ?? "" : `*(${fullCommand} sent)*`;
|
|
78140
|
+
sendResponse(ctx, rpcOk(id, { ok: true, output }));
|
|
78141
|
+
break;
|
|
78142
|
+
}
|
|
78143
|
+
const agentsDir = resolveAgentsDir(config);
|
|
78144
|
+
const chat = resolveAgentChat(config, sessionId, agentsDir);
|
|
78145
|
+
if (!chat) {
|
|
78146
|
+
sendResponse(ctx, rpcErr(id, -32603, `Cannot resolve chat for ${sessionId}`));
|
|
78147
|
+
break;
|
|
78148
|
+
}
|
|
78149
|
+
const promptKey = `slash-${bare}-${Date.now()}`;
|
|
78150
|
+
const result = await injectInbound(agentsDir, sessionId, chat.chatId, chat.threadId, fullCommand, promptKey);
|
|
78151
|
+
if (!result.ok) {
|
|
78152
|
+
sendResponse(ctx, rpcErr(id, -32603, result.error ?? "inject failed"));
|
|
78153
|
+
break;
|
|
78154
|
+
}
|
|
78155
|
+
sendResponse(ctx, rpcOk(id, { ok: true, output: `*(${fullCommand} sent to ${sessionId})*` }));
|
|
78156
|
+
break;
|
|
78157
|
+
}
|
|
77920
78158
|
case "approval.request":
|
|
77921
78159
|
case "approval.respond":
|
|
77922
78160
|
case "sudo.request":
|
|
@@ -78542,7 +78780,7 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
|
|
|
78542
78780
|
if (authError)
|
|
78543
78781
|
return authError;
|
|
78544
78782
|
return (async () => {
|
|
78545
|
-
const hermesResult = await handleHermesRest(req.method, pathname, config);
|
|
78783
|
+
const hermesResult = await handleHermesRest(req.method, pathname, config, url.search);
|
|
78546
78784
|
if (hermesResult !== null) {
|
|
78547
78785
|
return jsonResponse(hermesResult.body, hermesResult.status);
|
|
78548
78786
|
}
|
|
@@ -22587,7 +22587,7 @@ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:f
|
|
|
22587
22587
|
import { dirname as dirname4, join as join2 } from "node:path";
|
|
22588
22588
|
|
|
22589
22589
|
// src/build-info.ts
|
|
22590
|
-
var VERSION = "0.16.
|
|
22590
|
+
var VERSION = "0.16.22";
|
|
22591
22591
|
|
|
22592
22592
|
// src/cli/resolve-version.ts
|
|
22593
22593
|
function readPackageVersion() {
|
package/package.json
CHANGED
|
@@ -56246,10 +56246,10 @@ function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
|
56246
56246
|
}
|
|
56247
56247
|
|
|
56248
56248
|
// ../src/build-info.ts
|
|
56249
|
-
var VERSION = "0.16.
|
|
56250
|
-
var COMMIT_SHA = "
|
|
56251
|
-
var COMMIT_DATE = "2026-06-
|
|
56252
|
-
var LATEST_PR =
|
|
56249
|
+
var VERSION = "0.16.22";
|
|
56250
|
+
var COMMIT_SHA = "ea287bf4";
|
|
56251
|
+
var COMMIT_DATE = "2026-06-29T08:38:13Z";
|
|
56252
|
+
var LATEST_PR = 2663;
|
|
56253
56253
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
56254
56254
|
|
|
56255
56255
|
// gateway/boot-version.ts
|