吸收数字员工长期记忆闭环
This commit is contained in:
@@ -22,6 +22,7 @@ import type {
|
||||
FlowResponse,
|
||||
GovernanceCommandRequest,
|
||||
GovernanceCommandResponse,
|
||||
MemoryEntry,
|
||||
PlanDispatchResponse,
|
||||
PlanStepSummary,
|
||||
SkillSpec,
|
||||
@@ -37,6 +38,9 @@ declare global {
|
||||
getUiState?: () => Promise<AdapterState>;
|
||||
getSkillCapabilities?: () => Promise<QimingclawSkillCapabilities>;
|
||||
getPlanTemplates?: () => Promise<QimingclawPlanTemplateList>;
|
||||
listMemories?: (options?: { query?: string; category?: string; limit?: number }) => Promise<QimingclawMemoryRecord[]>;
|
||||
addMemory?: (input: QimingclawMemoryUpsertInput) => Promise<QimingclawMemoryRecord | null>;
|
||||
deleteMemory?: (keyOrId: string) => Promise<QimingclawMemoryRecord | null>;
|
||||
saveUiState?: (update: { state: AdapterState; action?: string; date?: string; metadata?: Record<string, unknown> }) => Promise<AdapterState>;
|
||||
getSyncStatus?: () => Promise<QimingclawSyncStatus>;
|
||||
flushSync?: () => Promise<QimingclawSyncStatus>;
|
||||
@@ -196,6 +200,40 @@ interface QimingclawRuntimeRecords {
|
||||
events: QimingclawEventRecord[];
|
||||
artifacts?: QimingclawArtifactRecord[];
|
||||
approvals?: QimingclawApprovalRecord[];
|
||||
memories?: QimingclawMemoryRecord[];
|
||||
}
|
||||
|
||||
interface QimingclawMemoryRecord {
|
||||
id: string;
|
||||
remoteId?: string | null;
|
||||
key: string;
|
||||
content: string;
|
||||
category: string;
|
||||
source: string;
|
||||
sourcePath?: string | null;
|
||||
sessionId?: string | null;
|
||||
score?: number | null;
|
||||
status: string;
|
||||
payload: unknown;
|
||||
syncStatus: string;
|
||||
lastSyncedAt?: string | null;
|
||||
syncError?: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt?: string | null;
|
||||
}
|
||||
|
||||
interface QimingclawMemoryUpsertInput {
|
||||
id?: string;
|
||||
key?: string;
|
||||
content: string;
|
||||
category?: string;
|
||||
source?: string;
|
||||
sourcePath?: string | null;
|
||||
sessionId?: string | null;
|
||||
score?: number | null;
|
||||
status?: string;
|
||||
payload?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface QimingclawCronSettings {
|
||||
@@ -567,6 +605,17 @@ export async function handleQimingclawDigitalApi<T>(
|
||||
if (method === 'GET' && pathname === '/api/artifact') {
|
||||
return { artifacts: buildArtifacts(await readQimingclawSnapshot()) } as T;
|
||||
}
|
||||
if (method === 'GET' && pathname === '/api/memory') {
|
||||
return { entries: await readMemoryEntries(url.searchParams.get('query'), url.searchParams.get('category')) } as T;
|
||||
}
|
||||
if (method === 'POST' && pathname === '/api/memory') {
|
||||
return await handleMemoryCreate(parseJsonBody<Record<string, unknown>>(options.body)) as T;
|
||||
}
|
||||
const memoryDeleteMatch = pathname.match(/^\/api\/memory\/([^/]+)$/);
|
||||
if (method === 'DELETE' && memoryDeleteMatch) {
|
||||
await handleMemoryDelete(decodeURIComponent(memoryDeleteMatch[1] || ''));
|
||||
return undefined as T;
|
||||
}
|
||||
if (method === 'GET' && pathname.startsWith('/api/debug/plan/') && pathname.endsWith('/flow')) {
|
||||
return buildPlanFlow(decodeURIComponent(pathname.split('/')[4] || ''), await readQimingclawSnapshot()) as T;
|
||||
}
|
||||
@@ -743,6 +792,57 @@ async function saveCronSettings(patch: Partial<QimingclawCronSettings>): Promise
|
||||
}
|
||||
}
|
||||
|
||||
async function readMemoryEntries(query?: string | null, category?: string | null): Promise<MemoryEntry[]> {
|
||||
try {
|
||||
const records = await window.QimingClawBridge?.digital?.listMemories?.({
|
||||
query: query || undefined,
|
||||
category: category || undefined,
|
||||
limit: 300,
|
||||
}) ?? [];
|
||||
return records.map(memoryEntryFromRecord);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function handleMemoryCreate(body: Record<string, unknown>): Promise<{ ok: true; entry: MemoryEntry | null }> {
|
||||
const key = stringFromUnknown(body.key);
|
||||
const content = stringFromUnknown(body.content ?? body.text);
|
||||
const category = stringFromUnknown(body.category) || 'fact';
|
||||
if (!content) return { ok: true, entry: null };
|
||||
const record = await window.QimingClawBridge?.digital?.addMemory?.({
|
||||
key: key || undefined,
|
||||
content,
|
||||
category,
|
||||
source: 'qimingclaw-digital-page',
|
||||
payload: {
|
||||
source: 'embedded-memory-api',
|
||||
key: key || null,
|
||||
},
|
||||
}) ?? null;
|
||||
return { ok: true, entry: record ? memoryEntryFromRecord(record) : null };
|
||||
}
|
||||
|
||||
async function handleMemoryDelete(keyOrId: string): Promise<void> {
|
||||
try {
|
||||
await window.QimingClawBridge?.digital?.deleteMemory?.(keyOrId);
|
||||
} catch {
|
||||
// Keep the DELETE endpoint best-effort so the UI can refresh into the true state.
|
||||
}
|
||||
}
|
||||
|
||||
function memoryEntryFromRecord(record: QimingclawMemoryRecord): MemoryEntry {
|
||||
return {
|
||||
id: record.id,
|
||||
key: record.key || record.id,
|
||||
content: record.content,
|
||||
category: record.category || 'fact',
|
||||
timestamp: record.updatedAt || record.createdAt || new Date().toISOString(),
|
||||
session_id: record.sessionId ?? null,
|
||||
score: typeof record.score === 'number' ? record.score : null,
|
||||
};
|
||||
}
|
||||
|
||||
function defaultCronSettings(): QimingclawCronSettings {
|
||||
return {
|
||||
enabled: true,
|
||||
|
||||
Reference in New Issue
Block a user