@xopcai/xopc 0.0.307 → 0.0.308
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CZeQo09T.js → agent-avatar-dicebear-dUotwUO6.js} +1 -1
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-C6LflMXr.js +1 -0
- package/dist/gateway/static/root/assets/{agents-BlrHMbRx.js → agents-VPDIGRSS.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-Bzw4fQP8.js → app-management-settings-panel-zPz0rOND.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-8GZpUXva.js → appearance-settings-DhvhR43G.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-BkQQtIxH.js → apps-page-Pmslbzzr.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-CQ2NaoHb.js → archive-plugin-BQVZMreT.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-BFi1H7yJ.js → attachment-load-CPE-TMPf.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-CDBXcii_.js → automations-page-C4IAy_zF.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-Fx5VUJRu.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-D07eWmOx.js → binary-plugins-DgTl7diZ.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-CtVnjyYU.js → block-editor-CcOVA-Gw.js} +3 -3
- package/dist/gateway/static/root/assets/{browser-automations-page-DyrSbDdG.js → browser-automations-page--zKAgj1k.js} +1 -1
- package/dist/gateway/static/root/assets/browser-settings-page-DBms6d35.js +3 -0
- package/dist/gateway/static/root/assets/capabilities-settings-panel-epdDWIZy.js +8 -0
- package/dist/gateway/static/root/assets/{channels-settings-DAD9qE3J.js → channels-settings-B0bsDtmP.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-B62SZdAo.js → chat-terminal-dock-BfLd2rIJ.js} +1 -1
- package/dist/gateway/static/root/assets/{computer-settings-page-ClSresNq.js → computer-settings-page-Ckgc0Py-.js} +1 -1
- package/dist/gateway/static/root/assets/connector-service-page-BlC1OXX4.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-By33-bF1.js +2 -0
- package/dist/gateway/static/root/assets/{cron-expression-CTMi_tql.js → cron-expression-CqNE-nf6.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-B71AJK50.js → date-picker-BS9ZjzYJ.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-DlyGK-kA.js → dependency-picker-CF_zYzxX.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-DGkbTE8_.js → desktop-pet-CDwtLR7Z.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-BY8r9j66.js → desktop-pet-settings-C0djIc1J.js} +1 -1
- package/dist/gateway/static/root/assets/{device-pairing-wizard-DzRrgd59.js → device-pairing-wizard-CDKj9LQj.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-L8Mm1n3A.js → extension-debug-page-CX5OXxEU.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CFc4vSNU.js → extension-page-BytR1PaY.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-B5zWyHr3.js → extension-settings-page-CkaMJA5F.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-D7qmTH4R.js → gateway-settings-_YU-1p0z.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-Glsl3X5b.js → heartbeat-settings-CVIvvm4x.js} +1 -1
- package/dist/gateway/static/root/assets/home-page-COCHelcL.js +1 -0
- package/dist/gateway/static/root/assets/{imports-page-ByBX7ome.js → imports-page-DrtbjL8O.js} +1 -1
- package/dist/gateway/static/root/assets/index-CDfcPiPb.js +79 -0
- package/dist/gateway/static/root/assets/index-CRu1p38D.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-C45RlO8a.js → keyboard-shortcuts-settings-BlZh41Xq.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-DP0x2jpR.js → local-app-workbench-page-D7_Raxjw.js} +2 -2
- package/dist/gateway/static/root/assets/{local-apps-page-C_AQc2bI.js → local-apps-page-DbP7wWuh.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-DvM8Pyok.js → logs-page-C0n66FiO.js} +1 -1
- package/dist/gateway/static/root/assets/{management-settings-D1uwSutV.js → management-settings-i6GhxpGV.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-C4mhf13f.js → media-plugins-CnWChQ3Y.js} +1 -1
- package/dist/gateway/static/root/assets/note-detail-panel-C59d8vDW.js +5 -0
- package/dist/gateway/static/root/assets/notes-page-C1GBPrVI.js +8 -0
- package/dist/gateway/static/root/assets/{page-tabs-1go_BtX1.js → page-tabs-BWitjb3e.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-DmoSC0j1.js → preview-open-alternatives-UaCWCxZv.js} +1 -1
- package/dist/gateway/static/root/assets/{proactive-page-xAjKTIID.js → proactive-page-CtSFJxoM.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-ILTmQ1YN.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-snl4rOQZ.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-BbaAcEN3.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-ClkOoxDl.js → runtime-tools-settings-panel-CtOjKGWg.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-BuEmLiaU.js → schema-form-DkyyDcl2.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DrWL71O8.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BG0kl4HT.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-Dor39QON.js → settings-form-section-C0JDGN_V.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-Xj-yvIc8.js → settings-page-DiIyMQ52.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-EPazhoZO.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-BFWZoXdm.js → share-preview-page-Bt1K657Y.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-g2MdRXSZ.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-B1DPzu98.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-Wd0uYSPG.js +2 -0
- package/dist/gateway/static/root/assets/{skills-page.utils-Ii8qeXhs.js → skills-page.utils-vq5Ujm03.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-Di6OSO-0.js → system-settings-panel-Cz1P-HDH.js} +1 -1
- package/dist/gateway/static/root/assets/task-detail-page-BG3A5gPZ.js +1 -0
- package/dist/gateway/static/root/assets/{tunnel-api-DDOlOYlj.js → tunnel-api-DQGoFuZO.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-CQ8uWNnB.js → use-autosave-DVDlBb95.js} +1 -1
- package/dist/gateway/static/root/assets/user-model-page-qM5UiTGx.js +2 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-COTzshjL.js → voice-api-key-field-B2pZCcxY.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-_URrXbi4.js → work-discovery-overlay-Bptv7bEY.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-CALs-1nd.js → workflow-run-setup-panel-BijCrNc4.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-DRy1RAYH.js +6 -0
- package/dist/gateway/static/root/index.html +2 -2
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/notifications.js +1 -1
- package/dist/src/agent/background-review/prompts.js +3 -1
- package/dist/src/agent/context/execution-context.js +4 -4
- package/dist/src/agent/memory/compaction-promotion.d.ts +1 -1
- package/dist/src/agent/memory/compaction-promotion.js +4 -3
- package/dist/src/agent/tools/knowledge-memory-tool.d.ts +1 -1
- package/dist/src/agent/tools/knowledge-memory-tool.js +3 -3
- package/dist/src/agent/tools/user-context-tool.js +17 -7
- package/dist/src/chat-commands/builtins/understanding.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/config/schema.d.ts +2 -4
- package/dist/src/config/schema.js +2 -2
- package/dist/src/connectors/connected-source-understanding.d.ts +1 -0
- package/dist/src/connectors/connected-source-understanding.js +1 -0
- package/dist/src/connectors/learning-coordinator.js +31 -9
- package/dist/src/gateway/hono/lib/config-payload.d.ts +2 -2
- package/dist/src/gateway/hono/routes/connectors.js +1 -1
- package/dist/src/gateway/hono/routes/context-sources.js +0 -23
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/hono/routes/projects.js +8 -0
- package/dist/src/gateway/hono/routes/user-model-refresh.d.ts +5 -0
- package/dist/src/gateway/hono/routes/user-model-refresh.js +128 -0
- package/dist/src/gateway/hono/routes/user-model.js +12 -5
- package/dist/src/gateway/hono/routes/work-discovery.js +0 -30
- package/dist/src/gateway/service.js +6 -6
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/knowledge/connected-knowledge-pipeline.js +1 -1
- package/dist/src/knowledge-memory/index.js +2 -2
- package/dist/src/knowledge-memory/repository.d.ts +7 -0
- package/dist/src/knowledge-memory/repository.js +58 -8
- package/dist/src/memory-maintenance/service.js +61 -31
- package/dist/src/migrations/memory-config.d.ts +2 -0
- package/dist/src/migrations/memory-config.js +77 -0
- package/dist/src/migrations/runner.js +2 -2
- package/dist/src/notifications/planner.js +5 -5
- package/dist/src/notifications/service.d.ts +2 -2
- package/dist/src/notifications/service.js +1 -1
- package/dist/src/storage/sqlite/migrations/184_memory_suppressions.sql +12 -0
- package/dist/src/storage/sqlite/migrations/185_understanding_refresh_batches.sql +6 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/user-context/config.d.ts +4 -9
- package/dist/src/user-context/config.js +7 -27
- package/dist/src/user-context/memory-suppression.d.ts +7 -0
- package/dist/src/user-context/memory-suppression.js +23 -0
- package/dist/src/user-context/relationship-continuity.js +1 -1
- package/dist/src/user-context/sources/refresh-service.d.ts +48 -0
- package/dist/src/user-context/sources/refresh-service.js +221 -0
- package/dist/src/user-context/sources/repository.js +1 -1
- package/dist/src/user-model/bootstrap.js +27 -24
- package/dist/src/user-model/capture/executor.d.ts +1 -1
- package/dist/src/user-model/capture/executor.js +17 -11
- package/dist/src/user-model/capture/semantic.js +1 -1
- package/dist/src/user-model/domain.d.ts +6 -2
- package/dist/src/user-model/index.js +3 -3
- package/dist/src/user-model/profile.js +1 -1
- package/dist/src/user-model/repository.d.ts +4 -1
- package/dist/src/user-model/repository.js +96 -6
- package/dist/src/user-model/usage-policy.d.ts +2 -0
- package/dist/src/user-model/usage-policy.js +6 -2
- package/dist/src/work-discovery/project-understanding.js +2 -1
- package/dist/src/work-discovery/service.d.ts +8 -24
- package/dist/src/work-discovery/service.js +138 -180
- package/package.json +3 -3
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-BPsRb9Ck.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace-CUpiTTiY.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-CqTxLpil.js +0 -3
- package/dist/gateway/static/root/assets/capabilities-settings-panel-BZ_-pnS6.js +0 -8
- package/dist/gateway/static/root/assets/connector-service-page-CPqVmNvx.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-BUQyg3CW.js +0 -2
- package/dist/gateway/static/root/assets/home-page-s9hDQ8Hc.js +0 -1
- package/dist/gateway/static/root/assets/index-RBCkv8a_.js +0 -79
- package/dist/gateway/static/root/assets/index-cFU4S35z.css +0 -1
- package/dist/gateway/static/root/assets/note-detail-panel-D35sErp2.js +0 -5
- package/dist/gateway/static/root/assets/notes-page-8jCDklDn.js +0 -8
- package/dist/gateway/static/root/assets/project-detail-page-BUMQHy_N.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-DT-Mt7XR.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-Clz3byir.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-tQRwktKm.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DqWg9XS5.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-BB_sMB7P.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-oDALm4b6.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DmwelnfK.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-BE9JXz4U.js +0 -2
- package/dist/gateway/static/root/assets/task-detail-page-D1AWszc7.js +0 -1
- package/dist/gateway/static/root/assets/user-model-page-C00i4ea0.js +0 -2
- package/dist/gateway/static/root/assets/workflows-page-BUiBjz6m.js +0 -6
- package/dist/src/migrations/knowledge-memory-read-policy-config.d.ts +0 -2
- package/dist/src/migrations/knowledge-memory-read-policy-config.js +0 -48
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { KnowledgeReviewConflictError, classifyKnowledgeContentSource, getKnowledgeItem, knowledgeItemAllowed, listKnowledgeItems, listKnowledgeStatusEvents, reviewKnowledgeItem, searchKnowledgeItems, transitionKnowledgeStatus, writeKnowledgeItem } from "./repository.js";
|
|
2
|
-
export { KnowledgeReviewConflictError, classifyKnowledgeContentSource, getKnowledgeItem, knowledgeItemAllowed, listKnowledgeItems, listKnowledgeStatusEvents, reviewKnowledgeItem, searchKnowledgeItems, transitionKnowledgeStatus, writeKnowledgeItem };
|
|
1
|
+
import { KnowledgeReviewConflictError, classifyKnowledgeContentSource, deleteKnowledgeItem, getKnowledgeItem, isKnowledgeCurrent, knowledgeItemAllowed, listKnowledgeItems, listKnowledgeStatusEvents, reviewKnowledgeItem, searchKnowledgeItems, transitionKnowledgeStatus, writeKnowledgeItem } from "./repository.js";
|
|
2
|
+
export { KnowledgeReviewConflictError, classifyKnowledgeContentSource, deleteKnowledgeItem, getKnowledgeItem, isKnowledgeCurrent, knowledgeItemAllowed, listKnowledgeItems, listKnowledgeStatusEvents, reviewKnowledgeItem, searchKnowledgeItems, transitionKnowledgeStatus, writeKnowledgeItem };
|
|
@@ -21,6 +21,7 @@ export interface WriteKnowledgeInput {
|
|
|
21
21
|
derivedFromRecalledContext?: boolean;
|
|
22
22
|
source?: Record<string, unknown>;
|
|
23
23
|
replaceExisting?: boolean;
|
|
24
|
+
restoreDeleted?: boolean;
|
|
24
25
|
now?: number;
|
|
25
26
|
}
|
|
26
27
|
export declare class KnowledgeReviewConflictError extends Error {
|
|
@@ -29,6 +30,10 @@ export declare class KnowledgeReviewConflictError extends Error {
|
|
|
29
30
|
export declare function writeKnowledgeItem(input: WriteKnowledgeInput): {
|
|
30
31
|
item: KnowledgeItem;
|
|
31
32
|
created: boolean;
|
|
33
|
+
} | {
|
|
34
|
+
item?: undefined;
|
|
35
|
+
created: false;
|
|
36
|
+
suppressed: true;
|
|
32
37
|
};
|
|
33
38
|
export declare function getKnowledgeItem(id: string): KnowledgeItem | undefined;
|
|
34
39
|
export declare function listKnowledgeItems(input?: {
|
|
@@ -70,3 +75,5 @@ export declare function searchKnowledgeItems(input: {
|
|
|
70
75
|
}): KnowledgeItem[];
|
|
71
76
|
export declare function classifyKnowledgeContentSource(item: KnowledgeItem): KnowledgeContentSource;
|
|
72
77
|
export declare function knowledgeItemAllowed(item: KnowledgeItem, policy: KnowledgeReadPolicy): boolean;
|
|
78
|
+
export declare function deleteKnowledgeItem(id: string, now?: number): boolean;
|
|
79
|
+
export declare function isKnowledgeCurrent(item: KnowledgeItem, asOf?: number): boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getSqliteDatabase, init_transaction, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
|
|
2
2
|
import { validateScope } from "../user-model/domain.js";
|
|
3
3
|
import { buildFts5SearchQuery, init_fts } from "../storage/sqlite/fts.js";
|
|
4
|
+
import { clearMemoryAudit, isMemorySuppressed, memoryFingerprint, restoreMemory, suppressMemory } from "../user-context/memory-suppression.js";
|
|
4
5
|
import { randomUUID } from "node:crypto";
|
|
5
6
|
//#region src/knowledge-memory/repository.ts
|
|
6
7
|
init_fts();
|
|
@@ -60,7 +61,14 @@ function writeKnowledgeItem(input) {
|
|
|
60
61
|
if (input.validFrom !== void 0 && input.validTo !== void 0 && input.validTo < input.validFrom) throw new Error("Knowledge validTo must be greater than or equal to validFrom.");
|
|
61
62
|
const principalId = input.principalId ?? "local-owner";
|
|
62
63
|
const now = input.now ?? Date.now();
|
|
64
|
+
const admittedStatus = input.status ?? (input.originClass === "owner" || input.originClass !== "untrusted" && input.confidence >= .7 && !input.derivedFromRecalledContext ? "active" : "candidate");
|
|
63
65
|
return runSqliteWriteTransaction((db) => {
|
|
66
|
+
const keys = knowledgeFingerprints(input);
|
|
67
|
+
if (input.restoreDeleted && input.originClass === "owner") restoreMemory(db, keys);
|
|
68
|
+
if (isMemorySuppressed(db, keys)) return {
|
|
69
|
+
created: false,
|
|
70
|
+
suppressed: true
|
|
71
|
+
};
|
|
64
72
|
const existingStatuses = input.replaceExisting ? [
|
|
65
73
|
"candidate",
|
|
66
74
|
"active",
|
|
@@ -76,12 +84,12 @@ function writeKnowledgeItem(input) {
|
|
|
76
84
|
const existing = db.prepare(`SELECT * FROM knowledge_items WHERE principal_id = ?
|
|
77
85
|
AND canonical_key = ? AND scope_type = ? AND COALESCE(scope_id, '') = COALESCE(?, '')
|
|
78
86
|
AND status IN (${existingStatuses.map(() => "?").join(", ")})`).get(principalId, input.canonicalKey.trim(), input.scope.type, input.scope.id ?? null, ...existingStatuses);
|
|
79
|
-
if (existing && !input.replaceExisting) return {
|
|
87
|
+
if (existing && (!input.replaceExisting || existing.origin_class === "owner" && input.originClass !== "owner")) return {
|
|
80
88
|
item: fromRow(existing),
|
|
81
89
|
created: false
|
|
82
90
|
};
|
|
83
91
|
if (existing) {
|
|
84
|
-
const nextStatus =
|
|
92
|
+
const nextStatus = admittedStatus;
|
|
85
93
|
db.prepare(`UPDATE knowledge_items SET kind = ?, content = ?, record_class = ?, status = ?, confidence = ?,
|
|
86
94
|
importance = ?, valid_from = ?, valid_to = ?, expires_at = ?, review_at = ?,
|
|
87
95
|
origin_class = ?, source_agent_id = ?, source_conversation_id = ?, source_turn_id = ?,
|
|
@@ -102,7 +110,7 @@ function writeKnowledgeItem(input) {
|
|
|
102
110
|
};
|
|
103
111
|
}
|
|
104
112
|
const id = randomUUID();
|
|
105
|
-
const status =
|
|
113
|
+
const status = admittedStatus;
|
|
106
114
|
db.prepare(`INSERT INTO knowledge_items (
|
|
107
115
|
knowledge_id, principal_id, kind, scope_type, scope_id, content, canonical_key, record_class,
|
|
108
116
|
status, confidence, importance, valid_from, valid_to, expires_at, review_at,
|
|
@@ -154,12 +162,14 @@ function transitionKnowledgeStatus(input) {
|
|
|
154
162
|
const current = db.prepare("SELECT * FROM knowledge_items WHERE knowledge_id = ?").get(input.id);
|
|
155
163
|
if (!current) return void 0;
|
|
156
164
|
if (input.expectedStatus && current.status !== input.expectedStatus) throw new KnowledgeReviewConflictError(`Knowledge status changed from ${input.expectedStatus} to ${current.status}.`);
|
|
157
|
-
db.prepare(`UPDATE knowledge_items SET status = ?, content = COALESCE(?, content),
|
|
158
|
-
|
|
165
|
+
db.prepare(`UPDATE knowledge_items SET status = ?, content = COALESCE(?, content),
|
|
166
|
+
origin_class = CASE WHEN ? THEN 'owner' ELSE origin_class END, updated_at = ?
|
|
167
|
+
WHERE knowledge_id = ?`).run(input.status, content ?? null, input.actor === "user" && input.status === "active" ? 1 : 0, now, input.id);
|
|
159
168
|
if (content !== void 0) {
|
|
160
169
|
db.prepare("DELETE FROM knowledge_items_fts WHERE knowledge_id = ?").run(input.id);
|
|
161
170
|
db.prepare("INSERT INTO knowledge_items_fts(content, knowledge_id) VALUES (?, ?)").run(content, input.id);
|
|
162
171
|
}
|
|
172
|
+
if (input.actor === "user" && (input.status === "rejected" || input.status === "archived")) suppressMemory(db, knowledgeFingerprints(fromRow(current)), now);
|
|
163
173
|
insertStatusEvent(db, {
|
|
164
174
|
knowledgeId: input.id,
|
|
165
175
|
fromStatus: current.status,
|
|
@@ -184,7 +194,8 @@ const REVIEW_TRANSITIONS = {
|
|
|
184
194
|
from: [
|
|
185
195
|
"candidate",
|
|
186
196
|
"needs_review",
|
|
187
|
-
"stale"
|
|
197
|
+
"stale",
|
|
198
|
+
"active"
|
|
188
199
|
],
|
|
189
200
|
to: "active",
|
|
190
201
|
requiresContent: true
|
|
@@ -292,11 +303,12 @@ function searchKnowledgeItems(input) {
|
|
|
292
303
|
AND (k.valid_from IS NULL OR k.valid_from <= ?)
|
|
293
304
|
AND (k.valid_to IS NULL OR k.valid_to >= ?)
|
|
294
305
|
AND (k.expires_at IS NULL OR k.expires_at >= ?)
|
|
306
|
+
AND (k.review_at IS NULL OR k.review_at > ?)
|
|
295
307
|
${input.recordClass ? "AND k.record_class = ?" : ""}
|
|
296
308
|
${input.trustedOnly ? "AND k.origin_class != 'untrusted'" : ""}
|
|
297
309
|
AND ${contentSource}
|
|
298
310
|
AND ${visible.sql}
|
|
299
|
-
ORDER BY rank ASC, k.importance DESC, k.updated_at DESC LIMIT ?`).all(fts, principalId, asOf, asOf, asOf, ...input.recordClass ? [input.recordClass] : [], ...visible.values, limit).map(fromRow);
|
|
311
|
+
ORDER BY rank ASC, k.importance DESC, k.updated_at DESC LIMIT ?`).all(fts, principalId, asOf, asOf, asOf, asOf, ...input.recordClass ? [input.recordClass] : [], ...visible.values, limit).map(fromRow);
|
|
300
312
|
}
|
|
301
313
|
function classifyKnowledgeContentSource(item) {
|
|
302
314
|
if (item.recordClass === "memory") return "memory";
|
|
@@ -305,5 +317,43 @@ function classifyKnowledgeContentSource(item) {
|
|
|
305
317
|
function knowledgeItemAllowed(item, policy) {
|
|
306
318
|
return policy.scopes.includes(item.scope.type) && policy.contentSources.includes(classifyKnowledgeContentSource(item));
|
|
307
319
|
}
|
|
320
|
+
function knowledgeFingerprints(input) {
|
|
321
|
+
const principal = input.principalId ?? "local-owner";
|
|
322
|
+
return [memoryFingerprint([
|
|
323
|
+
"knowledge-key",
|
|
324
|
+
principal,
|
|
325
|
+
input.scope.type,
|
|
326
|
+
input.scope.id ?? "",
|
|
327
|
+
input.canonicalKey
|
|
328
|
+
]), memoryFingerprint([
|
|
329
|
+
"knowledge-content",
|
|
330
|
+
principal,
|
|
331
|
+
input.scope.type,
|
|
332
|
+
input.scope.id ?? "",
|
|
333
|
+
input.content
|
|
334
|
+
])];
|
|
335
|
+
}
|
|
336
|
+
function deleteKnowledgeItem(id, now = Date.now()) {
|
|
337
|
+
return runSqliteWriteTransaction((db) => {
|
|
338
|
+
const rows = db.prepare(`WITH RECURSIVE deleted(id) AS (
|
|
339
|
+
SELECT knowledge_id FROM knowledge_items WHERE knowledge_id = ?
|
|
340
|
+
UNION SELECT k.knowledge_id FROM knowledge_items k JOIN deleted d
|
|
341
|
+
ON json_extract(k.source_json, '$.episodeKnowledgeId') = d.id
|
|
342
|
+
OR k.knowledge_id = (SELECT json_extract(source_json, '$.episodeKnowledgeId')
|
|
343
|
+
FROM knowledge_items WHERE knowledge_id = d.id)
|
|
344
|
+
) SELECT * FROM knowledge_items WHERE knowledge_id IN (SELECT id FROM deleted)`).all(id);
|
|
345
|
+
for (const row of rows) {
|
|
346
|
+
const item = fromRow(row);
|
|
347
|
+
suppressMemory(db, knowledgeFingerprints(item), now);
|
|
348
|
+
clearMemoryAudit(db, "knowledge", item.id);
|
|
349
|
+
db.prepare("DELETE FROM knowledge_items_fts WHERE knowledge_id = ?").run(item.id);
|
|
350
|
+
db.prepare("DELETE FROM knowledge_items WHERE knowledge_id = ?").run(item.id);
|
|
351
|
+
}
|
|
352
|
+
return rows.length > 0;
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
function isKnowledgeCurrent(item, asOf = Date.now()) {
|
|
356
|
+
return item.status === "active" && (item.validFrom === void 0 || item.validFrom <= asOf) && (item.validTo === void 0 || item.validTo >= asOf) && (item.expiresAt === void 0 || item.expiresAt >= asOf) && (item.reviewAt === void 0 || item.reviewAt > asOf);
|
|
357
|
+
}
|
|
308
358
|
//#endregion
|
|
309
|
-
export { KnowledgeReviewConflictError, classifyKnowledgeContentSource, getKnowledgeItem, knowledgeItemAllowed, listKnowledgeItems, listKnowledgeStatusEvents, reviewKnowledgeItem, searchKnowledgeItems, transitionKnowledgeStatus, writeKnowledgeItem };
|
|
359
|
+
export { KnowledgeReviewConflictError, classifyKnowledgeContentSource, deleteKnowledgeItem, getKnowledgeItem, isKnowledgeCurrent, knowledgeItemAllowed, listKnowledgeItems, listKnowledgeStatusEvents, reviewKnowledgeItem, searchKnowledgeItems, transitionKnowledgeStatus, writeKnowledgeItem };
|
|
@@ -3,7 +3,7 @@ import "../user-model/domain.js";
|
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
//#region src/memory-maintenance/service.ts
|
|
5
5
|
init_transaction();
|
|
6
|
-
const ALGORITHM_VERSION = "user-model-maintenance-
|
|
6
|
+
const ALGORITHM_VERSION = "user-model-maintenance-v2";
|
|
7
7
|
function emptyMetrics() {
|
|
8
8
|
return {
|
|
9
9
|
scanned: 0,
|
|
@@ -32,29 +32,22 @@ function addDecision(db, runId, objectType, objectId, action, reason, before, af
|
|
|
32
32
|
decision_id, run_id, object_type, object_id, action, reason, before_json, after_json, created_at
|
|
33
33
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(randomUUID(), runId, objectType, objectId, action, reason, before === void 0 ? null : JSON.stringify(before), after === void 0 ? null : JSON.stringify(after), now);
|
|
34
34
|
}
|
|
35
|
-
function transitionAssertions(db, runId, now, limit, metrics) {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
function transitionAssertions(db, runId, principalId, now, limit, metrics) {
|
|
36
|
+
const rows = db.prepare(`SELECT a.assertion_id, a.status FROM user_assertions a
|
|
37
|
+
JOIN user_assertion_slots s ON s.slot_id = a.slot_id
|
|
38
|
+
WHERE s.principal_id = ? AND a.status IN ('active', 'candidate', 'needs_review')
|
|
39
|
+
AND ((a.valid_to IS NOT NULL AND a.valid_to < ?)
|
|
40
|
+
OR (a.review_at IS NOT NULL AND a.review_at <= ?))
|
|
41
|
+
ORDER BY COALESCE(a.valid_to, a.review_at), a.assertion_id LIMIT ?`).all(principalId, now, now, limit);
|
|
42
|
+
metrics.scanned += rows.length;
|
|
43
|
+
for (const row of rows) {
|
|
44
|
+
db.prepare("UPDATE user_assertions SET status = 'stale' WHERE assertion_id = ?").run(row.assertion_id);
|
|
41
45
|
db.prepare(`INSERT INTO user_assertion_status_events (
|
|
42
46
|
event_id, assertion_id, from_status, to_status, actor_type, reason, source_run_id, created_at
|
|
43
|
-
) VALUES (?, ?,
|
|
44
|
-
addDecision(db, runId, "assertion", row.assertion_id, "stale", "
|
|
47
|
+
) VALUES (?, ?, ?, 'stale', 'maintenance', 'No current supporting evidence at review time.', ?, ?)`).run(randomUUID(), row.assertion_id, row.status, runId, now);
|
|
48
|
+
addDecision(db, runId, "assertion", row.assertion_id, "stale", "validity_or_review_ended", row, { status: "stale" }, now);
|
|
45
49
|
metrics.stale += 1;
|
|
46
50
|
}
|
|
47
|
-
const reviewDue = db.prepare(`SELECT assertion_id, status, review_at FROM user_assertions
|
|
48
|
-
WHERE status = 'active' AND review_at IS NOT NULL AND review_at <= ? LIMIT ?`).all(now, Math.max(0, limit - expired.length));
|
|
49
|
-
metrics.scanned += reviewDue.length;
|
|
50
|
-
for (const row of reviewDue) {
|
|
51
|
-
db.prepare("UPDATE user_assertions SET status = 'needs_review' WHERE assertion_id = ? AND status = 'active'").run(row.assertion_id);
|
|
52
|
-
db.prepare(`INSERT INTO user_assertion_status_events (
|
|
53
|
-
event_id, assertion_id, from_status, to_status, actor_type, reason, source_run_id, created_at
|
|
54
|
-
) VALUES (?, ?, 'active', 'needs_review', 'maintenance', 'Scheduled review became due.', ?, ?)`).run(randomUUID(), row.assertion_id, runId, now);
|
|
55
|
-
addDecision(db, runId, "assertion", row.assertion_id, "needs_review", "review_due", row, { status: "needs_review" }, now);
|
|
56
|
-
metrics.needsReview += 1;
|
|
57
|
-
}
|
|
58
51
|
}
|
|
59
52
|
function transitionPriorities(db, runId, principalId, now, limit, metrics) {
|
|
60
53
|
const rows = db.prepare(`SELECT priority_id, status, valid_to FROM user_priority_windows
|
|
@@ -68,7 +61,7 @@ function transitionPriorities(db, runId, principalId, now, limit, metrics) {
|
|
|
68
61
|
}
|
|
69
62
|
function transitionKnowledge(db, runId, principalId, now, limit, metrics) {
|
|
70
63
|
const expired = db.prepare(`SELECT knowledge_id, status, expires_at FROM knowledge_items
|
|
71
|
-
WHERE principal_id = ? AND status
|
|
64
|
+
WHERE principal_id = ? AND status IN ('active', 'candidate') AND expires_at IS NOT NULL AND expires_at < ? LIMIT ?`).all(principalId, now, limit);
|
|
72
65
|
metrics.scanned += expired.length;
|
|
73
66
|
for (const row of expired) {
|
|
74
67
|
db.prepare("UPDATE knowledge_items SET status = 'stale', updated_at = ? WHERE knowledge_id = ?").run(now, row.knowledge_id);
|
|
@@ -76,12 +69,12 @@ function transitionKnowledge(db, runId, principalId, now, limit, metrics) {
|
|
|
76
69
|
metrics.stale += 1;
|
|
77
70
|
}
|
|
78
71
|
const reviewDue = db.prepare(`SELECT knowledge_id, status, review_at FROM knowledge_items
|
|
79
|
-
WHERE principal_id = ? AND status
|
|
72
|
+
WHERE principal_id = ? AND status IN ('active', 'candidate', 'needs_review') AND review_at IS NOT NULL AND review_at <= ? LIMIT ?`).all(principalId, now, Math.max(0, limit - expired.length));
|
|
80
73
|
metrics.scanned += reviewDue.length;
|
|
81
74
|
for (const row of reviewDue) {
|
|
82
|
-
db.prepare("UPDATE knowledge_items SET status = '
|
|
83
|
-
addDecision(db, runId, "knowledge", row.knowledge_id, "
|
|
84
|
-
metrics.
|
|
75
|
+
db.prepare("UPDATE knowledge_items SET status = 'stale', updated_at = ? WHERE knowledge_id = ?").run(now, row.knowledge_id);
|
|
76
|
+
addDecision(db, runId, "knowledge", row.knowledge_id, "stale", "review_due", row, { status: "stale" }, now);
|
|
77
|
+
metrics.stale += 1;
|
|
85
78
|
}
|
|
86
79
|
}
|
|
87
80
|
function reconcileEvidence(db, runId, principalId, now, limit, evidenceThreshold, metrics) {
|
|
@@ -89,7 +82,10 @@ function reconcileEvidence(db, runId, principalId, now, limit, evidenceThreshold
|
|
|
89
82
|
FROM user_assertions a
|
|
90
83
|
JOIN user_assertion_slots s ON s.slot_id = a.slot_id
|
|
91
84
|
JOIN user_assertion_evidence ae ON ae.assertion_id = a.assertion_id AND ae.relation = 'contradicts'
|
|
92
|
-
|
|
85
|
+
JOIN context_evidence e ON e.evidence_id = ae.evidence_id
|
|
86
|
+
WHERE s.principal_id = ? AND a.status = 'active'
|
|
87
|
+
AND e.trust_level = 'owner' AND e.source_type IN ('conversation', 'user')
|
|
88
|
+
AND e.observed_at > a.observed_at AND ae.confidence >= 0.7 LIMIT ?`).all(principalId, limit);
|
|
93
89
|
metrics.scanned += contradicted.length;
|
|
94
90
|
for (const row of contradicted) {
|
|
95
91
|
db.prepare("UPDATE user_assertions SET status = 'needs_review' WHERE assertion_id = ?").run(row.assertion_id);
|
|
@@ -99,16 +95,31 @@ function reconcileEvidence(db, runId, principalId, now, limit, evidenceThreshold
|
|
|
99
95
|
addDecision(db, runId, "assertion", row.assertion_id, "needs_review", "contradictory_evidence", row, { status: "needs_review" }, now);
|
|
100
96
|
metrics.needsReview += 1;
|
|
101
97
|
}
|
|
102
|
-
const candidates = db.prepare(`SELECT a.assertion_id, a.status,
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
const candidates = db.prepare(`SELECT a.assertion_id, a.status,
|
|
99
|
+
MIN(COUNT(DISTINCT COALESCE(e.content_hash, e.source_ref)),
|
|
100
|
+
COUNT(DISTINCT e.source_type || ':' || COALESCE(e.source_instance_id, '') || ':' ||
|
|
101
|
+
COALESCE(e.source_item_id, e.source_ref))) AS evidence_count
|
|
105
102
|
FROM user_assertions a
|
|
106
103
|
JOIN user_assertion_slots s ON s.slot_id = a.slot_id
|
|
107
104
|
JOIN user_assertion_evidence ae ON ae.assertion_id = a.assertion_id AND ae.relation = 'supports'
|
|
108
105
|
JOIN context_evidence e ON e.evidence_id = ae.evidence_id AND e.trust_level = 'owner'
|
|
109
106
|
WHERE s.principal_id = ? AND a.status = 'candidate'
|
|
110
107
|
AND a.authority IN ('user_observed', 'system_inferred')
|
|
111
|
-
|
|
108
|
+
AND a.confidence >= 0.7 AND ae.confidence >= 0.7
|
|
109
|
+
AND a.sensitivity = 'normal' AND a.consequence IN ('low', 'medium')
|
|
110
|
+
AND a.disclosure_policy != 'ask_before_reference'
|
|
111
|
+
AND e.source_type IN ('conversation', 'user', 'connector')
|
|
112
|
+
AND (a.valid_from IS NULL OR a.valid_from <= ?)
|
|
113
|
+
AND (a.valid_to IS NULL OR a.valid_to >= ?)
|
|
114
|
+
AND (a.review_at IS NULL OR a.review_at > ?)
|
|
115
|
+
AND NOT EXISTS (SELECT 1 FROM user_assertion_evidence bad
|
|
116
|
+
WHERE bad.assertion_id = a.assertion_id AND bad.relation = 'contradicts')
|
|
117
|
+
AND (s.cardinality = 'multiple' OR NOT EXISTS (
|
|
118
|
+
SELECT 1 FROM user_assertions other WHERE other.slot_id = a.slot_id
|
|
119
|
+
AND other.assertion_id != a.assertion_id AND other.status IN ('active', 'conflicted')
|
|
120
|
+
AND other.normalized_value != a.normalized_value
|
|
121
|
+
AND (other.valid_to IS NULL OR other.valid_to >= ?)))
|
|
122
|
+
GROUP BY a.assertion_id HAVING evidence_count >= ? ORDER BY a.recorded_at LIMIT ?`).all(principalId, now, now, now, now, evidenceThreshold, limit);
|
|
112
123
|
metrics.scanned += candidates.length;
|
|
113
124
|
for (const row of candidates) {
|
|
114
125
|
db.prepare("UPDATE user_assertions SET status = 'active' WHERE assertion_id = ? AND status = 'candidate'").run(row.assertion_id);
|
|
@@ -119,6 +130,24 @@ function reconcileEvidence(db, runId, principalId, now, limit, evidenceThreshold
|
|
|
119
130
|
metrics.activated += 1;
|
|
120
131
|
}
|
|
121
132
|
}
|
|
133
|
+
function activateKnowledge(db, runId, principalId, now, limit, metrics) {
|
|
134
|
+
const rows = db.prepare(`SELECT knowledge_id FROM knowledge_items
|
|
135
|
+
WHERE principal_id = ? AND record_class = 'memory' AND status = 'candidate'
|
|
136
|
+
AND origin_class IN ('owner', 'agent', 'system') AND confidence >= 0.7
|
|
137
|
+
AND derived_from_recalled_context = 0
|
|
138
|
+
AND (valid_from IS NULL OR valid_from <= ?) AND (valid_to IS NULL OR valid_to >= ?)
|
|
139
|
+
AND (expires_at IS NULL OR expires_at >= ?) AND (review_at IS NULL OR review_at > ?)
|
|
140
|
+
ORDER BY created_at, knowledge_id LIMIT ?`).all(principalId, now, now, now, now, limit);
|
|
141
|
+
metrics.scanned += rows.length;
|
|
142
|
+
for (const row of rows) {
|
|
143
|
+
db.prepare("UPDATE knowledge_items SET status = 'active', updated_at = ? WHERE knowledge_id = ?").run(now, row.knowledge_id);
|
|
144
|
+
db.prepare(`INSERT INTO knowledge_item_status_events
|
|
145
|
+
(event_id, knowledge_id, from_status, to_status, actor_type, reason, created_at)
|
|
146
|
+
VALUES (?, ?, 'candidate', 'active', 'maintenance', 'Eligible scoped memory activated automatically.', ?)`).run(randomUUID(), row.knowledge_id, now);
|
|
147
|
+
addDecision(db, runId, "knowledge", row.knowledge_id, "activated", "eligible_scoped_memory", { status: "candidate" }, { status: "active" }, now);
|
|
148
|
+
metrics.activated += 1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
122
151
|
function repairIndexes(db, runId, now, metrics) {
|
|
123
152
|
const assertionMissing = db.prepare(`SELECT COUNT(*) AS count FROM user_assertions a
|
|
124
153
|
LEFT JOIN user_assertions_fts f ON f.assertion_id = a.assertion_id
|
|
@@ -178,11 +207,12 @@ function runMemoryMaintenance(input) {
|
|
|
178
207
|
});
|
|
179
208
|
try {
|
|
180
209
|
runSqliteWriteTransaction((db) => {
|
|
181
|
-
transitionAssertions(db, runId, now, limit, metrics);
|
|
210
|
+
transitionAssertions(db, runId, principalId, now, limit, metrics);
|
|
182
211
|
transitionPriorities(db, runId, principalId, now, limit, metrics);
|
|
183
212
|
transitionKnowledge(db, runId, principalId, now, limit, metrics);
|
|
184
213
|
if (input.jobType === "daily_reconciliation" || input.jobType === "manual_repair") {
|
|
185
214
|
reconcileEvidence(db, runId, principalId, now, limit, evidenceThreshold, metrics);
|
|
215
|
+
activateKnowledge(db, runId, principalId, now, limit, metrics);
|
|
186
216
|
repairIndexes(db, runId, now, metrics);
|
|
187
217
|
}
|
|
188
218
|
if (input.jobType === "weekly_knowledge" || input.jobType === "manual_repair") archiveOldKnowledge(db, runId, principalId, now - (input.staleRetentionDays ?? 30) * 24 * 60 * 60 * 1e3, now, limit, metrics);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { init_lock, isGatewayConfigLockedByAnotherProcessSync } from "../gateway/lock.js";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
//#region src/migrations/memory-config.ts
|
|
4
|
+
init_lock();
|
|
5
|
+
const id = "automatic-memory-config";
|
|
6
|
+
function normalizeKnowledgeMemoryConfigInput(input) {
|
|
7
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return input;
|
|
8
|
+
const memory = input;
|
|
9
|
+
if (!Array.isArray(memory.sources)) return input;
|
|
10
|
+
const oldSources = memory.sources.filter((source) => typeof source === "string");
|
|
11
|
+
const visibilityScopes = new Set([
|
|
12
|
+
"session",
|
|
13
|
+
"workspace",
|
|
14
|
+
"project"
|
|
15
|
+
]);
|
|
16
|
+
const normalized = { ...memory };
|
|
17
|
+
if (!Array.isArray(normalized.readScopes)) normalized.readScopes = oldSources.filter((source) => visibilityScopes.has(source));
|
|
18
|
+
if (!Array.isArray(normalized.contentSources)) normalized.contentSources = [
|
|
19
|
+
...oldSources.some((source) => visibilityScopes.has(source)) ? ["memory"] : [],
|
|
20
|
+
...oldSources.includes("workspace") ? ["local_import"] : [],
|
|
21
|
+
...oldSources.includes("connector") ? ["connector"] : []
|
|
22
|
+
];
|
|
23
|
+
delete normalized.sources;
|
|
24
|
+
return normalized;
|
|
25
|
+
}
|
|
26
|
+
function convertedConfig(ctx) {
|
|
27
|
+
if (!existsSync(ctx.configPath)) return null;
|
|
28
|
+
const config = JSON.parse(readFileSync(ctx.configPath, "utf8"));
|
|
29
|
+
const userContext = config.userContext;
|
|
30
|
+
if (!userContext || typeof userContext !== "object" || Array.isArray(userContext)) return null;
|
|
31
|
+
const context = userContext;
|
|
32
|
+
let changed = false;
|
|
33
|
+
for (const key of ["userModel", "knowledgeMemory"]) {
|
|
34
|
+
const section = context[key];
|
|
35
|
+
if (!section || typeof section !== "object" || Array.isArray(section)) continue;
|
|
36
|
+
const memory = section;
|
|
37
|
+
if (memory.writePolicy === "confirm") {
|
|
38
|
+
memory.writePolicy = "allow";
|
|
39
|
+
changed = true;
|
|
40
|
+
}
|
|
41
|
+
if (key === "knowledgeMemory" && Array.isArray(memory.sources)) {
|
|
42
|
+
context[key] = normalizeKnowledgeMemoryConfigInput(memory);
|
|
43
|
+
changed = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!changed) return null;
|
|
47
|
+
return config;
|
|
48
|
+
}
|
|
49
|
+
function item(status) {
|
|
50
|
+
return {
|
|
51
|
+
id,
|
|
52
|
+
title: "Automatic memory configuration",
|
|
53
|
+
kind: "config",
|
|
54
|
+
safety: "auto",
|
|
55
|
+
status,
|
|
56
|
+
message: "Enable automatic ordinary memory and separate visibility scopes from content sources."
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const memoryConfigMigration = {
|
|
60
|
+
id,
|
|
61
|
+
kind: "config",
|
|
62
|
+
safety: "auto",
|
|
63
|
+
detect: (ctx) => convertedConfig(ctx) ? item("planned") : null,
|
|
64
|
+
apply: (ctx) => {
|
|
65
|
+
if (isGatewayConfigLockedByAnotherProcessSync(ctx.configPath)) return {
|
|
66
|
+
...item("skipped"),
|
|
67
|
+
message: "Deferred knowledge memory config migration until the running gateway restarts."
|
|
68
|
+
};
|
|
69
|
+
const config = convertedConfig(ctx);
|
|
70
|
+
return config ? {
|
|
71
|
+
...item("applied"),
|
|
72
|
+
details: { config }
|
|
73
|
+
} : item("not_needed");
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
//#endregion
|
|
77
|
+
export { memoryConfigMigration };
|
|
@@ -3,7 +3,7 @@ import { init_logger } from "../utils/logger.js";
|
|
|
3
3
|
import { ConfigSchema, init_schema } from "../config/schema.js";
|
|
4
4
|
import { listRegisteredMigrations } from "./registry.js";
|
|
5
5
|
import { conversationRoutingConfigMigration } from "./conversation-routing-config.js";
|
|
6
|
-
import {
|
|
6
|
+
import { memoryConfigMigration } from "./memory-config.js";
|
|
7
7
|
import { dirname, join } from "node:path";
|
|
8
8
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
9
9
|
//#region src/migrations/runner.ts
|
|
@@ -12,7 +12,7 @@ init_logger();
|
|
|
12
12
|
const log = createLogger("Migrations");
|
|
13
13
|
const MIGRATION_LEDGER_FILENAME = "migrations.json";
|
|
14
14
|
const CONFIG_BACKUP_COUNT = 10;
|
|
15
|
-
const CORE_MIGRATIONS = [conversationRoutingConfigMigration,
|
|
15
|
+
const CORE_MIGRATIONS = [conversationRoutingConfigMigration, memoryConfigMigration];
|
|
16
16
|
function listAllMigrations() {
|
|
17
17
|
return [...CORE_MIGRATIONS, ...listRegisteredMigrations().map((entry) => entry.migration)];
|
|
18
18
|
}
|
|
@@ -162,7 +162,7 @@ function workDiscoveryPlan(eventType, payload) {
|
|
|
162
162
|
const event = payload;
|
|
163
163
|
const completed = eventType === "work-discovery.completed";
|
|
164
164
|
if (typeof event.runId !== "string" || !event.runId || typeof event.conversationId !== "string" || !event.conversationId || event.status !== (completed ? "completed" : "failed")) return null;
|
|
165
|
-
const type = completed ? "work_discovery.
|
|
165
|
+
const type = completed ? "work_discovery.completed" : "work_discovery.failed";
|
|
166
166
|
return {
|
|
167
167
|
dedupeKey: `${type}:${event.runId}`,
|
|
168
168
|
notification: {
|
|
@@ -174,15 +174,15 @@ function workDiscoveryPlan(eventType, payload) {
|
|
|
174
174
|
},
|
|
175
175
|
priority: completed ? "normal" : "high",
|
|
176
176
|
title: completed ? {
|
|
177
|
-
en: "Understanding
|
|
178
|
-
zh: "
|
|
177
|
+
en: "Understanding updated",
|
|
178
|
+
zh: "用户理解已更新"
|
|
179
179
|
} : {
|
|
180
180
|
en: "Understanding needs attention",
|
|
181
181
|
zh: "用户理解需要处理"
|
|
182
182
|
},
|
|
183
183
|
body: completed ? {
|
|
184
|
-
en: "
|
|
185
|
-
zh: "
|
|
184
|
+
en: "See what xopc learned. You can edit or delete it anytime.",
|
|
185
|
+
zh: "理解已自动整理,可随时查看、修改或删除。"
|
|
186
186
|
} : {
|
|
187
187
|
en: "Open user understanding to review or retry.",
|
|
188
188
|
zh: "打开用户理解,查看详情或重试。"
|
|
@@ -19,7 +19,7 @@ export declare class NotificationService {
|
|
|
19
19
|
persistGatewayEvent(type: string, payload: unknown): {
|
|
20
20
|
schemaVersion: 1;
|
|
21
21
|
id: string;
|
|
22
|
-
type: "work_discovery.failed" | "chat.completed" | "chat.failed" | "task.needs_input" | "task.blocked" | "task.failed" | "task.completed" | "automation.completed" | "automation.failed" | "proactive.insight"
|
|
22
|
+
type: "work_discovery.completed" | "work_discovery.failed" | "chat.completed" | "chat.failed" | "task.needs_input" | "task.blocked" | "task.failed" | "task.completed" | "automation.completed" | "automation.failed" | "proactive.insight";
|
|
23
23
|
priority: "normal" | "high";
|
|
24
24
|
title: {
|
|
25
25
|
en: string;
|
|
@@ -56,7 +56,7 @@ export declare class NotificationService {
|
|
|
56
56
|
persistPlan(plan: NotificationPlan): {
|
|
57
57
|
schemaVersion: 1;
|
|
58
58
|
id: string;
|
|
59
|
-
type: "work_discovery.failed" | "chat.completed" | "chat.failed" | "task.needs_input" | "task.blocked" | "task.failed" | "task.completed" | "automation.completed" | "automation.failed" | "proactive.insight"
|
|
59
|
+
type: "work_discovery.completed" | "work_discovery.failed" | "chat.completed" | "chat.failed" | "task.needs_input" | "task.blocked" | "task.failed" | "task.completed" | "automation.completed" | "automation.failed" | "proactive.insight";
|
|
60
60
|
priority: "normal" | "high";
|
|
61
61
|
title: {
|
|
62
62
|
en: string;
|
|
@@ -40,7 +40,7 @@ function preferenceAllows(type, preferences) {
|
|
|
40
40
|
case "automation.completed": return preferences.automationCompleted;
|
|
41
41
|
case "automation.failed": return preferences.automationFailed;
|
|
42
42
|
case "proactive.insight": return preferences.proactiveInsight;
|
|
43
|
-
case "work_discovery.
|
|
43
|
+
case "work_discovery.completed":
|
|
44
44
|
case "work_discovery.failed": return true;
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
CREATE TABLE memory_suppressions (
|
|
2
|
+
fingerprint TEXT PRIMARY KEY,
|
|
3
|
+
created_at INTEGER NOT NULL
|
|
4
|
+
);
|
|
5
|
+
|
|
6
|
+
UPDATE notification_events SET
|
|
7
|
+
event_type = 'work_discovery.completed',
|
|
8
|
+
dedupe_key = replace(dedupe_key, 'work_discovery.review_ready:', 'work_discovery.completed:'),
|
|
9
|
+
title_en = 'Understanding updated', title_zh = '用户理解已更新',
|
|
10
|
+
body_en = 'See what xopc learned. You can edit or delete it anytime.',
|
|
11
|
+
body_zh = '理解已自动整理,可随时查看、修改或删除。'
|
|
12
|
+
WHERE event_type = 'work_discovery.review_ready';
|
|
@@ -3,7 +3,7 @@ import type { ApplyMigrationsOptions } from './types.js';
|
|
|
3
3
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
4
4
|
export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 165;
|
|
5
5
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
6
|
-
export declare const XOPC_DB_SCHEMA_VERSION =
|
|
6
|
+
export declare const XOPC_DB_SCHEMA_VERSION = 185;
|
|
7
7
|
export declare function resolveMigrationsDir(override?: string): string;
|
|
8
8
|
/**
|
|
9
9
|
* Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
|
|
@@ -63,7 +63,7 @@ function applySingleMigration(db, migration) {
|
|
|
63
63
|
* Each step runs in its own transaction and updates schema_meta on success.
|
|
64
64
|
*/
|
|
65
65
|
function applyPendingMigrations(db, options = {}) {
|
|
66
|
-
const targetVersion = options.targetVersion ??
|
|
66
|
+
const targetVersion = options.targetVersion ?? 185;
|
|
67
67
|
let currentVersion = readSchemaVersion(db);
|
|
68
68
|
if (currentVersion > targetVersion) throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
|
|
69
69
|
if (!options.migrationsDir && currentVersion < 165) throw new DatabaseSchemaTooOldError(currentVersion, 165);
|
|
@@ -118,7 +118,7 @@ function applyPendingMigrations(db, options = {}) {
|
|
|
118
118
|
}
|
|
119
119
|
/** Inspect schema version without mutating the database. */
|
|
120
120
|
function inspectSchemaMigrationStatus(db, options = {}) {
|
|
121
|
-
const appVersion = options.targetVersion ??
|
|
121
|
+
const appVersion = options.targetVersion ?? 185;
|
|
122
122
|
const dbVersion = readSchemaVersion(db);
|
|
123
123
|
if (dbVersion > appVersion) return {
|
|
124
124
|
dbVersion,
|
|
@@ -157,7 +157,7 @@ var init_runner = __esmMin((() => {
|
|
|
157
157
|
init_errors();
|
|
158
158
|
log = createLogger("Sqlite:Migrations");
|
|
159
159
|
XOPC_DB_BASELINE_SCHEMA_VERSION = 165;
|
|
160
|
-
XOPC_DB_SCHEMA_VERSION =
|
|
160
|
+
XOPC_DB_SCHEMA_VERSION = 185;
|
|
161
161
|
MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
162
162
|
}));
|
|
163
163
|
//#endregion
|
|
@@ -39,7 +39,6 @@ export declare const UserModelConfigSchema: z.ZodDefault<z.ZodObject<{
|
|
|
39
39
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
40
40
|
writePolicy: z.ZodDefault<z.ZodEnum<{
|
|
41
41
|
allow: "allow";
|
|
42
|
-
confirm: "confirm";
|
|
43
42
|
deny: "deny";
|
|
44
43
|
}>>;
|
|
45
44
|
sensitiveWritePolicy: z.ZodDefault<z.ZodEnum<{
|
|
@@ -76,12 +75,10 @@ export declare const UserModelConfigSchema: z.ZodDefault<z.ZodObject<{
|
|
|
76
75
|
staleRetentionDays: z.ZodDefault<z.ZodNumber>;
|
|
77
76
|
}, z.core.$strict>>;
|
|
78
77
|
}, z.core.$strict>>;
|
|
79
|
-
export declare
|
|
80
|
-
export declare const KnowledgeMemoryConfigSchema: z.ZodDefault<z.ZodPreprocess<z.ZodObject<{
|
|
78
|
+
export declare const KnowledgeMemoryConfigSchema: z.ZodDefault<z.ZodObject<{
|
|
81
79
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
82
80
|
writePolicy: z.ZodDefault<z.ZodEnum<{
|
|
83
81
|
allow: "allow";
|
|
84
|
-
confirm: "confirm";
|
|
85
82
|
deny: "deny";
|
|
86
83
|
}>>;
|
|
87
84
|
readScopes: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
@@ -114,7 +111,7 @@ export declare const KnowledgeMemoryConfigSchema: z.ZodDefault<z.ZodPreprocess<z
|
|
|
114
111
|
allowExternalWrites: z.ZodDefault<z.ZodBoolean>;
|
|
115
112
|
allowedProviderIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
116
113
|
autoWriteKinds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
117
|
-
}, z.core.$strict
|
|
114
|
+
}, z.core.$strict>>;
|
|
118
115
|
export declare const ContextPlanningConfigSchema: z.ZodDefault<z.ZodObject<{
|
|
119
116
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
120
117
|
maxAssertions: z.ZodDefault<z.ZodNumber>;
|
|
@@ -152,7 +149,6 @@ export declare const UserContextConfigSchema: z.ZodDefault<z.ZodObject<{
|
|
|
152
149
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
153
150
|
writePolicy: z.ZodDefault<z.ZodEnum<{
|
|
154
151
|
allow: "allow";
|
|
155
|
-
confirm: "confirm";
|
|
156
152
|
deny: "deny";
|
|
157
153
|
}>>;
|
|
158
154
|
sensitiveWritePolicy: z.ZodDefault<z.ZodEnum<{
|
|
@@ -189,11 +185,10 @@ export declare const UserContextConfigSchema: z.ZodDefault<z.ZodObject<{
|
|
|
189
185
|
staleRetentionDays: z.ZodDefault<z.ZodNumber>;
|
|
190
186
|
}, z.core.$strict>>;
|
|
191
187
|
}, z.core.$strict>>;
|
|
192
|
-
knowledgeMemory: z.ZodDefault<z.
|
|
188
|
+
knowledgeMemory: z.ZodDefault<z.ZodObject<{
|
|
193
189
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
194
190
|
writePolicy: z.ZodDefault<z.ZodEnum<{
|
|
195
191
|
allow: "allow";
|
|
196
|
-
confirm: "confirm";
|
|
197
192
|
deny: "deny";
|
|
198
193
|
}>>;
|
|
199
194
|
readScopes: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
@@ -226,7 +221,7 @@ export declare const UserContextConfigSchema: z.ZodDefault<z.ZodObject<{
|
|
|
226
221
|
allowExternalWrites: z.ZodDefault<z.ZodBoolean>;
|
|
227
222
|
allowedProviderIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
228
223
|
autoWriteKinds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
229
|
-
}, z.core.$strict
|
|
224
|
+
}, z.core.$strict>>;
|
|
230
225
|
contextPlanning: z.ZodDefault<z.ZodObject<{
|
|
231
226
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
232
227
|
maxAssertions: z.ZodDefault<z.ZodNumber>;
|