switchroom 0.15.41 → 0.15.42
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 +2 -1
- package/dist/auth-broker/index.js +2 -1
- package/dist/cli/notion-write-pretool.mjs +2 -1
- package/dist/cli/switchroom.js +144 -12
- package/dist/cli/ui/index.html +31 -0
- package/dist/host-control/main.js +2 -1
- package/dist/vault/approvals/kernel-server.js +2 -1
- package/dist/vault/broker/server.js +2 -1
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +398 -227
- package/telegram-plugin/gateway/context-occupancy.ts +91 -0
- package/telegram-plugin/gateway/gateway.ts +204 -63
- package/telegram-plugin/gateway/hostd-dispatch.ts +1 -1
- package/telegram-plugin/gateway/idle-clear.ts +72 -0
- package/telegram-plugin/gateway/poll-health.ts +9 -4
- package/telegram-plugin/gateway/poll-stall-recovery.ts +59 -0
- package/telegram-plugin/tests/context-occupancy.test.ts +55 -0
- package/telegram-plugin/tests/idle-clear.test.ts +62 -0
- package/telegram-plugin/tests/poll-stall-recovery.test.ts +32 -0
- package/telegram-plugin/tests/welcome-text.test.ts +10 -11
- package/telegram-plugin/welcome-text.ts +11 -12
|
@@ -11114,7 +11114,8 @@ var SubagentSchema = exports_external.object({
|
|
|
11114
11114
|
var SessionSchema = exports_external.object({
|
|
11115
11115
|
max_idle: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '2h', '30m')").optional().describe("Start a fresh session if the previous one has been idle " + "longer than this duration. Examples: '2h', '30m', '7200s'."),
|
|
11116
11116
|
max_turns: exports_external.number().int().positive().optional().describe("Start a fresh session if the previous one has more user " + "turns than this. Useful for preventing context bloat on " + "long-running agents."),
|
|
11117
|
-
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context.")
|
|
11117
|
+
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context."),
|
|
11118
|
+
idle_clear_after: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '3h', '90m')").optional().describe("Auto-run /clear (wipe the working context) after the live " + "session has been idle this long. Defaults to '3h' when unset " + "(on by default); set '0s' to disable. Long-term memory lives " + "in Hindsight, so a clear loses only the in-session thread.")
|
|
11118
11119
|
}).optional();
|
|
11119
11120
|
var SessionContinuitySchema = exports_external.object({
|
|
11120
11121
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
|
@@ -11114,7 +11114,8 @@ var SubagentSchema = exports_external.object({
|
|
|
11114
11114
|
var SessionSchema = exports_external.object({
|
|
11115
11115
|
max_idle: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '2h', '30m')").optional().describe("Start a fresh session if the previous one has been idle " + "longer than this duration. Examples: '2h', '30m', '7200s'."),
|
|
11116
11116
|
max_turns: exports_external.number().int().positive().optional().describe("Start a fresh session if the previous one has more user " + "turns than this. Useful for preventing context bloat on " + "long-running agents."),
|
|
11117
|
-
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context.")
|
|
11117
|
+
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context."),
|
|
11118
|
+
idle_clear_after: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '3h', '90m')").optional().describe("Auto-run /clear (wipe the working context) after the live " + "session has been idle this long. Defaults to '3h' when unset " + "(on by default); set '0s' to disable. Long-term memory lives " + "in Hindsight, so a clear loses only the in-session thread.")
|
|
11118
11119
|
}).optional();
|
|
11119
11120
|
var SessionContinuitySchema = exports_external.object({
|
|
11120
11121
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
|
@@ -11862,7 +11862,8 @@ var SubagentSchema = exports_external.object({
|
|
|
11862
11862
|
var SessionSchema = exports_external.object({
|
|
11863
11863
|
max_idle: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '2h', '30m')").optional().describe("Start a fresh session if the previous one has been idle " + "longer than this duration. Examples: '2h', '30m', '7200s'."),
|
|
11864
11864
|
max_turns: exports_external.number().int().positive().optional().describe("Start a fresh session if the previous one has more user " + "turns than this. Useful for preventing context bloat on " + "long-running agents."),
|
|
11865
|
-
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context.")
|
|
11865
|
+
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context."),
|
|
11866
|
+
idle_clear_after: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '3h', '90m')").optional().describe("Auto-run /clear (wipe the working context) after the live " + "session has been idle this long. Defaults to '3h' when unset " + "(on by default); set '0s' to disable. Long-term memory lives " + "in Hindsight, so a clear loses only the in-session thread.")
|
|
11866
11867
|
}).optional();
|
|
11867
11868
|
var SessionContinuitySchema = exports_external.object({
|
|
11868
11869
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
package/dist/cli/switchroom.js
CHANGED
|
@@ -13678,7 +13678,8 @@ var init_schema = __esm(() => {
|
|
|
13678
13678
|
SessionSchema = exports_external.object({
|
|
13679
13679
|
max_idle: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '2h', '30m')").optional().describe("Start a fresh session if the previous one has been idle " + "longer than this duration. Examples: '2h', '30m', '7200s'."),
|
|
13680
13680
|
max_turns: exports_external.number().int().positive().optional().describe("Start a fresh session if the previous one has more user " + "turns than this. Useful for preventing context bloat on " + "long-running agents."),
|
|
13681
|
-
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context.")
|
|
13681
|
+
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context."),
|
|
13682
|
+
idle_clear_after: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '3h', '90m')").optional().describe("Auto-run /clear (wipe the working context) after the live " + "session has been idle this long. Defaults to '3h' when unset " + "(on by default); set '0s' to disable. Long-term memory lives " + "in Hindsight, so a clear loses only the in-session thread.")
|
|
13682
13683
|
}).optional();
|
|
13683
13684
|
SessionContinuitySchema = exports_external.object({
|
|
13684
13685
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
|
@@ -31678,6 +31679,46 @@ async function runMcpSecretChecks(config, deps = {}) {
|
|
|
31678
31679
|
return results;
|
|
31679
31680
|
}
|
|
31680
31681
|
|
|
31682
|
+
// src/cli/doctor-context.ts
|
|
31683
|
+
function fmtK(n) {
|
|
31684
|
+
return n >= 1000 ? `${(n / 1000).toFixed(n >= 1e4 ? 0 : 1)}k` : `${n}`;
|
|
31685
|
+
}
|
|
31686
|
+
function contextRow(agent, read) {
|
|
31687
|
+
const name = `context:${agent}`;
|
|
31688
|
+
if (read.kind === "absent") {
|
|
31689
|
+
return { name, status: "skip", detail: "no snapshot yet (agent idle since boot, or stopped)" };
|
|
31690
|
+
}
|
|
31691
|
+
if (read.kind === "unreadable") {
|
|
31692
|
+
return { name, status: "skip", detail: read.msg };
|
|
31693
|
+
}
|
|
31694
|
+
const s = read.snapshot;
|
|
31695
|
+
if (s.state === "unknown") {
|
|
31696
|
+
return { name, status: "skip", detail: "occupancy not yet measured" };
|
|
31697
|
+
}
|
|
31698
|
+
if (s.cap == null || s.pct == null) {
|
|
31699
|
+
return { name, status: "ok", detail: `${fmtK(s.occupancy)} tok (no cap \u2014 native compaction)` };
|
|
31700
|
+
}
|
|
31701
|
+
const pctLabel = `${Math.round(s.pct * 100)}%`;
|
|
31702
|
+
const detail = `${fmtK(s.occupancy)}/${fmtK(s.cap)} (${pctLabel})`;
|
|
31703
|
+
if (s.state === "tight") {
|
|
31704
|
+
return {
|
|
31705
|
+
name,
|
|
31706
|
+
status: "warn",
|
|
31707
|
+
detail: `${detail} \u2014 tight; a /compact is near`,
|
|
31708
|
+
fix: `Working context is ${pctLabel} of the cap. Normal if mid-large-task (it compacts automatically); if persistent, consider raising session.max_context_tokens or trimming this agent's tool/prose surface.`
|
|
31709
|
+
};
|
|
31710
|
+
}
|
|
31711
|
+
return { name, status: "ok", detail };
|
|
31712
|
+
}
|
|
31713
|
+
async function runContextChecks(config, deps = {}) {
|
|
31714
|
+
const agents = Object.keys(config.agents ?? {});
|
|
31715
|
+
if (agents.length === 0)
|
|
31716
|
+
return [];
|
|
31717
|
+
const read = deps.readSnapshot ?? (async () => ({ kind: "absent" }));
|
|
31718
|
+
const results = await Promise.all(agents.sort().map(async (a) => contextRow(a, await read(a))));
|
|
31719
|
+
return results;
|
|
31720
|
+
}
|
|
31721
|
+
|
|
31681
31722
|
// src/cli/doctor-credentials-migration.ts
|
|
31682
31723
|
import {
|
|
31683
31724
|
existsSync as realExistsSync5,
|
|
@@ -34082,6 +34123,24 @@ function registerDoctorCommand(program3) {
|
|
|
34082
34123
|
title: "MCP Connections (auth)",
|
|
34083
34124
|
results: await runMcpSecretChecks(config, { vaultAclReader })
|
|
34084
34125
|
},
|
|
34126
|
+
{
|
|
34127
|
+
title: "Context Headroom",
|
|
34128
|
+
results: await runContextChecks(config, {
|
|
34129
|
+
readSnapshot: async (agent) => {
|
|
34130
|
+
try {
|
|
34131
|
+
const { execFileSync: execFileSync19 } = await import("node:child_process");
|
|
34132
|
+
const out = execFileSync19("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/context-occupancy.json"], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 5000 });
|
|
34133
|
+
return { kind: "ok", snapshot: JSON.parse(out) };
|
|
34134
|
+
} catch (err) {
|
|
34135
|
+
const msg = err.message ?? String(err);
|
|
34136
|
+
if (/No such|cannot|not running|exited|No such container/i.test(msg)) {
|
|
34137
|
+
return { kind: "absent" };
|
|
34138
|
+
}
|
|
34139
|
+
return { kind: "unreadable", msg: `snapshot read failed: ${msg}` };
|
|
34140
|
+
}
|
|
34141
|
+
}
|
|
34142
|
+
})
|
|
34143
|
+
},
|
|
34085
34144
|
{ title: "MFF Skill", results: await checkMff(passphrase, vaultPath, config) },
|
|
34086
34145
|
{ title: "Webkite", results: runWebkiteChecks(config) },
|
|
34087
34146
|
{ title: "Cron Session", results: runCronSessionChecks(config) }
|
|
@@ -50708,8 +50767,8 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
50708
50767
|
import { dirname, join } from "node:path";
|
|
50709
50768
|
|
|
50710
50769
|
// src/build-info.ts
|
|
50711
|
-
var VERSION = "0.15.
|
|
50712
|
-
var COMMIT_SHA = "
|
|
50770
|
+
var VERSION = "0.15.42";
|
|
50771
|
+
var COMMIT_SHA = "2c7e12da";
|
|
50713
50772
|
|
|
50714
50773
|
// src/cli/resolve-version.ts
|
|
50715
50774
|
function readPackageVersion() {
|
|
@@ -68965,6 +69024,29 @@ function stopHindsight() {
|
|
|
68965
69024
|
execFileSync16("docker", ["rm", "switchroom-hindsight"], { stdio: "pipe" });
|
|
68966
69025
|
} catch {}
|
|
68967
69026
|
}
|
|
69027
|
+
function pullHindsightImage() {
|
|
69028
|
+
execFileSync16("docker", ["pull", HINDSIGHT_IMAGE], { stdio: "inherit" });
|
|
69029
|
+
}
|
|
69030
|
+
function getRunningHindsightPorts() {
|
|
69031
|
+
const readPort = (containerPort) => {
|
|
69032
|
+
try {
|
|
69033
|
+
const out = execFileSync16("docker", ["port", "switchroom-hindsight", `${containerPort}/tcp`], { stdio: "pipe", encoding: "utf-8" });
|
|
69034
|
+
const m = out.split(`
|
|
69035
|
+
`).map((l) => l.trim()).find((l) => l.length > 0);
|
|
69036
|
+
if (!m)
|
|
69037
|
+
return null;
|
|
69038
|
+
const port = Number(m.slice(m.lastIndexOf(":") + 1));
|
|
69039
|
+
return Number.isInteger(port) && port > 0 ? port : null;
|
|
69040
|
+
} catch {
|
|
69041
|
+
return null;
|
|
69042
|
+
}
|
|
69043
|
+
};
|
|
69044
|
+
const apiPort = readPort(HINDSIGHT_DEFAULT_API_PORT);
|
|
69045
|
+
if (apiPort === null)
|
|
69046
|
+
return null;
|
|
69047
|
+
const uiPort = readPort(HINDSIGHT_DEFAULT_UI_PORT) ?? (apiPort === HINDSIGHT_DEFAULT_API_PORT ? HINDSIGHT_DEFAULT_UI_PORT : 19999);
|
|
69048
|
+
return { apiPort, uiPort };
|
|
69049
|
+
}
|
|
68968
69050
|
function getHindsightStatus() {
|
|
68969
69051
|
try {
|
|
68970
69052
|
const output = execFileSync16("docker", ["ps", "-a", "--filter", "name=switchroom-hindsight", "--format", "{{.Status}}"], { stdio: "pipe", encoding: "utf-8" });
|
|
@@ -69195,7 +69277,7 @@ Cross-agent reflection plan
|
|
|
69195
69277
|
}
|
|
69196
69278
|
console.log();
|
|
69197
69279
|
}));
|
|
69198
|
-
memory.command("setup").description("Manage the Hindsight Docker container").option("--stop", "Stop and remove the Hindsight container").option("--status", "Show Hindsight container status").option("--provider <provider>", "LLM provider (ollama, openai, anthropic)").action(async (opts) => {
|
|
69280
|
+
memory.command("setup").description("Manage the Hindsight Docker container").option("--stop", "Stop and remove the Hindsight container").option("--status", "Show Hindsight container status").option("--recreate", "Pull the latest image and recreate the container (reusing its current port). Used by `switchroom update` to keep the hindsight singleton current.").option("--provider <provider>", "LLM provider (ollama, openai, anthropic)").action(async (opts) => {
|
|
69199
69281
|
if (opts.status) {
|
|
69200
69282
|
if (!isDockerAvailable()) {
|
|
69201
69283
|
console.log(source_default.red(" Docker is not available."));
|
|
@@ -69236,24 +69318,41 @@ Cross-agent reflection plan
|
|
|
69236
69318
|
`));
|
|
69237
69319
|
process.exit(1);
|
|
69238
69320
|
}
|
|
69239
|
-
|
|
69321
|
+
const recreate = opts.recreate === true;
|
|
69322
|
+
const reusePorts = recreate ? getRunningHindsightPorts() : null;
|
|
69323
|
+
if (isHindsightRunning() && !recreate) {
|
|
69240
69324
|
console.log(source_default.green(`
|
|
69241
69325
|
Hindsight container is already running (switchroom-hindsight).
|
|
69242
69326
|
`));
|
|
69243
69327
|
return;
|
|
69244
69328
|
}
|
|
69329
|
+
if (recreate) {
|
|
69330
|
+
console.log(source_default.gray(" Pulling latest Hindsight image..."));
|
|
69331
|
+
try {
|
|
69332
|
+
pullHindsightImage();
|
|
69333
|
+
} catch (err) {
|
|
69334
|
+
console.error(source_default.red(`
|
|
69335
|
+
Failed to pull Hindsight image: ${err.message}
|
|
69336
|
+
`));
|
|
69337
|
+
process.exit(1);
|
|
69338
|
+
}
|
|
69339
|
+
}
|
|
69245
69340
|
if (isHindsightContainerExists()) {
|
|
69246
|
-
console.log(source_default.gray(" Removing
|
|
69341
|
+
console.log(source_default.gray(" Removing existing switchroom-hindsight container..."));
|
|
69247
69342
|
stopHindsight();
|
|
69248
69343
|
}
|
|
69249
69344
|
let ports;
|
|
69250
|
-
|
|
69251
|
-
ports =
|
|
69252
|
-
}
|
|
69253
|
-
|
|
69345
|
+
if (reusePorts) {
|
|
69346
|
+
ports = reusePorts;
|
|
69347
|
+
} else {
|
|
69348
|
+
try {
|
|
69349
|
+
ports = await pickHindsightPorts();
|
|
69350
|
+
} catch (err) {
|
|
69351
|
+
console.error(source_default.red(`
|
|
69254
69352
|
${err.message}
|
|
69255
69353
|
`));
|
|
69256
|
-
|
|
69354
|
+
process.exit(1);
|
|
69355
|
+
}
|
|
69257
69356
|
}
|
|
69258
69357
|
if (ports.apiPort !== HINDSIGHT_DEFAULT_API_PORT) {
|
|
69259
69358
|
console.log(source_default.yellow(` Port ${HINDSIGHT_DEFAULT_API_PORT} is already in use; ` + `using ${ports.apiPort}/${ports.uiPort} instead.`));
|
|
@@ -74629,6 +74728,17 @@ var DASHBOARD_CACHE_TTL = {
|
|
|
74629
74728
|
approvals: 15000,
|
|
74630
74729
|
grants: 15000
|
|
74631
74730
|
};
|
|
74731
|
+
function readContextOccupancy(agentsDir, name) {
|
|
74732
|
+
try {
|
|
74733
|
+
const raw = readFileSync44(resolve28(agentsDir, name, "context-occupancy.json"), "utf-8");
|
|
74734
|
+
const s = JSON.parse(raw);
|
|
74735
|
+
if (typeof s?.occupancy !== "number")
|
|
74736
|
+
return null;
|
|
74737
|
+
return s;
|
|
74738
|
+
} catch {
|
|
74739
|
+
return null;
|
|
74740
|
+
}
|
|
74741
|
+
}
|
|
74632
74742
|
function readLastTurnAt(agentsDir, name) {
|
|
74633
74743
|
try {
|
|
74634
74744
|
const db = openTurnsDb(resolve28(agentsDir, name));
|
|
@@ -74663,6 +74773,7 @@ async function cachedFleetStatus(now, fetchImpl = fetchFleetStatusViaHostd) {
|
|
|
74663
74773
|
}
|
|
74664
74774
|
async function handleGetAgents(config, deps = {}) {
|
|
74665
74775
|
const readLastTurn = deps.readLastTurn ?? readLastTurnAt;
|
|
74776
|
+
const readContext = deps.readContext ?? readContextOccupancy;
|
|
74666
74777
|
const statuses = getAllAgentStatuses(config);
|
|
74667
74778
|
const authStatuses = getAllAuthStatuses(config);
|
|
74668
74779
|
const agentsDir = resolveAgentsDir(config);
|
|
@@ -74695,7 +74806,8 @@ async function handleGetAgents(config, deps = {}) {
|
|
|
74695
74806
|
expiresAt: auth?.expiresAt
|
|
74696
74807
|
},
|
|
74697
74808
|
memoryCollection: collection,
|
|
74698
|
-
lastTurnAt: readLastTurn(agentsDir, name)
|
|
74809
|
+
lastTurnAt: readLastTurn(agentsDir, name),
|
|
74810
|
+
context: readContext(agentsDir, name)
|
|
74699
74811
|
});
|
|
74700
74812
|
}
|
|
74701
74813
|
return agents;
|
|
@@ -78047,6 +78159,26 @@ function planUpdate(opts) {
|
|
|
78047
78159
|
throw new Error("switchroom webd install failed");
|
|
78048
78160
|
}
|
|
78049
78161
|
});
|
|
78162
|
+
let memoryBackendHindsight;
|
|
78163
|
+
if (typeof opts.memoryBackendHindsight === "boolean") {
|
|
78164
|
+
memoryBackendHindsight = opts.memoryBackendHindsight;
|
|
78165
|
+
} else {
|
|
78166
|
+
try {
|
|
78167
|
+
memoryBackendHindsight = loadConfig().memory?.backend === "hindsight";
|
|
78168
|
+
} catch {
|
|
78169
|
+
memoryBackendHindsight = false;
|
|
78170
|
+
}
|
|
78171
|
+
}
|
|
78172
|
+
steps.push({
|
|
78173
|
+
name: "refresh-hindsight",
|
|
78174
|
+
description: "switchroom memory setup --recreate \u2014 pull latest hindsight image + recreate the memory singleton (standalone container, reusing its current port)",
|
|
78175
|
+
skipReason: !memoryBackendHindsight ? "memory.backend is not hindsight \u2014 no hindsight singleton to refresh" : opts.skipImages ? "--skip-images flag set" : undefined,
|
|
78176
|
+
run: () => {
|
|
78177
|
+
const r = runner(process.execPath, [scriptPath, "memory", "setup", "--recreate"]);
|
|
78178
|
+
if (r.status !== 0)
|
|
78179
|
+
throw new Error("switchroom memory setup --recreate failed");
|
|
78180
|
+
}
|
|
78181
|
+
});
|
|
78050
78182
|
steps.push({
|
|
78051
78183
|
name: "sync-bundled-skills",
|
|
78052
78184
|
description: "Sync shipped skills/ to ~/.switchroom/skills/_bundled/ (host-stable pool dir).",
|
package/dist/cli/ui/index.html
CHANGED
|
@@ -122,6 +122,15 @@
|
|
|
122
122
|
|
|
123
123
|
.meta-item span { color: var(--text); }
|
|
124
124
|
|
|
125
|
+
/* Context-headroom gauge (RFC context-headroom-surface). */
|
|
126
|
+
.ctx-gauge { display: inline-flex; align-items: center; gap: 0.4rem; }
|
|
127
|
+
.ctx-text { color: var(--text); font-variant-numeric: tabular-nums; }
|
|
128
|
+
.ctx-bar {
|
|
129
|
+
display: inline-block; width: 48px; height: 6px; border-radius: 3px;
|
|
130
|
+
background: var(--bg-dim, rgba(255,255,255,0.12)); overflow: hidden;
|
|
131
|
+
}
|
|
132
|
+
.ctx-fill { display: block; height: 100%; border-radius: 3px; }
|
|
133
|
+
|
|
125
134
|
.scope-toggle {
|
|
126
135
|
cursor: pointer;
|
|
127
136
|
color: var(--text);
|
|
@@ -1659,6 +1668,27 @@
|
|
|
1659
1668
|
return 'inactive';
|
|
1660
1669
|
}
|
|
1661
1670
|
|
|
1671
|
+
// Context-headroom gauge (RFC context-headroom-surface): occupancy/cap
|
|
1672
|
+
// with a fill bar, amber when tight (≥80%). '—' when no snapshot yet.
|
|
1673
|
+
function fmtTok(n) {
|
|
1674
|
+
return n >= 1000 ? (n / 1000).toFixed(n >= 10000 ? 0 : 1) + 'k' : String(n);
|
|
1675
|
+
}
|
|
1676
|
+
function formatContext(ctx) {
|
|
1677
|
+
if (!ctx || ctx.state === 'unknown' || typeof ctx.occupancy !== 'number') {
|
|
1678
|
+
return '<span style="color:var(--text-dim)">—</span>';
|
|
1679
|
+
}
|
|
1680
|
+
if (ctx.cap == null || ctx.pct == null) {
|
|
1681
|
+
return `<span>${fmtTok(ctx.occupancy)} <span style="color:var(--text-dim)">(no cap)</span></span>`;
|
|
1682
|
+
}
|
|
1683
|
+
const pct = Math.max(0, Math.min(1, ctx.pct));
|
|
1684
|
+
const tight = ctx.state === 'tight';
|
|
1685
|
+
const col = tight ? 'var(--amber, #d99)' : 'var(--green)';
|
|
1686
|
+
return `<span class="ctx-gauge" title="${fmtTok(ctx.occupancy)} / ${fmtTok(ctx.cap)} tokens — ${Math.round(pct * 100)}% of cap${tight ? ' (tight — /compact near)' : ''}">`
|
|
1687
|
+
+ `<span class="ctx-text">${fmtTok(ctx.occupancy)}/${fmtTok(ctx.cap)} (${Math.round(pct * 100)}%)</span>`
|
|
1688
|
+
+ `<span class="ctx-bar"><span class="ctx-fill" style="width:${(pct * 100).toFixed(0)}%;background:${col}"></span></span>`
|
|
1689
|
+
+ `</span>`;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1662
1692
|
function formatUptime(timestamp) {
|
|
1663
1693
|
if (!timestamp) return '--';
|
|
1664
1694
|
const d = new Date(timestamp);
|
|
@@ -1693,6 +1723,7 @@
|
|
|
1693
1723
|
<div class="meta-item"><label>Uptime </label><span>${formatUptime(a.uptime)}</span></div>
|
|
1694
1724
|
<div class="meta-item"><label>Mem </label><span>${a.memory || '--'}</span></div>
|
|
1695
1725
|
<div class="meta-item"><label>Last turn </label><span>${a.lastTurnAt ? formatTimestamp(a.lastTurnAt) : '—'}</span></div>
|
|
1726
|
+
<div class="meta-item meta-context"><label>Context </label>${formatContext(a.context)}</div>
|
|
1696
1727
|
<div class="meta-item"><label>Profile </label><span>${escapeHtml(a.extends)}</span></div>
|
|
1697
1728
|
<div class="meta-item"><label>Auth </label><span>${a.auth.authenticated ? '✓' : '✗'}</span></div>
|
|
1698
1729
|
<div class="meta-item"><label>Account </label><span>${a.primaryAccount ? escapeHtml(a.primaryAccount) : '<span style="color:var(--text-dim)">default</span>'}</span></div>
|
|
@@ -13849,7 +13849,8 @@ var SubagentSchema = exports_external.object({
|
|
|
13849
13849
|
var SessionSchema = exports_external.object({
|
|
13850
13850
|
max_idle: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '2h', '30m')").optional().describe("Start a fresh session if the previous one has been idle " + "longer than this duration. Examples: '2h', '30m', '7200s'."),
|
|
13851
13851
|
max_turns: exports_external.number().int().positive().optional().describe("Start a fresh session if the previous one has more user " + "turns than this. Useful for preventing context bloat on " + "long-running agents."),
|
|
13852
|
-
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context.")
|
|
13852
|
+
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context."),
|
|
13853
|
+
idle_clear_after: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '3h', '90m')").optional().describe("Auto-run /clear (wipe the working context) after the live " + "session has been idle this long. Defaults to '3h' when unset " + "(on by default); set '0s' to disable. Long-term memory lives " + "in Hindsight, so a clear loses only the in-session thread.")
|
|
13853
13854
|
}).optional();
|
|
13854
13855
|
var SessionContinuitySchema = exports_external.object({
|
|
13855
13856
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
|
@@ -11457,7 +11457,8 @@ var init_schema = __esm(() => {
|
|
|
11457
11457
|
SessionSchema = exports_external.object({
|
|
11458
11458
|
max_idle: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '2h', '30m')").optional().describe("Start a fresh session if the previous one has been idle " + "longer than this duration. Examples: '2h', '30m', '7200s'."),
|
|
11459
11459
|
max_turns: exports_external.number().int().positive().optional().describe("Start a fresh session if the previous one has more user " + "turns than this. Useful for preventing context bloat on " + "long-running agents."),
|
|
11460
|
-
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context.")
|
|
11460
|
+
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context."),
|
|
11461
|
+
idle_clear_after: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '3h', '90m')").optional().describe("Auto-run /clear (wipe the working context) after the live " + "session has been idle this long. Defaults to '3h' when unset " + "(on by default); set '0s' to disable. Long-term memory lives " + "in Hindsight, so a clear loses only the in-session thread.")
|
|
11461
11462
|
}).optional();
|
|
11462
11463
|
SessionContinuitySchema = exports_external.object({
|
|
11463
11464
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
|
@@ -11457,7 +11457,8 @@ var init_schema = __esm(() => {
|
|
|
11457
11457
|
SessionSchema = exports_external.object({
|
|
11458
11458
|
max_idle: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '2h', '30m')").optional().describe("Start a fresh session if the previous one has been idle " + "longer than this duration. Examples: '2h', '30m', '7200s'."),
|
|
11459
11459
|
max_turns: exports_external.number().int().positive().optional().describe("Start a fresh session if the previous one has more user " + "turns than this. Useful for preventing context bloat on " + "long-running agents."),
|
|
11460
|
-
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context.")
|
|
11460
|
+
max_context_tokens: exports_external.number().int().positive().optional().describe("Proactively run /compact when the live context window " + "occupancy (latest assistant turn input + cache-read + " + "cache-creation tokens) reaches this many tokens. Opt-in: " + "unset means rely on Claude Code's native auto-compaction. " + "Useful on large-window models (e.g. 1M Opus) to hold a " + "deliberately lean working context."),
|
|
11461
|
+
idle_clear_after: exports_external.string().regex(/^\d+[smh]$/, "Duration must be a number followed by s, m, or h (e.g. '3h', '90m')").optional().describe("Auto-run /clear (wipe the working context) after the live " + "session has been idle this long. Defaults to '3h' when unset " + "(on by default); set '0s' to disable. Long-term memory lives " + "in Hindsight, so a clear loses only the in-session thread.")
|
|
11461
11462
|
}).optional();
|
|
11462
11463
|
SessionContinuitySchema = exports_external.object({
|
|
11463
11464
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
package/package.json
CHANGED