fix(tasks): restore session key registry compatibility

This commit is contained in:
Peter Steinberger
2026-03-31 19:48:51 +01:00
parent 2d2c271cdf
commit 5fdde9b237
9 changed files with 102 additions and 14 deletions

View File

@@ -1,5 +1,5 @@
import { resolveAgentModelPrimaryValue } from "openclaw/plugin-sdk/provider-onboard";
import { SYNTHETIC_DEFAULT_MODEL_ID } from "openclaw/plugin-sdk/synthetic";
import { SYNTHETIC_DEFAULT_MODEL_REF as SYNTHETIC_DEFAULT_MODEL_REF_PUBLIC } from "openclaw/plugin-sdk/synthetic";
import { describe, expect, it } from "vitest";
import { createLegacyProviderConfig } from "../../test/helpers/plugins/onboard-config.js";
import {
@@ -16,7 +16,7 @@ describe("synthetic onboard", () => {
api: "anthropic-messages",
});
expect(resolveAgentModelPrimaryValue(cfg.agents?.defaults?.model)).toBe(
SYNTHETIC_DEFAULT_MODEL_REF,
SYNTHETIC_DEFAULT_MODEL_REF_PUBLIC,
);
});
@@ -32,6 +32,6 @@ describe("synthetic onboard", () => {
expect(cfg.models?.providers?.synthetic?.apiKey).toBe("old-key");
const ids = cfg.models?.providers?.synthetic?.models.map((m) => m.id);
expect(ids).toContain("old-model");
expect(ids).toContain(SYNTHETIC_DEFAULT_MODEL_ID);
expect(ids).toContain(SYNTHETIC_DEFAULT_MODEL_REF.replace(/^synthetic\//, ""));
});
});

View File

