Compare commits
2 Commits
main
...
pr/session
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99546ca3d9 | ||
|
|
26a5d02e69 |
@ -18,6 +18,7 @@ Docs: https://docs.clawd.bot
|
||||
- Auth: dedupe codex-cli profiles when tokens match custom openai-codex entries. (#1264) — thanks @odrobnik.
|
||||
- Agents: avoid misclassifying context-window-too-small errors as context overflow. (#1266) — thanks @humanwritten.
|
||||
- Slack: resolve Bolt default-export shapes for monitor startup. (#1208) — thanks @24601.
|
||||
- UI: allow editing session labels in the Sessions table. (#1294) — thanks @bradleypriest.
|
||||
|
||||
## 2026.1.19-3
|
||||
|
||||
|
||||
@ -10,7 +10,6 @@ import {
|
||||
signDevicePayload,
|
||||
} from "../infra/device-identity.js";
|
||||
import { emitHeartbeatEvent } from "../infra/heartbeat-events.js";
|
||||
import { loadOrCreateDeviceIdentity } from "../infra/device-identity.js";
|
||||
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
|
||||
import {
|
||||
connectOk,
|
||||
|
||||
@ -111,7 +111,7 @@ export function enqueueCommand<T>(
|
||||
return enqueueCommandInLane(CommandLane.Main, task, opts);
|
||||
}
|
||||
|
||||
export function getQueueSize(lane = CommandLane.Main) {
|
||||
export function getQueueSize(lane: string = CommandLane.Main) {
|
||||
const state = lanes.get(lane);
|
||||
if (!state) return 0;
|
||||
return state.queue.length + state.active;
|
||||
@ -125,7 +125,7 @@ export function getTotalQueueSize() {
|
||||
return total;
|
||||
}
|
||||
|
||||
export function clearCommandLane(lane = CommandLane.Main) {
|
||||
export function clearCommandLane(lane: string = CommandLane.Main) {
|
||||
const cleaned = lane.trim() || CommandLane.Main;
|
||||
const state = lanes.get(cleaned);
|
||||
if (!state) return 0;
|
||||
|
||||
@ -43,6 +43,7 @@ export async function patchSession(
|
||||
state: SessionsState,
|
||||
key: string,
|
||||
patch: {
|
||||
label?: string | null;
|
||||
thinkingLevel?: string | null;
|
||||
verboseLevel?: string | null;
|
||||
reasoningLevel?: string | null;
|
||||
@ -50,6 +51,7 @@ export async function patchSession(
|
||||
) {
|
||||
if (!state.client || !state.connected) return;
|
||||
const params: Record<string, unknown> = { key };
|
||||
if ("label" in patch) params.label = patch.label;
|
||||
if ("thinkingLevel" in patch) params.thinkingLevel = patch.thinkingLevel;
|
||||
if ("verboseLevel" in patch) params.verboseLevel = patch.verboseLevel;
|
||||
if ("reasoningLevel" in patch) params.reasoningLevel = patch.reasoningLevel;
|
||||
|
||||
93
ui/src/ui/views/sessions.test.ts
Normal file
93
ui/src/ui/views/sessions.test.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import { render } from "lit";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import type { GatewaySessionRow, SessionsListResult } from "../types";
|
||||
import { renderSessions, type SessionsProps } from "./sessions";
|
||||
|
||||
function createRow(overrides: Partial<GatewaySessionRow> = {}): GatewaySessionRow {
|
||||
return {
|
||||
key: "session-1",
|
||||
kind: "direct",
|
||||
updatedAt: 0,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function createResult(rows: GatewaySessionRow[]): SessionsListResult {
|
||||
return {
|
||||
ts: 0,
|
||||
path: "/sessions",
|
||||
count: rows.length,
|
||||
defaults: {
|
||||
model: null,
|
||||
contextTokens: null,
|
||||
},
|
||||
sessions: rows,
|
||||
};
|
||||
}
|
||||
|
||||
function createProps(overrides: Partial<SessionsProps> = {}): SessionsProps {
|
||||
return {
|
||||
loading: false,
|
||||
result: createResult([]),
|
||||
error: null,
|
||||
activeMinutes: "",
|
||||
limit: "",
|
||||
includeGlobal: true,
|
||||
includeUnknown: true,
|
||||
basePath: "/",
|
||||
onFiltersChange: () => undefined,
|
||||
onRefresh: () => undefined,
|
||||
onPatch: () => undefined,
|
||||
onDelete: () => undefined,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("sessions view", () => {
|
||||
it("skips patching when the label is unchanged", () => {
|
||||
const container = document.createElement("div");
|
||||
const onPatch = vi.fn();
|
||||
const row = createRow({ label: "Alpha" });
|
||||
|
||||
render(
|
||||
renderSessions(
|
||||
createProps({
|
||||
result: createResult([row]),
|
||||
onPatch,
|
||||
}),
|
||||
),
|
||||
container,
|
||||
);
|
||||
|
||||
const input = container.querySelector("input") as HTMLInputElement | null;
|
||||
expect(input).not.toBeNull();
|
||||
input?.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
|
||||
expect(onPatch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("clears labels when the input is empty", () => {
|
||||
const container = document.createElement("div");
|
||||
const onPatch = vi.fn();
|
||||
const row = createRow({ label: "Alpha" });
|
||||
|
||||
render(
|
||||
renderSessions(
|
||||
createProps({
|
||||
result: createResult([row]),
|
||||
onPatch,
|
||||
}),
|
||||
),
|
||||
container,
|
||||
);
|
||||
|
||||
const input = container.querySelector("input") as HTMLInputElement | null;
|
||||
expect(input).not.toBeNull();
|
||||
if (!input) return;
|
||||
input.value = " ";
|
||||
input.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
|
||||
expect(onPatch).toHaveBeenCalledWith("session-1", { label: null });
|
||||
});
|
||||
});
|
||||
@ -24,6 +24,7 @@ export type SessionsProps = {
|
||||
onPatch: (
|
||||
key: string,
|
||||
patch: {
|
||||
label?: string | null;
|
||||
thinkingLevel?: string | null;
|
||||
verboseLevel?: string | null;
|
||||
reasoningLevel?: string | null;
|
||||
@ -195,7 +196,19 @@ function renderRow(
|
||||
<div class="mono">${canLink
|
||||
? html`<a href=${chatUrl} class="session-link">${displayName}</a>`
|
||||
: displayName}</div>
|
||||
<div>${row.label ?? ""}</div>
|
||||
<div>
|
||||
<input
|
||||
.value=${row.label ?? ""}
|
||||
?disabled=${disabled}
|
||||
placeholder="(optional)"
|
||||
@change=${(e: Event) => {
|
||||
const value = (e.target as HTMLInputElement).value.trim();
|
||||
const current = (row.label ?? "").trim();
|
||||
if (value === current) return;
|
||||
onPatch(row.key, { label: value || null });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>${row.kind}</div>
|
||||
<div>${updated}</div>
|
||||
<div>${formatSessionTokens(row)}</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user