switchroom 0.15.41 → 0.15.43
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 +157 -13
- 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 +397 -226
- 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.43";
|
|
50771
|
+
var COMMIT_SHA = "5480a4c3";
|
|
50713
50772
|
|
|
50714
50773
|
// src/cli/resolve-version.ts
|
|
50715
50774
|
function readPackageVersion() {
|
|
@@ -68833,6 +68892,9 @@ var HINDSIGHT_DEFAULT_RERANKER_BUCKET_BATCHING = "true";
|
|
|
68833
68892
|
var HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES = 150;
|
|
68834
68893
|
var HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT = 4;
|
|
68835
68894
|
var HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8;
|
|
68895
|
+
var HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600;
|
|
68896
|
+
var HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_BATCH_SIZE = 12;
|
|
68897
|
+
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS = 3;
|
|
68836
68898
|
var HINDSIGHT_DEFAULT_MEM_LIMIT = "4g";
|
|
68837
68899
|
var HINDSIGHT_DEFAULT_MEM_RESERVATION = "2g";
|
|
68838
68900
|
var HINDSIGHT_DEFAULT_PIDS_LIMIT = 1000;
|
|
@@ -68917,7 +68979,13 @@ function startHindsight(ports) {
|
|
|
68917
68979
|
"-e",
|
|
68918
68980
|
`HINDSIGHT_API_RERANKER_LOCAL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT}`,
|
|
68919
68981
|
"-e",
|
|
68920
|
-
`HINDSIGHT_API_RECALL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT}
|
|
68982
|
+
`HINDSIGHT_API_RECALL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT}`,
|
|
68983
|
+
"-e",
|
|
68984
|
+
`HINDSIGHT_API_REFLECT_WALL_TIMEOUT=${HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S}`,
|
|
68985
|
+
"-e",
|
|
68986
|
+
`HINDSIGHT_API_CONSOLIDATION_LLM_BATCH_SIZE=${HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_BATCH_SIZE}`,
|
|
68987
|
+
"-e",
|
|
68988
|
+
`HINDSIGHT_API_WORKER_CONSOLIDATION_MAX_SLOTS=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS}`
|
|
68921
68989
|
];
|
|
68922
68990
|
const args = [
|
|
68923
68991
|
"run",
|
|
@@ -68965,6 +69033,29 @@ function stopHindsight() {
|
|
|
68965
69033
|
execFileSync16("docker", ["rm", "switchroom-hindsight"], { stdio: "pipe" });
|
|
68966
69034
|
} catch {}
|
|
68967
69035
|
}
|
|
69036
|
+
function pullHindsightImage() {
|
|
69037
|
+
execFileSync16("docker", ["pull", HINDSIGHT_IMAGE], { stdio: "inherit" });
|
|
69038
|
+
}
|
|
69039
|
+
function getRunningHindsightPorts() {
|
|
69040
|
+
const readPort = (containerPort) => {
|
|
69041
|
+
try {
|
|
69042
|
+
const out = execFileSync16("docker", ["port", "switchroom-hindsight", `${containerPort}/tcp`], { stdio: "pipe", encoding: "utf-8" });
|
|
69043
|
+
const m = out.split(`
|
|
69044
|
+
`).map((l) => l.trim()).find((l) => l.length > 0);
|
|
69045
|
+
if (!m)
|
|
69046
|
+
return null;
|
|
69047
|
+
const port = Number(m.slice(m.lastIndexOf(":") + 1));
|
|
69048
|
+
return Number.isInteger(port) && port > 0 ? port : null;
|
|
69049
|
+
} catch {
|
|
69050
|
+
return null;
|
|
69051
|
+
}
|
|
69052
|
+
};
|
|
69053
|
+
const apiPort = readPort(HINDSIGHT_DEFAULT_API_PORT);
|
|
69054
|
+
if (apiPort === null)
|
|
69055
|
+
return null;
|
|
69056
|
+
const uiPort = readPort(HINDSIGHT_DEFAULT_UI_PORT) ?? (apiPort === HINDSIGHT_DEFAULT_API_PORT ? HINDSIGHT_DEFAULT_UI_PORT : 19999);
|
|
69057
|
+
return { apiPort, uiPort };
|
|
69058
|
+
}
|
|
68968
69059
|
function getHindsightStatus() {
|
|
68969
69060
|
try {
|
|
68970
69061
|
const output = execFileSync16("docker", ["ps", "-a", "--filter", "name=switchroom-hindsight", "--format", "{{.Status}}"], { stdio: "pipe", encoding: "utf-8" });
|
|
@@ -68992,6 +69083,9 @@ function generateHindsightComposeSnippet() {
|
|
|
68992
69083
|
` - HINDSIGHT_API_RERANKER_MAX_CANDIDATES=${HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES}`,
|
|
68993
69084
|
` - HINDSIGHT_API_RERANKER_LOCAL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT}`,
|
|
68994
69085
|
` - HINDSIGHT_API_RECALL_MAX_CONCURRENT=${HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT}`,
|
|
69086
|
+
` - HINDSIGHT_API_REFLECT_WALL_TIMEOUT=${HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S}`,
|
|
69087
|
+
` - HINDSIGHT_API_CONSOLIDATION_LLM_BATCH_SIZE=${HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_BATCH_SIZE}`,
|
|
69088
|
+
` - HINDSIGHT_API_WORKER_CONSOLIDATION_MAX_SLOTS=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS}`,
|
|
68995
69089
|
` mem_limit: ${HINDSIGHT_DEFAULT_MEM_LIMIT}`,
|
|
68996
69090
|
` mem_reservation: ${HINDSIGHT_DEFAULT_MEM_RESERVATION}`,
|
|
68997
69091
|
` pids_limit: ${HINDSIGHT_DEFAULT_PIDS_LIMIT}`,
|
|
@@ -69195,7 +69289,7 @@ Cross-agent reflection plan
|
|
|
69195
69289
|
}
|
|
69196
69290
|
console.log();
|
|
69197
69291
|
}));
|
|
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) => {
|
|
69292
|
+
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
69293
|
if (opts.status) {
|
|
69200
69294
|
if (!isDockerAvailable()) {
|
|
69201
69295
|
console.log(source_default.red(" Docker is not available."));
|
|
@@ -69236,24 +69330,41 @@ Cross-agent reflection plan
|
|
|
69236
69330
|
`));
|
|
69237
69331
|
process.exit(1);
|
|
69238
69332
|
}
|
|
69239
|
-
|
|
69333
|
+
const recreate = opts.recreate === true;
|
|
69334
|
+
const reusePorts = recreate ? getRunningHindsightPorts() : null;
|
|
69335
|
+
if (isHindsightRunning() && !recreate) {
|
|
69240
69336
|
console.log(source_default.green(`
|
|
69241
69337
|
Hindsight container is already running (switchroom-hindsight).
|
|
69242
69338
|
`));
|
|
69243
69339
|
return;
|
|
69244
69340
|
}
|
|
69341
|
+
if (recreate) {
|
|
69342
|
+
console.log(source_default.gray(" Pulling latest Hindsight image..."));
|
|
69343
|
+
try {
|
|
69344
|
+
pullHindsightImage();
|
|
69345
|
+
} catch (err) {
|
|
69346
|
+
console.error(source_default.red(`
|
|
69347
|
+
Failed to pull Hindsight image: ${err.message}
|
|
69348
|
+
`));
|
|
69349
|
+
process.exit(1);
|
|
69350
|
+
}
|
|
69351
|
+
}
|
|
69245
69352
|
if (isHindsightContainerExists()) {
|
|
69246
|
-
console.log(source_default.gray(" Removing
|
|
69353
|
+
console.log(source_default.gray(" Removing existing switchroom-hindsight container..."));
|
|
69247
69354
|
stopHindsight();
|
|
69248
69355
|
}
|
|
69249
69356
|
let ports;
|
|
69250
|
-
|
|
69251
|
-
ports =
|
|
69252
|
-
}
|
|
69253
|
-
|
|
69357
|
+
if (reusePorts) {
|
|
69358
|
+
ports = reusePorts;
|
|
69359
|
+
} else {
|
|
69360
|
+
try {
|
|
69361
|
+
ports = await pickHindsightPorts();
|
|
69362
|
+
} catch (err) {
|
|
69363
|
+
console.error(source_default.red(`
|
|
69254
69364
|
${err.message}
|
|
69255
69365
|
`));
|
|
69256
|
-
|
|
69366
|
+
process.exit(1);
|
|
69367
|
+
}
|
|
69257
69368
|
}
|
|
69258
69369
|
if (ports.apiPort !== HINDSIGHT_DEFAULT_API_PORT) {
|
|
69259
69370
|
console.log(source_default.yellow(` Port ${HINDSIGHT_DEFAULT_API_PORT} is already in use; ` + `using ${ports.apiPort}/${ports.uiPort} instead.`));
|
|
@@ -74629,6 +74740,17 @@ var DASHBOARD_CACHE_TTL = {
|
|
|
74629
74740
|
approvals: 15000,
|
|
74630
74741
|
grants: 15000
|
|
74631
74742
|
};
|
|
74743
|
+
function readContextOccupancy(agentsDir, name) {
|
|
74744
|
+
try {
|
|
74745
|
+
const raw = readFileSync44(resolve28(agentsDir, name, "context-occupancy.json"), "utf-8");
|
|
74746
|
+
const s = JSON.parse(raw);
|
|
74747
|
+
if (typeof s?.occupancy !== "number")
|
|
74748
|
+
return null;
|
|
74749
|
+
return s;
|
|
74750
|
+
} catch {
|
|
74751
|
+
return null;
|
|
74752
|
+
}
|
|
74753
|
+
}
|
|
74632
74754
|
function readLastTurnAt(agentsDir, name) {
|
|
74633
74755
|
try {
|
|
74634
74756
|
const db = openTurnsDb(resolve28(agentsDir, name));
|
|
@@ -74663,6 +74785,7 @@ async function cachedFleetStatus(now, fetchImpl = fetchFleetStatusViaHostd) {
|
|
|
74663
74785
|
}
|
|
74664
74786
|
async function handleGetAgents(config, deps = {}) {
|
|
74665
74787
|
const readLastTurn = deps.readLastTurn ?? readLastTurnAt;
|
|
74788
|
+
const readContext = deps.readContext ?? readContextOccupancy;
|
|
74666
74789
|
const statuses = getAllAgentStatuses(config);
|
|
74667
74790
|
const authStatuses = getAllAuthStatuses(config);
|
|
74668
74791
|
const agentsDir = resolveAgentsDir(config);
|
|
@@ -74695,7 +74818,8 @@ async function handleGetAgents(config, deps = {}) {
|
|
|
74695
74818
|
expiresAt: auth?.expiresAt
|
|
74696
74819
|
},
|
|
74697
74820
|
memoryCollection: collection,
|
|
74698
|
-
lastTurnAt: readLastTurn(agentsDir, name)
|
|
74821
|
+
lastTurnAt: readLastTurn(agentsDir, name),
|
|
74822
|
+
context: readContext(agentsDir, name)
|
|
74699
74823
|
});
|
|
74700
74824
|
}
|
|
74701
74825
|
return agents;
|
|
@@ -78047,6 +78171,26 @@ function planUpdate(opts) {
|
|
|
78047
78171
|
throw new Error("switchroom webd install failed");
|
|
78048
78172
|
}
|
|
78049
78173
|
});
|
|
78174
|
+
let memoryBackendHindsight;
|
|
78175
|
+
if (typeof opts.memoryBackendHindsight === "boolean") {
|
|
78176
|
+
memoryBackendHindsight = opts.memoryBackendHindsight;
|
|
78177
|
+
} else {
|
|
78178
|
+
try {
|
|
78179
|
+
memoryBackendHindsight = loadConfig().memory?.backend === "hindsight";
|
|
78180
|
+
} catch {
|
|
78181
|
+
memoryBackendHindsight = false;
|
|
78182
|
+
}
|
|
78183
|
+
}
|
|
78184
|
+
steps.push({
|
|
78185
|
+
name: "refresh-hindsight",
|
|
78186
|
+
description: "switchroom memory setup --recreate \u2014 pull latest hindsight image + recreate the memory singleton (standalone container, reusing its current port)",
|
|
78187
|
+
skipReason: !memoryBackendHindsight ? "memory.backend is not hindsight \u2014 no hindsight singleton to refresh" : opts.skipImages ? "--skip-images flag set" : undefined,
|
|
78188
|
+
run: () => {
|
|
78189
|
+
const r = runner(process.execPath, [scriptPath, "memory", "setup", "--recreate"]);
|
|
78190
|
+
if (r.status !== 0)
|
|
78191
|
+
throw new Error("switchroom memory setup --recreate failed");
|
|
78192
|
+
}
|
|
78193
|
+
});
|
|
78050
78194
|
steps.push({
|
|
78051
78195
|
name: "sync-bundled-skills",
|
|
78052
78196
|
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