@@ -14,8 +14,9 @@ function createTask(partial: Partial<TaskRecord>): TaskRecord {
return {
taskId: partial.taskId ?? "task-1",
runtime: partial.runtime ?? "acp",
ownerKey: partial.ownerKey ?? "agent:main:main",
scopeKind: "session",
requesterSessionKey: partial.requesterSessionKey ?? partial.ownerKey ?? "agent:main:main",
ownerKey: partial.ownerKey ?? partial.requesterSessionKey ?? "agent:main:main",
scopeKind: partial.scopeKind ?? "session",
task: partial.task ?? "Investigate issue",
status: partial.status ?? "running",
deliveryStatus: partial.deliveryStatus ?? "pending",

View File

@@ -14,6 +14,7 @@ import type {
TaskNotifyPolicy,
TaskRecord,
TaskRuntime,
TaskScopeKind,
TaskStatus,
TaskTerminalOutcome,
} from "./task-registry.types.js";
@@ -21,7 +22,9 @@ import type {
export function createQueuedTaskRun(params: {
runtime: TaskRuntime;
sourceId?: string;
requesterSessionKey: string;
requesterSessionKey?: string;
ownerKey?: string;
scopeKind?: TaskScopeKind;
requesterOrigin?: TaskDeliveryState["requesterOrigin"];
childSessionKey?: string;
parentTaskId?: string;
@@ -42,7 +45,9 @@ export function createQueuedTaskRun(params: {
export function createRunningTaskRun(params: {
runtime: TaskRuntime;
sourceId?: string;
requesterSessionKey: string;
requesterSessionKey?: string;
ownerKey?: string;
scopeKind?: TaskScopeKind;
requesterOrigin?: TaskDeliveryState["requesterOrigin"];
childSessionKey?: string;
parentTaskId?: string;

View File

@@ -6,8 +6,9 @@ function createTask(partial: Partial<TaskRecord>): TaskRecord {
return {
taskId: partial.taskId ?? "task-1",
runtime: partial.runtime ?? "acp",
ownerKey: partial.ownerKey ?? "agent:main:main",
scopeKind: "session",
requesterSessionKey: partial.requesterSessionKey ?? partial.ownerKey ?? "agent:main:main",
ownerKey: partial.ownerKey ?? partial.requesterSessionKey ?? "agent:main:main",
scopeKind: partial.scopeKind ?? "session",
task: partial.task ?? "Background task",
status: partial.status ?? "queued",
deliveryStatus: partial.deliveryStatus ?? "pending",

View File

@@ -95,6 +95,7 @@ function rowToTaskRecord(row: TaskRegistryRow): TaskRecord {
taskId: row.task_id,
runtime: row.runtime,
...(row.source_id ? { sourceId: row.source_id } : {}),
requesterSessionKey: row.scope_kind === "system" ? "" : row.owner_key,
ownerKey: row.owner_key,
scopeKind: row.scope_kind,
...(row.child_session_key ? { childSessionKey: row.child_session_key } : {}),

View File

@@ -19,6 +19,7 @@ function createStoredTask(): TaskRecord {
taskId: "task-restored",
runtime: "acp",
sourceId: "run-restored",
requesterSessionKey: "agent:main:main",
ownerKey: "agent:main:main",
scopeKind: "session",
childSessionKey: "agent:codex:acp:restored",

View File

@@ -1051,6 +1051,8 @@ describe("task-registry", () => {
taskId: "task-missing-cleanup",
runtime: "cron",
requesterSessionKey: "",
ownerKey: "system:cron:task-missing-cleanup",
scopeKind: "system",
runId: "run-maintenance-cleanup",
task: "Finished cron",
status: "failed",
@@ -1098,6 +1100,8 @@ describe("task-registry", () => {
taskId: "task-audit-summary",
runtime: "acp",
requesterSessionKey: "agent:main:main",
ownerKey: "agent:main:main",
scopeKind: "session",
runId: "run-audit-summary",
task: "Hung task",
status: "running",

View File

@@ -35,6 +35,7 @@ import type {
TaskRegistrySummary,
TaskRegistrySnapshot,
TaskRuntime,
TaskScopeKind,
TaskStatus,
TaskTerminalOutcome,
} from "./task-registry.types.js";
@@ -97,6 +98,14 @@ function persistTaskRegistry() {
function persistTaskUpsert(task: TaskRecord) {
const store = getTaskRegistryStore();
const deliveryState = taskDeliveryStates.get(task.taskId);
if (store.upsertTaskWithDeliveryState) {
store.upsertTaskWithDeliveryState({
task,
...(deliveryState ? { deliveryState } : {}),
});
return;
}
if (store.upsertTask) {
store.upsertTask(task);
return;
@@ -109,6 +118,10 @@ function persistTaskUpsert(task: TaskRecord) {
function persistTaskDelete(taskId: string) {
const store = getTaskRegistryStore();
if (store.deleteTaskWithDeliveryState) {
store.deleteTaskWithDeliveryState(taskId);
return;
}
if (store.deleteTask) {
store.deleteTask(taskId);
return;
@@ -159,6 +172,35 @@ function ensureNotifyPolicy(params: {
return deliveryStatus === "not_applicable" ? "silent" : "done_only";
}
function resolveTaskScopeKind(params: {
scopeKind?: TaskScopeKind;
requesterSessionKey: string;
}): TaskScopeKind {
if (params.scopeKind) {
return params.scopeKind;
}
return params.requesterSessionKey.trim() ? "session" : "system";
}
function resolveTaskRequesterSessionKey(params: {
requesterSessionKey?: string;
ownerKey?: string;
scopeKind?: TaskScopeKind;
}): string {
const requesterSessionKey = params.requesterSessionKey?.trim();
if (requesterSessionKey) {
return requesterSessionKey;
}
if (params.scopeKind === "system") {
return "";
}
return params.ownerKey?.trim() ?? "";
}
function resolveTaskOwnerKey(params: { requesterSessionKey: string; ownerKey?: string }): string {
return params.ownerKey?.trim() || params.requesterSessionKey.trim();
}
function normalizeTaskSummary(value: string | null | undefined): string | undefined {
const normalized = value?.replace(/\s+/g, " ").trim();
return normalized || undefined;
@@ -989,7 +1031,9 @@ function ensureListener() {
export function createTaskRecord(params: {
runtime: TaskRuntime;
sourceId?: string;
requesterSessionKey: string;
requesterSessionKey?: string;
ownerKey?: string;
scopeKind?: TaskScopeKind;
requesterOrigin?: TaskDeliveryState["requesterOrigin"];
childSessionKey?: string;
parentTaskId?: string;
@@ -1009,25 +1053,39 @@ export function createTaskRecord(params: {
terminalOutcome?: TaskTerminalOutcome | null;
}): TaskRecord {
ensureTaskRegistryReady();
const existing = findExistingTaskForCreate(params);
const requesterSessionKey = resolveTaskRequesterSessionKey(params);
const scopeKind = resolveTaskScopeKind({
scopeKind: params.scopeKind,
requesterSessionKey,
});
const ownerKey = resolveTaskOwnerKey({
requesterSessionKey,
ownerKey: params.ownerKey,
});
const existing = findExistingTaskForCreate({
...params,
requesterSessionKey,
});
if (existing) {
return mergeExistingTaskForCreate(existing, params);
}
const now = Date.now();
const taskId = crypto.randomUUID();
const status = normalizeTaskStatus(params.status);
const deliveryStatus = params.deliveryStatus ?? ensureDeliveryStatus(params.requesterSessionKey);
const deliveryStatus = params.deliveryStatus ?? ensureDeliveryStatus(requesterSessionKey);
const notifyPolicy = ensureNotifyPolicy({
notifyPolicy: params.notifyPolicy,
deliveryStatus,
requesterSessionKey: params.requesterSessionKey,
requesterSessionKey,
});
const lastEventAt = params.lastEventAt ?? params.startedAt ?? now;
const record: TaskRecord = {
taskId,
runtime: params.runtime,
sourceId: params.sourceId?.trim() || undefined,
requesterSessionKey: params.requesterSessionKey,
requesterSessionKey,
ownerKey,
scopeKind,
childSessionKey: params.childSessionKey,
parentTaskId: params.parentTaskId?.trim() || undefined,
agentId: params.agentId?.trim() || undefined,
@@ -1376,6 +1434,14 @@ export function findLatestTaskForSessionKey(sessionKey: string): TaskRecord | un
return task ? cloneTaskRecord(task) : undefined;
}
export function findLatestTaskForOwnerKey(ownerKey: string): TaskRecord | undefined {
return findLatestTaskForSessionKey(ownerKey);
}
export function findLatestTaskForRelatedSessionKey(sessionKey: string): TaskRecord | undefined {
return findLatestTaskForSessionKey(sessionKey);
}
export function listTasksForSessionKey(sessionKey: string): TaskRecord[] {
ensureTaskRegistryReady();
const key = normalizeSessionIndexKey(sessionKey);
@@ -1402,6 +1468,14 @@ export function listTasksForSessionKey(sessionKey: string): TaskRecord[] {
.map(({ insertionIndex: _, ...task }) => task);
}
export function listTasksForOwnerKey(ownerKey: string): TaskRecord[] {
return listTasksForSessionKey(ownerKey);
}
export function listTasksForRelatedSessionKey(sessionKey: string): TaskRecord[] {
return listTasksForSessionKey(sessionKey);
}
export function resolveTaskForLookupToken(token: string): TaskRecord | undefined {
const lookup = token.trim();
if (!lookup) {

View File

@@ -54,6 +54,7 @@ export type TaskRecord = {
taskId: string;
runtime: TaskRuntime;
sourceId?: string;
requesterSessionKey: string;
ownerKey: string;
scopeKind: TaskScopeKind;
childSessionKey?: string;