feat: add scoped history and artifacts
Generic Container CI/CD / test-build-publish (push) Failing after 8m25s
Agent CI/CD / build-test-publish-and-deploy (push) Failing after 8m25s

This commit is contained in:
2026-10-22 18:40:20 +08:00
parent bc4e9834e5
commit 7d47a2df86
48 changed files with 2632 additions and 78 deletions
+10 -3
View File
@@ -1,5 +1,6 @@
import { join } from "node:path";
import { SessionTopicStore } from "./topic-store.js";
import { config } from "../config.js";
import { type SessionWorkspaceStore } from "./workspace-store.js";
import {
@@ -30,6 +31,7 @@ export type SessionRecord = {
requestId?: string;
modelId?: string;
title?: string;
topicId?: string | null;
triggerId?: string;
triggerSource?: string;
triggerSubjectType?: string;
@@ -63,14 +65,17 @@ type EnsureSessionMetadataInput = SessionMetadataContext & {
runId?: string;
requestId?: string;
modelId?: string;
topicId?: string;
};
export class SessionMetadataStore {
readonly topics: SessionTopicStore;
private readonly ensureQueues = new Map<string, Promise<void>>();
private index?: Map<string, Map<string, SessionRecord>>;
private indexLoading?: Promise<void>;
private indexVersion = 0;
constructor(private readonly baseDir = config.SESSION_METADATA_STORAGE_DIR, private readonly workspaces?: SessionWorkspaceStore) {
this.topics = new SessionTopicStore(join(baseDir, "topics"));
workspaces?.setDeletionCheck(async (context) => Boolean((await this.get(context, context.sessionId, { includeDeleted: true }))?.deletedAt));
}
@@ -133,6 +138,7 @@ export class SessionMetadataStore {
runId: input.runId?.trim(),
requestId: input.requestId?.trim(),
modelId: input.modelId?.trim(),
topicId: input.topicId,
...(input.trigger ? {
triggerId: input.trigger.triggerId,
triggerSource: input.trigger.source,
@@ -175,7 +181,7 @@ export class SessionMetadataStore {
async touch(
record: SessionRecord,
updates: Partial<Pick<SessionRecord, "title" | "status">> = {},
updates: Partial<Pick<SessionRecord, "title" | "status" | "topicId">> = {},
) {
return this.serialize(record.sessionId, async () => {
const current = await this.get({ ...record, userId: record.ownerUserId }, record.sessionId);
@@ -314,12 +320,13 @@ const matchesContext = (record: SessionRecord, context: SessionMetadataContext)
(context.projectId === undefined || record.projectId === context.projectId);
const normalizeSessionUpdates = (
updates: Partial<Pick<SessionRecord, "title" | "status">>,
updates: Partial<Pick<SessionRecord, "title" | "status" | "topicId">>,
) => {
const normalized: Partial<Pick<SessionRecord, "title" | "status">> = {};
const normalized: Partial<Pick<SessionRecord, "title" | "status" | "topicId">> = {};
if (updates.status === "active" || updates.status === "archived") {
normalized.status = updates.status;
}
if (updates.topicId === null || typeof updates.topicId === "string") normalized.topicId = updates.topicId;
if (typeof updates.title === "string") {
const trimmed = updates.title.trim();
if (trimmed) {