@@ -7,6 +7,7 @@
|
|||||||
- Memory: allow custom OpenAI-compatible embedding endpoints for memory search (remote baseUrl/apiKey/headers). (#819 — thanks @mukhtharcm)
|
- Memory: allow custom OpenAI-compatible embedding endpoints for memory search (remote baseUrl/apiKey/headers). (#819 — thanks @mukhtharcm)
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
- System events: include local timestamps when events are injected into prompts. (#245 — thanks @thewilloftheshadow)
|
||||||
- Cron: accept `jobId` aliases for cron update/run/remove params in gateway validation. (#252 — thanks @thewilloftheshadow)
|
- Cron: accept `jobId` aliases for cron update/run/remove params in gateway validation. (#252 — thanks @thewilloftheshadow)
|
||||||
- Models/Google: normalize Gemini 3 model ids to preview variants before runtime selection. (#795 — thanks @thewilloftheshadow)
|
- Models/Google: normalize Gemini 3 model ids to preview variants before runtime selection. (#795 — thanks @thewilloftheshadow)
|
||||||
- TUI: keep the last streamed response instead of replacing it with “(no output)”. (#747 — thanks @thewilloftheshadow)
|
- TUI: keep the last streamed response instead of replacing it with “(no output)”. (#747 — thanks @thewilloftheshadow)
|
||||||
|
|||||||
43
src/auto-reply/reply/session-updates.test.ts
Normal file
43
src/auto-reply/reply/session-updates.test.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import type { ClawdbotConfig } from "../../config/config.js";
|
||||||
|
import {
|
||||||
|
enqueueSystemEvent,
|
||||||
|
resetSystemEventsForTest,
|
||||||
|
} from "../../infra/system-events.js";
|
||||||
|
import { prependSystemEvents } from "./session-updates.js";
|
||||||
|
|
||||||
|
describe("prependSystemEvents", () => {
|
||||||
|
it("adds a local timestamp to queued system events", async () => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
const timestamp = new Date("2026-01-12T20:19:17");
|
||||||
|
vi.setSystemTime(timestamp);
|
||||||
|
|
||||||
|
enqueueSystemEvent("Model switched.", { sessionKey: "agent:main:main" });
|
||||||
|
|
||||||
|
const result = await prependSystemEvents({
|
||||||
|
cfg: {} as ClawdbotConfig,
|
||||||
|
sessionKey: "agent:main:main",
|
||||||
|
isMainSession: false,
|
||||||
|
isNewSession: false,
|
||||||
|
prefixedBodyBase: "User: hi",
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectedTimestamp = timestamp.toLocaleString("en-US", {
|
||||||
|
hour12: false,
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toContain(
|
||||||
|
`System: [${expectedTimestamp}] Model switched.`,
|
||||||
|
);
|
||||||
|
|
||||||
|
resetSystemEventsForTest();
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -4,7 +4,7 @@ import { buildWorkspaceSkillSnapshot } from "../../agents/skills.js";
|
|||||||
import type { ClawdbotConfig } from "../../config/config.js";
|
import type { ClawdbotConfig } from "../../config/config.js";
|
||||||
import { type SessionEntry, saveSessionStore } from "../../config/sessions.js";
|
import { type SessionEntry, saveSessionStore } from "../../config/sessions.js";
|
||||||
import { buildProviderSummary } from "../../infra/provider-summary.js";
|
import { buildProviderSummary } from "../../infra/provider-summary.js";
|
||||||
import { drainSystemEvents } from "../../infra/system-events.js";
|
import { drainSystemEventEntries } from "../../infra/system-events.js";
|
||||||
|
|
||||||
export async function prependSystemEvents(params: {
|
export async function prependSystemEvents(params: {
|
||||||
cfg: ClawdbotConfig;
|
cfg: ClawdbotConfig;
|
||||||
@@ -25,10 +25,27 @@ export async function prependSystemEvents(params: {
|
|||||||
return trimmed;
|
return trimmed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const formatSystemEventTimestamp = (ts: number) =>
|
||||||
|
new Date(ts).toLocaleString("en-US", {
|
||||||
|
hour12: false,
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
});
|
||||||
|
|
||||||
const systemLines: string[] = [];
|
const systemLines: string[] = [];
|
||||||
const queued = drainSystemEvents(params.sessionKey);
|
const queued = drainSystemEventEntries(params.sessionKey);
|
||||||
systemLines.push(
|
systemLines.push(
|
||||||
...queued.map(compactSystemEvent).filter((v): v is string => Boolean(v)),
|
...queued
|
||||||
|
.map((event) => {
|
||||||
|
const compacted = compactSystemEvent(event.text);
|
||||||
|
if (!compacted) return null;
|
||||||
|
return `[${formatSystemEventTimestamp(event.ts)}] ${compacted}`;
|
||||||
|
})
|
||||||
|
.filter((v): v is string => Boolean(v)),
|
||||||
);
|
);
|
||||||
if (params.isMainSession && params.isNewSession) {
|
if (params.isMainSession && params.isNewSession) {
|
||||||
const summary = await buildProviderSummary(params.cfg);
|
const summary = await buildProviderSummary(params.cfg);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// prefixed to the next prompt. We intentionally avoid persistence to keep
|
// prefixed to the next prompt. We intentionally avoid persistence to keep
|
||||||
// events ephemeral. Events are session-scoped and require an explicit key.
|
// events ephemeral. Events are session-scoped and require an explicit key.
|
||||||
|
|
||||||
type SystemEvent = { text: string; ts: number };
|
export type SystemEvent = { text: string; ts: number };
|
||||||
|
|
||||||
const MAX_EVENTS = 20;
|
const MAX_EVENTS = 20;
|
||||||
|
|
||||||
@@ -66,11 +66,11 @@ export function enqueueSystemEvent(text: string, options: SystemEventOptions) {
|
|||||||
if (entry.queue.length > MAX_EVENTS) entry.queue.shift();
|
if (entry.queue.length > MAX_EVENTS) entry.queue.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function drainSystemEvents(sessionKey: string): string[] {
|
export function drainSystemEventEntries(sessionKey: string): SystemEvent[] {
|
||||||
const key = requireSessionKey(sessionKey);
|
const key = requireSessionKey(sessionKey);
|
||||||
const entry = queues.get(key);
|
const entry = queues.get(key);
|
||||||
if (!entry || entry.queue.length === 0) return [];
|
if (!entry || entry.queue.length === 0) return [];
|
||||||
const out = entry.queue.map((e) => e.text);
|
const out = entry.queue.slice();
|
||||||
entry.queue.length = 0;
|
entry.queue.length = 0;
|
||||||
entry.lastText = null;
|
entry.lastText = null;
|
||||||
entry.lastContextKey = null;
|
entry.lastContextKey = null;
|
||||||
@@ -78,6 +78,10 @@ export function drainSystemEvents(sessionKey: string): string[] {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function drainSystemEvents(sessionKey: string): string[] {
|
||||||
|
return drainSystemEventEntries(sessionKey).map((event) => event.text);
|
||||||
|
}
|
||||||
|
|
||||||
export function peekSystemEvents(sessionKey: string): string[] {
|
export function peekSystemEvents(sessionKey: string): string[] {
|
||||||
const key = requireSessionKey(sessionKey);
|
const key = requireSessionKey(sessionKey);
|
||||||
return queues.get(key)?.queue.map((e) => e.text) ?? [];
|
return queues.get(key)?.queue.map((e) => e.text) ?? [];
|
||||||
|
|||||||
Reference in New Issue
Block a user