Merged via /review-pr -> /prepare-pr -> /merge-pr. Prepared head SHA: 7533b85156186863609fee9379cd9aedf74435af Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com> Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com> Reviewed-by: @shakkernerd
228 lines
7.0 KiB
TypeScript
228 lines
7.0 KiB
TypeScript
import { lookupContextTokens } from "../agents/context.js";
|
|
import { DEFAULT_CONTEXT_TOKENS } from "../agents/defaults.js";
|
|
import { loadConfig } from "../config/config.js";
|
|
import { loadSessionStore, resolveFreshSessionTotalTokens } from "../config/sessions.js";
|
|
import { classifySessionKey } from "../gateway/session-utils.js";
|
|
import { info } from "../globals.js";
|
|
import { parseAgentSessionKey } from "../routing/session-key.js";
|
|
import type { RuntimeEnv } from "../runtime.js";
|
|
import { isRich, theme } from "../terminal/theme.js";
|
|
import { resolveSessionStoreTargets } from "./session-store-targets.js";
|
|
import {
|
|
formatSessionAgeCell,
|
|
formatSessionFlagsCell,
|
|
formatSessionKeyCell,
|
|
formatSessionModelCell,
|
|
resolveSessionDisplayDefaults,
|
|
resolveSessionDisplayModel,
|
|
SESSION_AGE_PAD,
|
|
SESSION_KEY_PAD,
|
|
SESSION_MODEL_PAD,
|
|
type SessionDisplayRow,
|
|
toSessionDisplayRows,
|
|
} from "./sessions-table.js";
|
|
|
|
type SessionRow = SessionDisplayRow & {
|
|
agentId: string;
|
|
kind: "direct" | "group" | "global" | "unknown";
|
|
};
|
|
|
|
const AGENT_PAD = 10;
|
|
const KIND_PAD = 6;
|
|
const TOKENS_PAD = 20;
|
|
|
|
const formatKTokens = (value: number) => `${(value / 1000).toFixed(value >= 10_000 ? 0 : 1)}k`;
|
|
|
|
const colorByPct = (label: string, pct: number | null, rich: boolean) => {
|
|
if (!rich || pct === null) {
|
|
return label;
|
|
}
|
|
if (pct >= 95) {
|
|
return theme.error(label);
|
|
}
|
|
if (pct >= 80) {
|
|
return theme.warn(label);
|
|
}
|
|
if (pct >= 60) {
|
|
return theme.success(label);
|
|
}
|
|
return theme.muted(label);
|
|
};
|
|
|
|
const formatTokensCell = (
|
|
total: number | undefined,
|
|
contextTokens: number | null,
|
|
rich: boolean,
|
|
) => {
|
|
if (total === undefined) {
|
|
const ctxLabel = contextTokens ? formatKTokens(contextTokens) : "?";
|
|
const label = `unknown/${ctxLabel} (?%)`;
|
|
return rich ? theme.muted(label.padEnd(TOKENS_PAD)) : label.padEnd(TOKENS_PAD);
|
|
}
|
|
const totalLabel = formatKTokens(total);
|
|
const ctxLabel = contextTokens ? formatKTokens(contextTokens) : "?";
|
|
const pct = contextTokens ? Math.min(999, Math.round((total / contextTokens) * 100)) : null;
|
|
const label = `${totalLabel}/${ctxLabel} (${pct ?? "?"}%)`;
|
|
const padded = label.padEnd(TOKENS_PAD);
|
|
return colorByPct(padded, pct, rich);
|
|
};
|
|
|
|
const formatKindCell = (kind: SessionRow["kind"], rich: boolean) => {
|
|
const label = kind.padEnd(KIND_PAD);
|
|
if (!rich) {
|
|
return label;
|
|
}
|
|
if (kind === "group") {
|
|
return theme.accentBright(label);
|
|
}
|
|
if (kind === "global") {
|
|
return theme.warn(label);
|
|
}
|
|
if (kind === "direct") {
|
|
return theme.accent(label);
|
|
}
|
|
return theme.muted(label);
|
|
};
|
|
|
|
export async function sessionsCommand(
|
|
opts: { json?: boolean; store?: string; active?: string; agent?: string; allAgents?: boolean },
|
|
runtime: RuntimeEnv,
|
|
) {
|
|
const aggregateAgents = opts.allAgents === true;
|
|
const cfg = loadConfig();
|
|
const displayDefaults = resolveSessionDisplayDefaults(cfg);
|
|
const configContextTokens =
|
|
cfg.agents?.defaults?.contextTokens ??
|
|
lookupContextTokens(displayDefaults.model) ??
|
|
DEFAULT_CONTEXT_TOKENS;
|
|
let targets: ReturnType<typeof resolveSessionStoreTargets>;
|
|
try {
|
|
targets = resolveSessionStoreTargets(cfg, {
|
|
store: opts.store,
|
|
agent: opts.agent,
|
|
allAgents: opts.allAgents,
|
|
});
|
|
} catch (error) {
|
|
runtime.error(error instanceof Error ? error.message : String(error));
|
|
runtime.exit(1);
|
|
return;
|
|
}
|
|
|
|
let activeMinutes: number | undefined;
|
|
if (opts.active !== undefined) {
|
|
const parsed = Number.parseInt(String(opts.active), 10);
|
|
if (Number.isNaN(parsed) || parsed <= 0) {
|
|
runtime.error("--active must be a positive integer (minutes)");
|
|
runtime.exit(1);
|
|
return;
|
|
}
|
|
activeMinutes = parsed;
|
|
}
|
|
|
|
const rows = targets
|
|
.flatMap((target) => {
|
|
const store = loadSessionStore(target.storePath);
|
|
return toSessionDisplayRows(store).map((row) => ({
|
|
...row,
|
|
agentId: parseAgentSessionKey(row.key)?.agentId ?? target.agentId,
|
|
kind: classifySessionKey(row.key, store[row.key]),
|
|
}));
|
|
})
|
|
.filter((row) => {
|
|
if (activeMinutes === undefined) {
|
|
return true;
|
|
}
|
|
if (!row.updatedAt) {
|
|
return false;
|
|
}
|
|
return Date.now() - row.updatedAt <= activeMinutes * 60_000;
|
|
})
|
|
.toSorted((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
|
|
|
|
if (opts.json) {
|
|
const multi = targets.length > 1;
|
|
const aggregate = aggregateAgents || multi;
|
|
runtime.log(
|
|
JSON.stringify(
|
|
{
|
|
path: aggregate ? null : (targets[0]?.storePath ?? null),
|
|
stores: aggregate
|
|
? targets.map((target) => ({
|
|
agentId: target.agentId,
|
|
path: target.storePath,
|
|
}))
|
|
: undefined,
|
|
allAgents: aggregateAgents ? true : undefined,
|
|
count: rows.length,
|
|
activeMinutes: activeMinutes ?? null,
|
|
sessions: rows.map((r) => {
|
|
const model = resolveSessionDisplayModel(cfg, r, displayDefaults);
|
|
return {
|
|
...r,
|
|
totalTokens: resolveFreshSessionTotalTokens(r) ?? null,
|
|
totalTokensFresh:
|
|
typeof r.totalTokens === "number" ? r.totalTokensFresh !== false : false,
|
|
contextTokens:
|
|
r.contextTokens ?? lookupContextTokens(model) ?? configContextTokens ?? null,
|
|
model,
|
|
};
|
|
}),
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (targets.length === 1 && !aggregateAgents) {
|
|
runtime.log(info(`Session store: ${targets[0]?.storePath}`));
|
|
} else {
|
|
runtime.log(
|
|
info(`Session stores: ${targets.length} (${targets.map((t) => t.agentId).join(", ")})`),
|
|
);
|
|
}
|
|
runtime.log(info(`Sessions listed: ${rows.length}`));
|
|
if (activeMinutes) {
|
|
runtime.log(info(`Filtered to last ${activeMinutes} minute(s)`));
|
|
}
|
|
if (rows.length === 0) {
|
|
runtime.log("No sessions found.");
|
|
return;
|
|
}
|
|
|
|
const rich = isRich();
|
|
const showAgentColumn = aggregateAgents || targets.length > 1;
|
|
const header = [
|
|
...(showAgentColumn ? ["Agent".padEnd(AGENT_PAD)] : []),
|
|
"Kind".padEnd(KIND_PAD),
|
|
"Key".padEnd(SESSION_KEY_PAD),
|
|
"Age".padEnd(SESSION_AGE_PAD),
|
|
"Model".padEnd(SESSION_MODEL_PAD),
|
|
"Tokens (ctx %)".padEnd(TOKENS_PAD),
|
|
"Flags",
|
|
].join(" ");
|
|
|
|
runtime.log(rich ? theme.heading(header) : header);
|
|
|
|
for (const row of rows) {
|
|
const model = resolveSessionDisplayModel(cfg, row, displayDefaults);
|
|
const contextTokens = row.contextTokens ?? lookupContextTokens(model) ?? configContextTokens;
|
|
const total = resolveFreshSessionTotalTokens(row);
|
|
|
|
const line = [
|
|
...(showAgentColumn
|
|
? [rich ? theme.accentBright(row.agentId.padEnd(AGENT_PAD)) : row.agentId.padEnd(AGENT_PAD)]
|
|
: []),
|
|
formatKindCell(row.kind, rich),
|
|
formatSessionKeyCell(row.key, rich),
|
|
formatSessionAgeCell(row.updatedAt, rich),
|
|
formatSessionModelCell(model, rich),
|
|
formatTokensCell(total, contextTokens ?? null, rich),
|
|
formatSessionFlagsCell(row, rich),
|
|
].join(" ");
|
|
|
|
runtime.log(line.trimEnd());
|
|
}
|
|
}
|