@xopcai/xopc 0.0.409 → 0.0.410
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-lMrejpsz.js → agent-avatar-dicebear-ColV_Z1C.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-lXwR3Rff.js → agent-defaults-settings-panel-rgRXzBCX.js} +1 -1
- package/dist/gateway/static/root/assets/{agents-DFwBf95X.js → agents-C1rSl_yE.js} +3 -3
- package/dist/gateway/static/root/assets/{app-management-settings-panel-Cja3eV7H.js → app-management-settings-panel-B-Cl1lzt.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-Dzhmj_ZO.js → appearance-settings-byEnJz9H.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-C3xbCdYa.js → apps-page-BfVMfjkC.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-D4r4P5uG.js → archive-plugin-BXssEuq3.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-BlNe3DXU.js → automations-page-C-puATkS.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-workspace-CWvHbJBK.js → automations-workspace-b7MTHd-X.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-DuN5L6lm.js → binary-plugins-DBz_2AhG.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-DUSVn51h.js → block-editor-BOO5kjIl.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-BtvOURHQ.js → browser-automations-page-DMqDOJgO.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-C_OeE79S.js → browser-settings-page-KZStwhX7.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-page-BKF6q_tC.js → capabilities-page-DeJuO2rf.js} +2 -2
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-B7qOaUZs.js → capabilities-settings-panel-DPxL_3Km.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-header-actions-oBPqUlbj.js → capability-header-actions-yfPUz5Wt.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-3M9-anCN.js → channels-Cp4F9zPz.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-BLs3NaOP.js → chat-terminal-dock-Dlzn80cK.js} +1 -1
- package/dist/gateway/static/root/assets/{computer-settings-page-LaekhDG6.js → computer-settings-page-BXhmUWbA.js} +1 -1
- package/dist/gateway/static/root/assets/{connector-service-page-DJP1n1_n.js → connector-service-page-B1oJCj74.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-DmQqMy6l.js → connectors-page-Da_M7h3m.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-DVlbwrYg.js → date-picker-DGjpqOKx.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-BhElpD_6.js → dependency-picker-BfWUD3LG.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-ByvV-cDD.js → desktop-pet-CPEi55Vt.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-BBruQu-_.js → desktop-pet-settings-CmQ_H-nM.js} +1 -1
- package/dist/gateway/static/root/assets/{device-pairing-wizard-C3zOy8PO.js → device-pairing-wizard-BeTjXc3C.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-QZ0p-uNa.js → directory-picker-path-field-CwT1LcLR.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-DoVPUHu_.js → extension-debug-page-DX-hX_d0.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BNe8n3iJ.js → extension-page-Cji87mN9.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DxNpjGJD.js → extension-settings-page-BulzpwBD.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-CzrFZ0lB.js → gateway-settings-CIdEroWr.js} +1 -1
- package/dist/gateway/static/root/assets/{highlighted-body-KQOG7T2V-DrZq5KTj.js → highlighted-body-KQOG7T2V-BgTqMa_d.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-cXmd8x0S.js → home-page-BSbYrVda.js} +1 -1
- package/dist/gateway/static/root/assets/{image-generation-api-C6su4n9B.js → image-generation-api-BfPYnMoc.js} +1 -1
- package/dist/gateway/static/root/assets/{imports-page-CsXYryWS.js → imports-page-DYSOfHfw.js} +1 -1
- package/dist/gateway/static/root/assets/{index-uAJlrGvo.js → index-D2hEKnTM.js} +116 -116
- package/dist/gateway/static/root/assets/index-SmKNPEmJ.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-GYKLt0oF.js → keyboard-shortcuts-settings-BwsGM6Yd.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-Dw-i6VkX.js → local-app-workbench-page-K6UR6cAx.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-DslilwNE.js → local-apps-page-gkdwtjDE.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-4N8bGPIE.js → logs-page-BAQNYk0e.js} +1 -1
- package/dist/gateway/static/root/assets/{management-settings-CPmxI1k0.js → management-settings-CMVRLGox.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-s6EqtAUQ.js → media-plugins-DQJXvbr_.js} +1 -1
- package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-Cx0bXBqu.js +1 -0
- package/dist/gateway/static/root/assets/{note-detail-panel-CjTm5gxS.js → note-detail-panel-1GCqp26U.js} +3 -3
- package/dist/gateway/static/root/assets/note-image-lightbox-B7RA-WsY.js +1 -0
- package/dist/gateway/static/root/assets/{notes-page-DkESStKc.js → notes-page-DnjlEdzM.js} +1 -1
- package/dist/gateway/static/root/assets/{page-context-capture-button-eyzTNrCe.js → page-context-capture-button-BAwI_3uh.js} +1 -1
- package/dist/gateway/static/root/assets/{personal-page-DZxbFQvF.js → personal-page-XoLrcR0U.js} +1 -1
- package/dist/gateway/static/root/assets/{plugin-details-dialog-BjVLbxZM.js → plugin-details-dialog-BjzIp3T5.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-7pUtUL4O.js → preview-open-alternatives-2YjwlRHu.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-BPWSqtRt.js → product-open-page-Bfqez8Je.js} +1 -1
- package/dist/gateway/static/root/assets/{product-preview-content-COo4zwp9.js → product-preview-content-D7wLwrqj.js} +1 -1
- package/dist/gateway/static/root/assets/{project-detail-page-CrIH6nEy.js → project-detail-page-1ueRCmXg.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-D4swvBmH.js → projects-page-CDQycMPo.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-DltfIMtv.js → remote-access-hub-YvVmZA4K.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-CNCx2RnO.js → runtime-tools-settings-panel-DSsANtha.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-nRDA2XfI.js → schema-form-vbhv4JE6.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-CfYU53aw.js → sessions-page-B3rPljgr.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-Ch20Ptzp.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-_ALUREPr.js → settings-form-section-4nn0sb6v.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-29ptuMYZ.js → settings-page-_HB9FC08.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-DZgVMRXr.js → setup-status-panel-L1HHrvzi.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-C2LHd_Nl.js → share-preview-page-C4lC0W3d.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-C5MTc6t-.js → shares-settings-DtHZ9z5K.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-api-C-T0K7Oo.js → skill-api-CyO89DKl.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-reload-api-BcdA9ezU.js → skill-reload-api-Drg356Pb.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-BMw8XbKL.js → skills-page-XhCLHKlK.js} +1 -1
- package/dist/gateway/static/root/assets/{style-DR0-RKaL.js → style-DTLpCnuA.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-2Kr9Y-FJ.js → system-settings-panel-bSogz4S-.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-7kPco8Ys.js → task-detail-page-CSv2x4q-.js} +1 -1
- package/dist/gateway/static/root/assets/{tracing-settings-page-D53wxq60.js → tracing-settings-page-CZ5XLQmX.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-DcDUSAjv.js → tunnel-api-DOUyF9Ew.js} +1 -1
- package/dist/gateway/static/root/assets/{usage-page-Hut0SDqn.js → usage-page-Bv3fdfw1.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-C4TUlpsu.js → use-autosave-D0MhLMD2.js} +1 -1
- package/dist/gateway/static/root/assets/{user-model-page-KRy50OBd.js → user-model-page-BD7WvhI0.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-CMdihbPb.js → voice-api-key-field-Ds_ADBQH.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-Bd9HLsds.js → work-discovery-overlay-CDJp2_l2.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-C9mAiHBd.js → workflow-run-setup-panel-CR4fRhZZ.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-CGg4GWwt.js → workflows-page-BeJw5liC.js} +1 -1
- package/dist/gateway/static/root/assets/{xopc-cloud-account-card-ClQKM2RA.js → xopc-cloud-account-card-Di-oEpCt.js} +1 -1
- package/dist/gateway/static/root/index.html +2 -2
- package/dist/package.js +1 -1
- package/dist/src/agent/background-review/prompts.d.ts +8 -0
- package/dist/src/agent/background-review/prompts.js +9 -2
- package/dist/src/agent/background-review/run-background-review.js +73 -11
- package/dist/src/agent/inbound/attachment-pipeline.d.ts +2 -0
- package/dist/src/agent/inbound/attachment-pipeline.js +10 -5
- package/dist/src/agent/tools/factory.js +15 -1
- package/dist/src/agent/tools/session-recall-tool.d.ts +3 -0
- package/dist/src/agent/tools/session-recall-tool.js +3 -0
- package/dist/src/agent/tools/session-search-tool.d.ts +3 -0
- package/dist/src/agent/tools/session-search-tool.js +20 -4
- package/dist/src/gateway/service.js +11 -11
- package/dist/src/home-intelligence/host.js +2 -2
- package/dist/src/knowledge-memory/repository.d.ts +3 -1
- package/dist/src/knowledge-memory/repository.js +10 -3
- package/dist/src/memory-learning/forget-capture.d.ts +14 -0
- package/dist/src/memory-learning/forget-capture.js +100 -0
- package/dist/src/memory-learning/forgotten-history.d.ts +5 -0
- package/dist/src/memory-learning/forgotten-history.js +33 -0
- package/dist/src/memory-learning/work-capture.js +2 -1
- package/dist/src/session/store.d.ts +1 -0
- package/dist/src/storage/sqlite/fts.d.ts +4 -0
- package/dist/src/storage/sqlite/fts.js +13 -4
- package/dist/src/storage/sqlite/session-repository.js +6 -3
- package/dist/src/storage/sqlite/transcript-repository.d.ts +1 -0
- package/dist/src/storage/sqlite/transcript-repository.js +14 -12
- package/dist/src/user-model/capture/semantic.d.ts +18 -1
- package/dist/src/user-model/capture/semantic.js +26 -1
- package/package.json +3 -3
- package/dist/gateway/static/root/assets/index-BZlryZm8.css +0 -1
- package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-Ba-WQCki.js +0 -1
- package/dist/gateway/static/root/assets/note-image-lightbox-hbSbEEfN.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BJBoWYbO.js +0 -1
|
@@ -7,8 +7,11 @@ import { loadCompactionSourceSnapshot } from "../../storage/sqlite/transcript-re
|
|
|
7
7
|
import { init_sqlite } from "../../storage/sqlite/index.js";
|
|
8
8
|
import { extractTextContent } from "../context/workspace.js";
|
|
9
9
|
import { init_model_usage, withModelUsageOrigin } from "../../providers/model-usage.js";
|
|
10
|
+
import { canUseKnowledge } from "../../knowledge-memory/usage-policy.js";
|
|
11
|
+
import { listKnowledgeItems } from "../../knowledge-memory/repository.js";
|
|
10
12
|
import { resolveUserContextSessionAccess } from "../../user-context/access-policy.js";
|
|
11
13
|
import { failLearningJob, finishLearningJob, learningPolicyHash } from "../../memory-learning/repository.js";
|
|
14
|
+
import "../../knowledge-memory/index.js";
|
|
12
15
|
import { extractProfileAgentId } from "../../config/agent-profile.js";
|
|
13
16
|
import { resolveModelIntentRef, resolveModelSelector } from "../../config/agent-model-intents.js";
|
|
14
17
|
import { init_sync_provider_auth, resolveProviderApiKeySync } from "../../auth/sync-provider-auth.js";
|
|
@@ -19,6 +22,7 @@ import { isLoopbackModel } from "../../providers/local-model.js";
|
|
|
19
22
|
import { claimRegisteredExtraction } from "../../user-context/extraction/registry.js";
|
|
20
23
|
import { applyWorkCandidates } from "../../memory-learning/work-capture.js";
|
|
21
24
|
import { stripRuntimeContextFromUserMessage } from "../../session/user-message-display.js";
|
|
25
|
+
import { applyKnowledgeDeletions } from "../../memory-learning/forget-capture.js";
|
|
22
26
|
import { getAssertionSlot, listUserAssertions } from "../../user-model/repository.js";
|
|
23
27
|
import { emptyUserModelCaptureResult, executeUserModelInterpretation } from "../../user-model/capture/executor.js";
|
|
24
28
|
import { parseUserModelInterpretation } from "../../user-model/capture/semantic.js";
|
|
@@ -129,6 +133,17 @@ function resolveInterpreterRuntime(params) {
|
|
|
129
133
|
};
|
|
130
134
|
}
|
|
131
135
|
async function interpret(params) {
|
|
136
|
+
const context = {
|
|
137
|
+
conversationId: params.conversationId,
|
|
138
|
+
agentId: extractProfileAgentId(params.conversationId),
|
|
139
|
+
workspaceId: params.workspaceId ?? "",
|
|
140
|
+
projectId: getSessionMetadata(params.conversationId)?.projectId
|
|
141
|
+
};
|
|
142
|
+
const access = params.getConfig() ? resolveUserContextSessionAccess(params.getConfig(), params.conversationId) : void 0;
|
|
143
|
+
const availableKnowledge = access?.knowledge ? listKnowledgeItems({
|
|
144
|
+
recordClass: "memory",
|
|
145
|
+
limit: 200
|
|
146
|
+
}).filter((item) => canUseKnowledge(item, context, access.knowledgePolicy)).slice(0, 80) : [];
|
|
132
147
|
const providerStreamFn = createExtensionAwareStreamFn();
|
|
133
148
|
const reviewAgent = new Agent({
|
|
134
149
|
initialState: {
|
|
@@ -159,6 +174,11 @@ async function interpret(params) {
|
|
|
159
174
|
content: buildUserModelInterpreterPrompt({
|
|
160
175
|
mode: params.mode,
|
|
161
176
|
availableAssertions: params.availableAssertions,
|
|
177
|
+
availableKnowledge: availableKnowledge.map(({ id, content, scope }) => ({
|
|
178
|
+
id,
|
|
179
|
+
content,
|
|
180
|
+
scope
|
|
181
|
+
})),
|
|
162
182
|
evidenceTimestamp: new Date(params.evidence.at(-1)?.createdAt ?? Date.now()).toISOString(),
|
|
163
183
|
timezone,
|
|
164
184
|
scopeContext: {
|
|
@@ -184,7 +204,7 @@ async function interpret(params) {
|
|
|
184
204
|
let failure;
|
|
185
205
|
interpretation = parseUserModelInterpretation(raw, params.evidence, params.availableAssertions.map((item) => item.id), (value) => {
|
|
186
206
|
failure = value;
|
|
187
|
-
});
|
|
207
|
+
}, availableKnowledge.map((item) => item.id));
|
|
188
208
|
if (interpretation) return;
|
|
189
209
|
const fields = {
|
|
190
210
|
...diagnostics,
|
|
@@ -261,6 +281,24 @@ async function executeReviewInner(params) {
|
|
|
261
281
|
},
|
|
262
282
|
...params.turnId ? { turnId: params.turnId } : {}
|
|
263
283
|
});
|
|
284
|
+
if (config) {
|
|
285
|
+
const access = resolveUserContextSessionAccess(config, params.conversationId);
|
|
286
|
+
const context = {
|
|
287
|
+
conversationId: params.conversationId,
|
|
288
|
+
agentId,
|
|
289
|
+
workspaceId: params.workspaceId,
|
|
290
|
+
projectId: getSessionMetadata(params.conversationId)?.projectId
|
|
291
|
+
};
|
|
292
|
+
if (access.knowledge) applyKnowledgeDeletions({
|
|
293
|
+
interpretation,
|
|
294
|
+
evidence: params.evidence,
|
|
295
|
+
context,
|
|
296
|
+
allowedTargetIds: listKnowledgeItems({
|
|
297
|
+
recordClass: "memory",
|
|
298
|
+
limit: 200
|
|
299
|
+
}).filter((item) => canUseKnowledge(item, context, access.knowledgePolicy)).map((item) => item.id)
|
|
300
|
+
});
|
|
301
|
+
}
|
|
264
302
|
finishContextExtractionRun({
|
|
265
303
|
runId: extraction.run.id,
|
|
266
304
|
status: "completed",
|
|
@@ -346,7 +384,12 @@ async function runPersistentLearningJobInner(job, getConfig) {
|
|
|
346
384
|
"assistant",
|
|
347
385
|
"toolResult"
|
|
348
386
|
].includes(row.role)) continue;
|
|
349
|
-
|
|
387
|
+
const sourceContexts = message.metadata?.sourceContexts;
|
|
388
|
+
if (Array.isArray(sourceContexts) && sourceContexts.length > 0) {
|
|
389
|
+
const raw = messageText(message);
|
|
390
|
+
const wrapped = /<source_contexts>[\s\S]*<\/source_contexts>\s*<user_message>\r?\n[\s\S]*\r?\n<\/user_message>\s*$/u.test(raw);
|
|
391
|
+
if (row.role !== "user" || !wrapped || raw === text || /<source_contexts>|<\/?user_message>/u.test(text)) continue;
|
|
392
|
+
}
|
|
350
393
|
if (row.role === "toolResult" && /(?:memory|user_context|knowledge|session|history|recall)/i.test(message.toolName ?? "")) continue;
|
|
351
394
|
if (text.length > maxChars || /<xopc_user_context>|<xopc_task_execution>|<user-context>/.test(text)) continue;
|
|
352
395
|
chars += text.length;
|
|
@@ -439,7 +482,22 @@ async function runPersistentLearningJobInner(job, getConfig) {
|
|
|
439
482
|
processing: runtime.processingPolicy
|
|
440
483
|
}
|
|
441
484
|
}) : emptyUserModelCaptureResult();
|
|
442
|
-
const
|
|
485
|
+
const deletionContext = {
|
|
486
|
+
conversationId: job.conversation_id,
|
|
487
|
+
agentId,
|
|
488
|
+
workspaceId: job.workspace_id,
|
|
489
|
+
projectId
|
|
490
|
+
};
|
|
491
|
+
const deletions = access.knowledge ? applyKnowledgeDeletions({
|
|
492
|
+
interpretation,
|
|
493
|
+
evidence,
|
|
494
|
+
context: deletionContext,
|
|
495
|
+
allowedTargetIds: listKnowledgeItems({
|
|
496
|
+
recordClass: "memory",
|
|
497
|
+
limit: 200
|
|
498
|
+
}).filter((item) => canUseKnowledge(item, deletionContext, access.knowledgePolicy)).map((item) => item.id)
|
|
499
|
+
}) : [];
|
|
500
|
+
const work = interpretation.intent !== "forget" && !interpretation.knowledgeDeletions?.length && access.knowledge && current.userContext.knowledgeMemory.writePolicy === "allow" && access.knowledgePolicy.contentSources.includes("memory") ? applyWorkCandidates({
|
|
443
501
|
candidates: (interpretation.knowledge ?? []).filter((candidate) => access.knowledgePolicy.scopes.includes(candidate.scope)),
|
|
444
502
|
evidence,
|
|
445
503
|
conversationId: job.conversation_id,
|
|
@@ -452,14 +510,18 @@ async function runPersistentLearningJobInner(job, getConfig) {
|
|
|
452
510
|
finishContextExtractionRun({
|
|
453
511
|
runId: extraction.run.id,
|
|
454
512
|
status: "completed",
|
|
455
|
-
outputs: [
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
513
|
+
outputs: [
|
|
514
|
+
...result.outputs.map((output) => ({
|
|
515
|
+
candidateKey: output.candidateKey,
|
|
516
|
+
...output.assertionId ? {
|
|
517
|
+
objectType: "assertion",
|
|
518
|
+
objectId: output.assertionId
|
|
519
|
+
} : {},
|
|
520
|
+
outcome: output.outcome === "rejected" ? "rejected" : output.outcome === "deduplicated" ? "deduplicated" : "created"
|
|
521
|
+
})),
|
|
522
|
+
...deletions,
|
|
523
|
+
...work
|
|
524
|
+
]
|
|
463
525
|
});
|
|
464
526
|
}, through);
|
|
465
527
|
} catch (err) {
|
|
@@ -34,6 +34,8 @@ export declare function hydrateUserTurnForLlm(opts: {
|
|
|
34
34
|
}): Promise<LlmUserTurn>;
|
|
35
35
|
export declare function setPendingTranscriptUserMessage(conversationId: string, message: TranscriptUserMessage): void;
|
|
36
36
|
export declare function getPendingTranscriptUserText(conversationId: string): string | undefined;
|
|
37
|
+
/** The active owner's utterance remains available after its persistence receipt is consumed. */
|
|
38
|
+
export declare function getActiveTranscriptUserText(conversationId: string): string | undefined;
|
|
37
39
|
export declare function takePendingTranscriptUserMessage(conversationId: string): TranscriptUserMessage | undefined;
|
|
38
40
|
export declare function clearPendingTranscriptUserMessage(conversationId: string, message?: TranscriptUserMessage): void;
|
|
39
41
|
export declare function pendingTranscriptReferencesMediaUri(conversationId: string, uri: string): boolean;
|
|
@@ -143,6 +143,7 @@ async function hydrateUserTurnForLlm(opts) {
|
|
|
143
143
|
};
|
|
144
144
|
}
|
|
145
145
|
const pendingTranscriptBySession = /* @__PURE__ */ new Map();
|
|
146
|
+
const activeTranscriptUserBySession = /* @__PURE__ */ new Map();
|
|
146
147
|
function messageTextForPendingCompare(message) {
|
|
147
148
|
return joinTextBlocks(textBlocksFromContent(readAgentMessageContent(message)));
|
|
148
149
|
}
|
|
@@ -159,22 +160,26 @@ function pendingMatchesMessage(pending, message) {
|
|
|
159
160
|
function setPendingTranscriptUserMessage(conversationId, message) {
|
|
160
161
|
assertTranscriptUserMessage(message);
|
|
161
162
|
pendingTranscriptBySession.set(conversationId, message);
|
|
163
|
+
activeTranscriptUserBySession.set(conversationId, message);
|
|
162
164
|
}
|
|
163
165
|
function getPendingTranscriptUserText(conversationId) {
|
|
164
166
|
const message = pendingTranscriptBySession.get(conversationId);
|
|
165
167
|
if (!message) return void 0;
|
|
166
168
|
return stripRuntimeUserMessageEnvelope(messageTextForPendingCompare(message)).trim();
|
|
167
169
|
}
|
|
170
|
+
/** The active owner's utterance remains available after its persistence receipt is consumed. */
|
|
171
|
+
function getActiveTranscriptUserText(conversationId) {
|
|
172
|
+
const message = activeTranscriptUserBySession.get(conversationId);
|
|
173
|
+
return message ? stripRuntimeUserMessageEnvelope(messageTextForPendingCompare(message)).trim() : void 0;
|
|
174
|
+
}
|
|
168
175
|
function takePendingTranscriptUserMessage(conversationId) {
|
|
169
176
|
const msg = pendingTranscriptBySession.get(conversationId);
|
|
170
177
|
pendingTranscriptBySession.delete(conversationId);
|
|
171
178
|
return msg;
|
|
172
179
|
}
|
|
173
180
|
function clearPendingTranscriptUserMessage(conversationId, message) {
|
|
174
|
-
|
|
175
|
-
if (!
|
|
176
|
-
if (message && existing !== message) return;
|
|
177
|
-
pendingTranscriptBySession.delete(conversationId);
|
|
181
|
+
if (!message || pendingTranscriptBySession.get(conversationId) === message) pendingTranscriptBySession.delete(conversationId);
|
|
182
|
+
if (!message || activeTranscriptUserBySession.get(conversationId) === message) activeTranscriptUserBySession.delete(conversationId);
|
|
178
183
|
}
|
|
179
184
|
function pendingTranscriptReferencesMediaUri(conversationId, uri) {
|
|
180
185
|
return pendingTranscriptBySession.get(conversationId)?.media?.some((ref) => ref.uri === uri.trim()) === true;
|
|
@@ -192,4 +197,4 @@ function transformUserMessageForPersistence(conversationId, message) {
|
|
|
192
197
|
return message;
|
|
193
198
|
}
|
|
194
199
|
//#endregion
|
|
195
|
-
export { assertTranscriptUserMessage, buildTranscriptUserMessage, clearPendingTranscriptUserMessage, getPendingTranscriptUserText, hydrateUserTurnForLlm, pendingTranscriptReferencesMediaUri, setPendingTranscriptUserMessage, takePendingTranscriptUserMessage, transformUserMessageForPersistence };
|
|
200
|
+
export { assertTranscriptUserMessage, buildTranscriptUserMessage, clearPendingTranscriptUserMessage, getActiveTranscriptUserText, getPendingTranscriptUserText, hydrateUserTurnForLlm, pendingTranscriptReferencesMediaUri, setPendingTranscriptUserMessage, takePendingTranscriptUserMessage, transformUserMessageForPersistence };
|
|
@@ -53,7 +53,7 @@ import { BrowserRuntime } from "../../browser/runtime/browser-runtime.js";
|
|
|
53
53
|
import { createBrowserDriver } from "../../browser/drivers/create-driver.js";
|
|
54
54
|
import { checkBrowserReadiness } from "../../browser/readiness.js";
|
|
55
55
|
import "../../browser/index.js";
|
|
56
|
-
import { getPendingTranscriptUserText } from "../inbound/attachment-pipeline.js";
|
|
56
|
+
import { getActiveTranscriptUserText, getPendingTranscriptUserText } from "../inbound/attachment-pipeline.js";
|
|
57
57
|
import { createDelegateTool } from "./delegate-tool.js";
|
|
58
58
|
import { createWorkflowTool } from "./workflow-tool.js";
|
|
59
59
|
import { createWorkflowCatalog } from "../workflow/catalog.js";
|
|
@@ -521,10 +521,24 @@ var AgentToolsFactory = class AgentToolsFactory {
|
|
|
521
521
|
...this.deps.getSessionStore ? [createSessionRecallTool({
|
|
522
522
|
getSessionStore: this.deps.getSessionStore,
|
|
523
523
|
getCurrentConversationId: currentConversationId,
|
|
524
|
+
getHistoryContext: () => ({
|
|
525
|
+
agentId: resolvedAgentId,
|
|
526
|
+
workspaceId: workspace,
|
|
527
|
+
conversationId: currentConversationId() ?? "",
|
|
528
|
+
projectId: currentProjectId()
|
|
529
|
+
}),
|
|
530
|
+
getCurrentUserText: () => currentConversationId() ? getActiveTranscriptUserText(currentConversationId()) : void 0,
|
|
524
531
|
...personalToolsConfigured ? { maxOutputChars: LOCAL_READ_MAX_CHARS } : {}
|
|
525
532
|
})] : [],
|
|
526
533
|
...this.deps.getSessionStore && getPrimary ? [createSessionSearchTool({
|
|
527
534
|
localOnly: personalToolsConfigured,
|
|
535
|
+
getHistoryContext: () => ({
|
|
536
|
+
agentId: resolvedAgentId,
|
|
537
|
+
workspaceId: workspace,
|
|
538
|
+
conversationId: currentConversationId() ?? "",
|
|
539
|
+
projectId: currentProjectId()
|
|
540
|
+
}),
|
|
541
|
+
getCurrentUserText: () => currentConversationId() ? getActiveTranscriptUserText(currentConversationId()) : void 0,
|
|
528
542
|
getSessionStore: this.deps.getSessionStore,
|
|
529
543
|
getPrimaryModel: getPrimary,
|
|
530
544
|
getCurrentConversationId: () => this.deps.getCurrentContext()?.conversationId,
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { AgentTool } from '@earendil-works/pi-agent-core';
|
|
2
|
+
import type { CaptureScopeContext } from '../../user-model/capture/executor.js';
|
|
2
3
|
import type { SessionStore } from '../../session/store.js';
|
|
3
4
|
export interface SessionRecallToolDeps {
|
|
4
5
|
getSessionStore: () => SessionStore;
|
|
5
6
|
getCurrentConversationId: () => string | undefined;
|
|
6
7
|
maxOutputChars?: number;
|
|
8
|
+
getHistoryContext?: () => CaptureScopeContext;
|
|
9
|
+
getCurrentUserText?: () => string | undefined;
|
|
7
10
|
}
|
|
8
11
|
export declare function createSessionRecallTool(deps: SessionRecallToolDeps): AgentTool;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FORGOTTEN_HISTORY_EXCERPT, forgottenHistoryEntries } from "../../memory-learning/forgotten-history.js";
|
|
1
2
|
import { Type } from "@sinclair/typebox";
|
|
2
3
|
//#region src/agent/tools/session-recall-tool.ts
|
|
3
4
|
const SessionRecallSchema = Type.Object({
|
|
@@ -55,6 +56,8 @@ function createSessionRecallTool(deps) {
|
|
|
55
56
|
...input.beforeSeq ? { beforeSeq: input.beforeSeq } : {},
|
|
56
57
|
...deps.maxOutputChars ? { orderBySeq: true } : {}
|
|
57
58
|
});
|
|
59
|
+
const protectedEntries = deps.getHistoryContext ? forgottenHistoryEntries(deps.getHistoryContext(), deps.getCurrentUserText?.()) : /* @__PURE__ */ new Set();
|
|
60
|
+
for (const match of matches) if (protectedEntries.has(`${conversationId}:*`) || protectedEntries.has(`${conversationId}:${match.entryId}`)) match.content = FORGOTTEN_HISTORY_EXCERPT;
|
|
58
61
|
if (deps.maxOutputChars) matches.sort((a, b) => b.seq - a.seq);
|
|
59
62
|
const results = matches.map((match) => ({
|
|
60
63
|
seq: match.seq,
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { AgentTool } from '@earendil-works/pi-agent-core';
|
|
2
2
|
import { type Api, type Model } from '@earendil-works/pi-ai/compat';
|
|
3
3
|
import type { SessionStore } from '../../session/store.js';
|
|
4
|
+
import type { CaptureScopeContext } from '../../user-model/capture/executor.js';
|
|
4
5
|
export interface SessionSearchToolDeps {
|
|
5
6
|
getSessionStore: () => SessionStore;
|
|
6
7
|
getPrimaryModel: () => Model<Api>;
|
|
7
8
|
getCurrentConversationId?: () => string | undefined;
|
|
8
9
|
canAccess?: () => boolean;
|
|
9
10
|
localOnly?: boolean;
|
|
11
|
+
getHistoryContext?: () => CaptureScopeContext;
|
|
12
|
+
getCurrentUserText?: () => string | undefined;
|
|
10
13
|
}
|
|
11
14
|
export declare function createSessionSearchTool(deps: SessionSearchToolDeps): AgentTool;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
|
+
import { init_fts, sessionSearchTerms } from "../../storage/sqlite/fts.js";
|
|
3
4
|
import { init_providers, resolveModel } from "../../providers/index.js";
|
|
4
5
|
import { completeWithResolvedCredentials } from "../../providers/model-call.js";
|
|
5
6
|
import { readAgentMessageContent } from "../memory/agent-message-access.js";
|
|
6
7
|
import "./local-read-budget.js";
|
|
8
|
+
import { FORGOTTEN_HISTORY_EXCERPT, forgottenHistoryEntries } from "../../memory-learning/forgotten-history.js";
|
|
7
9
|
import { Type } from "@sinclair/typebox";
|
|
8
10
|
//#region src/agent/tools/session-search-tool.ts
|
|
9
11
|
init_providers();
|
|
10
12
|
init_logger();
|
|
13
|
+
init_fts();
|
|
11
14
|
const log = createLogger("Agent:SessionSearch");
|
|
12
15
|
const MAX_SUMMARY_CHARS = 2e4;
|
|
13
16
|
function resolveSummaryModel(deps) {
|
|
@@ -164,18 +167,26 @@ function createSessionSearchTool(deps) {
|
|
|
164
167
|
const exclude = p.excludeConversationId?.trim() || deps.getCurrentConversationId?.() || "";
|
|
165
168
|
if (exclude) matches = matches.filter((m) => m.key !== exclude);
|
|
166
169
|
const top = matches.slice(0, limit);
|
|
170
|
+
const protectedEntries = deps.getHistoryContext ? forgottenHistoryEntries(deps.getHistoryContext(), deps.getCurrentUserText?.()) : /* @__PURE__ */ new Set();
|
|
167
171
|
if (deps.localOnly) {
|
|
168
172
|
const results = top.map(({ key }) => ({
|
|
169
173
|
conversationId: key,
|
|
170
174
|
name: listed.items.find((item) => item.key === key)?.name?.slice(0, 200),
|
|
171
|
-
excerpts: store.recallSession(key, query, {
|
|
172
|
-
|
|
175
|
+
excerpts: store.recallSession(key, query, {
|
|
176
|
+
limit: 2,
|
|
177
|
+
matchMode: "keyword"
|
|
178
|
+
}).filter((match) => !p.roleFilter || match.role === p.roleFilter).map((match) => {
|
|
179
|
+
const content = protectedEntries.has(`${key}:*`) || protectedEntries.has(`${key}:${match.entryId}`) ? FORGOTTEN_HISTORY_EXCERPT : match.content;
|
|
180
|
+
const positions = sessionSearchTerms(query).map((term) => content.toLowerCase().indexOf(term.toLowerCase())).filter((at) => at >= 0);
|
|
181
|
+
const at = positions.length ? Math.min(...positions) : 0;
|
|
173
182
|
const start = Math.max(0, at - 200);
|
|
174
183
|
return {
|
|
184
|
+
entryId: match.entryId,
|
|
185
|
+
createdAt: match.createdAt,
|
|
175
186
|
seq: match.seq,
|
|
176
187
|
role: match.role,
|
|
177
|
-
content:
|
|
178
|
-
truncated: start > 0 ||
|
|
188
|
+
content: content.slice(start, start + 800),
|
|
189
|
+
truncated: start > 0 || content.length > start + 800
|
|
179
190
|
};
|
|
180
191
|
})
|
|
181
192
|
}));
|
|
@@ -208,6 +219,11 @@ function createSessionSearchTool(deps) {
|
|
|
208
219
|
};
|
|
209
220
|
}
|
|
210
221
|
const summaries = await Promise.all(top.map(async ({ key, score }) => {
|
|
222
|
+
if ([...protectedEntries].some((entry) => entry.startsWith(`${key}:`))) return {
|
|
223
|
+
conversationId: key,
|
|
224
|
+
score,
|
|
225
|
+
summary: FORGOTTEN_HISTORY_EXCERPT
|
|
226
|
+
};
|
|
211
227
|
let messages = await store.load(key);
|
|
212
228
|
if (p.roleFilter) messages = messages.filter((m) => m.role === p.roleFilter);
|
|
213
229
|
return {
|
|
@@ -88,6 +88,13 @@ import { ChatPreviewService } from "../chat-previews/service.js";
|
|
|
88
88
|
import "../chat-previews/index.js";
|
|
89
89
|
import { configureGatewayHttpProxy } from "../infra/http-proxy-env.js";
|
|
90
90
|
import "../config/index.js";
|
|
91
|
+
import { ManagedComposioEventPoller } from "../connectors/composio-managed-events.js";
|
|
92
|
+
import { startConnectedKnowledgeCoordinator } from "../knowledge/coordinator.js";
|
|
93
|
+
import "../knowledge/index.js";
|
|
94
|
+
import { PersonalReplyComposer } from "../personal-agent/reply-composer.js";
|
|
95
|
+
import { ConnectionRecoveryService } from "../connectors/connection-recovery-service.js";
|
|
96
|
+
import { startConnectorLearningCoordinator } from "../connectors/learning-coordinator.js";
|
|
97
|
+
import { startConnectorSupervisor } from "../connectors/supervisor.js";
|
|
91
98
|
import { DiscussionService } from "../discussions/service.js";
|
|
92
99
|
import { DiscussionOrganizer } from "../discussions/organizer.js";
|
|
93
100
|
import { DiscussionLiveWorker } from "../discussions/live-worker.js";
|
|
@@ -96,13 +103,10 @@ import { DiscussionSealer } from "../discussions/sealer.js";
|
|
|
96
103
|
import "../discussions/index.js";
|
|
97
104
|
import { EndpointToolRuntime } from "../endpoint-tools/runtime.js";
|
|
98
105
|
import "../endpoint-tools/index.js";
|
|
99
|
-
import {
|
|
100
|
-
import {
|
|
101
|
-
import "../
|
|
102
|
-
import {
|
|
103
|
-
import { ConnectionRecoveryService } from "../connectors/connection-recovery-service.js";
|
|
104
|
-
import { startConnectorLearningCoordinator } from "../connectors/learning-coordinator.js";
|
|
105
|
-
import { startConnectorSupervisor } from "../connectors/supervisor.js";
|
|
106
|
+
import { HomeCapabilityPreflightService } from "../home-intelligence/capability-preflight.js";
|
|
107
|
+
import { HomeAdviceGenerator } from "../home-intelligence/generator.js";
|
|
108
|
+
import { HomeIntelligenceHost } from "../home-intelligence/host.js";
|
|
109
|
+
import { HomeSnapshotBuilder } from "../home-intelligence/snapshot.js";
|
|
106
110
|
import { assertGatewayAuthConfigured, extractToken, resolveGatewayAuth, validateToken } from "./auth.js";
|
|
107
111
|
import { prepareAutomationAgentSession } from "./automation-agent-session.js";
|
|
108
112
|
import { stopTailscaleExposure } from "./tailscale-lifecycle.js";
|
|
@@ -163,10 +167,6 @@ import { TaskMainUpdateDecisionService } from "../tasks/task-main-update-decisio
|
|
|
163
167
|
import { selectTaskMainUpdateAttempt, submitAndConfirmTaskMainUpdate } from "../tasks/task-main-update-input.js";
|
|
164
168
|
import { checkAppContextAccess, prepareAppContext } from "./service/app-context-access.js";
|
|
165
169
|
import { applyAutomaticVoiceLanguage, inferProductLanguageFromEnvironment, initializeVoiceDefaults } from "../voice/language-profile.js";
|
|
166
|
-
import { HomeAdviceGenerator } from "../home-intelligence/generator.js";
|
|
167
|
-
import { HomeIntelligenceHost } from "../home-intelligence/host.js";
|
|
168
|
-
import { HomeSnapshotBuilder } from "../home-intelligence/snapshot.js";
|
|
169
|
-
import { HomeCapabilityPreflightService } from "../home-intelligence/capability-preflight.js";
|
|
170
170
|
import crypto from "node:crypto";
|
|
171
171
|
//#region src/gateway/service.ts
|
|
172
172
|
init_session_repository();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createLogger } from "../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
|
-
import { HomeAdviceBudgetExceededError, isHomeProseLocalized } from "./generator.js";
|
|
4
3
|
import { homePatternKey } from "./strategy.js";
|
|
5
|
-
import { HomeAdvicePolicy } from "./policy.js";
|
|
6
4
|
import { HomeIntelligenceRepository } from "./repository.js";
|
|
7
5
|
import { HomeOpportunityApplicationService } from "./application-service.js";
|
|
6
|
+
import { HomeAdvicePolicy } from "./policy.js";
|
|
7
|
+
import { HomeAdviceBudgetExceededError, isHomeProseLocalized } from "./generator.js";
|
|
8
8
|
import { buildHomeOpportunityNotification } from "./notification.js";
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
10
10
|
//#region src/home-intelligence/host.ts
|
|
@@ -83,5 +83,7 @@ export declare function searchKnowledgeItems(input: {
|
|
|
83
83
|
}): KnowledgeItem[];
|
|
84
84
|
export declare function classifyKnowledgeContentSource(item: KnowledgeItem): KnowledgeContentSource;
|
|
85
85
|
export declare function knowledgeItemAllowed(item: KnowledgeItem, policy: KnowledgeReadPolicy): boolean;
|
|
86
|
-
export declare function deleteKnowledgeItem(id: string, now?: number
|
|
86
|
+
export declare function deleteKnowledgeItem(id: string, now?: number, options?: {
|
|
87
|
+
includeRelated?: boolean;
|
|
88
|
+
}): boolean;
|
|
87
89
|
export declare function isKnowledgeCurrent(item: KnowledgeItem, asOf?: number): boolean;
|
|
@@ -350,7 +350,7 @@ function knowledgeFingerprints(input) {
|
|
|
350
350
|
input.content
|
|
351
351
|
])];
|
|
352
352
|
}
|
|
353
|
-
function deleteKnowledgeItem(id, now = Date.now()) {
|
|
353
|
+
function deleteKnowledgeItem(id, now = Date.now(), options = {}) {
|
|
354
354
|
return runSqliteWriteTransaction((db) => {
|
|
355
355
|
const rows = db.prepare(`WITH RECURSIVE deleted(id) AS (
|
|
356
356
|
SELECT knowledge_id FROM knowledge_items WHERE knowledge_id = ?
|
|
@@ -358,7 +358,8 @@ function deleteKnowledgeItem(id, now = Date.now()) {
|
|
|
358
358
|
ON json_extract(k.source_json, '$.episodeKnowledgeId') = d.id
|
|
359
359
|
OR k.knowledge_id = (SELECT json_extract(source_json, '$.episodeKnowledgeId')
|
|
360
360
|
FROM knowledge_items WHERE knowledge_id = d.id)
|
|
361
|
-
|
|
361
|
+
WHERE ?
|
|
362
|
+
) SELECT * FROM knowledge_items WHERE knowledge_id IN (SELECT id FROM deleted)`).all(id, options.includeRelated === false ? 0 : 1);
|
|
362
363
|
for (const row of rows) {
|
|
363
364
|
const item = fromRow(row);
|
|
364
365
|
suppressMemory(db, knowledgeFingerprints(item), now);
|
|
@@ -375,7 +376,13 @@ function isKnowledgeCurrent(item, asOf = Date.now()) {
|
|
|
375
376
|
function writeEvidence(db, id, input) {
|
|
376
377
|
db.prepare("UPDATE knowledge_items SET claim_mode = ?, verification = ?, sensitivity = ? WHERE knowledge_id = ?").run(input.claimMode ?? (input.originClass === "owner" ? "explicit" : input.originClass === "untrusted" ? "reported" : "observed"), input.verification ?? (input.originClass === "untrusted" && (input.recordClass ?? "memory") === "memory" ? "tentative" : "supported"), input.sensitivity ?? inferMemorySensitivity(input.content), id);
|
|
377
378
|
db.prepare("DELETE FROM knowledge_evidence_links WHERE knowledge_id = ?").run(id);
|
|
378
|
-
|
|
379
|
+
const links = /* @__PURE__ */ new Map();
|
|
380
|
+
for (const evidence of input.evidence ?? []) {
|
|
381
|
+
const existing = links.get(evidence.id);
|
|
382
|
+
if (existing && (existing.contentHash !== evidence.contentHash || existing.consentReceiptId !== evidence.consentReceiptId)) throw new Error("Conflicting provenance for the same knowledge evidence.");
|
|
383
|
+
links.set(evidence.id, evidence);
|
|
384
|
+
}
|
|
385
|
+
for (const evidence of links.values()) db.prepare("INSERT INTO knowledge_evidence_links(knowledge_id, evidence_id, content_hash, consent_receipt_id) VALUES (?, ?, ?, ?)").run(id, evidence.id, evidence.contentHash ?? null, evidence.consentReceiptId ?? null);
|
|
379
386
|
}
|
|
380
387
|
//#endregion
|
|
381
388
|
export { KnowledgeReviewConflictError, classifyKnowledgeContentSource, deleteKnowledgeItem, getKnowledgeItem, isKnowledgeCurrent, knowledgeItemAllowed, listKnowledgeItems, listKnowledgeStatusEvents, reviewKnowledgeItem, searchKnowledgeItems, transitionKnowledgeStatus, writeKnowledgeItem };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CaptureScopeContext } from '../user-model/capture/executor.js';
|
|
2
|
+
import type { CaptureEvidence, UserModelInterpretation } from '../user-model/capture/semantic.js';
|
|
3
|
+
/** Only grounded owner requests can forget derived knowledge visible in this scope. */
|
|
4
|
+
export declare function applyKnowledgeDeletions(input: {
|
|
5
|
+
interpretation: UserModelInterpretation;
|
|
6
|
+
evidence: CaptureEvidence[];
|
|
7
|
+
context: CaptureScopeContext;
|
|
8
|
+
allowedTargetIds: string[];
|
|
9
|
+
}): Array<{
|
|
10
|
+
candidateKey: string;
|
|
11
|
+
objectType: 'knowledge';
|
|
12
|
+
objectId: string;
|
|
13
|
+
outcome: 'created' | 'rejected';
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { init_transaction, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
|
|
4
|
+
import { createCollaborationRule, init_collaboration_rule_repository, listCollaborationRules } from "../storage/sqlite/collaboration-rule-repository.js";
|
|
5
|
+
import { canUseKnowledge } from "../knowledge-memory/usage-policy.js";
|
|
6
|
+
import { deleteKnowledgeItem, getKnowledgeItem, writeKnowledgeItem } from "../knowledge-memory/repository.js";
|
|
7
|
+
import "../knowledge-memory/index.js";
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
|
+
//#region src/memory-learning/forget-capture.ts
|
|
10
|
+
init_logger();
|
|
11
|
+
init_transaction();
|
|
12
|
+
init_collaboration_rule_repository();
|
|
13
|
+
const log = createLogger("MemoryLearning:Forget");
|
|
14
|
+
/** Only grounded owner requests can forget derived knowledge visible in this scope. */
|
|
15
|
+
function applyKnowledgeDeletions(input) {
|
|
16
|
+
return runSqliteWriteTransaction((db) => {
|
|
17
|
+
const outputs = [];
|
|
18
|
+
for (const operation of input.interpretation.knowledgeDeletions ?? []) {
|
|
19
|
+
if (!operation.evidenceRefs.length || operation.evidenceRefs.some((ref) => !input.evidence.some((entry) => entry.ref === ref && entry.role === "user"))) continue;
|
|
20
|
+
if (!operation.evidence?.length || operation.evidence.some((claim) => !input.evidence.some((entry) => entry.ref === claim.ref && entry.role === "user" && entry.text.normalize("NFKC").toLowerCase().includes(claim.quote.normalize("NFKC").toLowerCase())))) continue;
|
|
21
|
+
let targets = operation.targetKnowledgeIds.flatMap((id) => {
|
|
22
|
+
const item = input.allowedTargetIds.includes(id) ? getKnowledgeItem(id) : void 0;
|
|
23
|
+
return item && item.recordClass === "memory" && canUseKnowledge(item, input.context) ? [item] : [];
|
|
24
|
+
});
|
|
25
|
+
const requestedRetention = operation.retained ?? [];
|
|
26
|
+
const invalidTargets = new Set(requestedRetention.filter((part) => {
|
|
27
|
+
const item = targets.find((target) => target.id === part.targetKnowledgeId);
|
|
28
|
+
return !item || !part.spans.length || part.spans.some((span) => !span.trim() || !item.content.includes(span));
|
|
29
|
+
}).map((part) => part.targetKnowledgeId));
|
|
30
|
+
if (invalidTargets.size) log.warn({
|
|
31
|
+
phase: "forget_retention",
|
|
32
|
+
count: invalidTargets.size,
|
|
33
|
+
conversationId: input.context.conversationId
|
|
34
|
+
}, "Ungrounded retained spans rejected; affected knowledge was preserved");
|
|
35
|
+
targets = targets.filter((item) => !invalidTargets.has(item.id));
|
|
36
|
+
const retained = requestedRetention.filter((part) => targets.some((item) => item.id === part.targetKnowledgeId));
|
|
37
|
+
for (const part of retained) {
|
|
38
|
+
const item = targets.find((target) => target.id === part.targetKnowledgeId);
|
|
39
|
+
const content = [...new Set(part.spans)].join(";");
|
|
40
|
+
const links = db.prepare(`SELECT evidence_id, content_hash, consent_receipt_id FROM knowledge_evidence_links
|
|
41
|
+
WHERE knowledge_id = ?`).all(item.id);
|
|
42
|
+
const saved = writeKnowledgeItem({
|
|
43
|
+
...item,
|
|
44
|
+
content,
|
|
45
|
+
canonicalKey: `${item.canonicalKey}:retained:${createHash("sha256").update(content).digest("hex").slice(0, 16)}`,
|
|
46
|
+
evidence: links.map((link) => ({
|
|
47
|
+
id: link.evidence_id,
|
|
48
|
+
contentHash: link.content_hash ?? void 0,
|
|
49
|
+
consentReceiptId: link.consent_receipt_id ?? void 0
|
|
50
|
+
})),
|
|
51
|
+
source: {
|
|
52
|
+
...item.source,
|
|
53
|
+
evidenceQuotes: void 0,
|
|
54
|
+
retainedFromKnowledgeId: item.id
|
|
55
|
+
},
|
|
56
|
+
replaceExisting: false
|
|
57
|
+
});
|
|
58
|
+
if (!saved.item) throw new Error("Unable to retain unforgotten knowledge; deletion was rolled back.");
|
|
59
|
+
outputs.push({
|
|
60
|
+
candidateKey: saved.item.canonicalKey,
|
|
61
|
+
objectType: "knowledge",
|
|
62
|
+
objectId: saved.item.id,
|
|
63
|
+
outcome: "created"
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const forgottenEntries = targets.flatMap((item) => db.prepare(`SELECT DISTINCT e.conversation_id, e.message_id
|
|
67
|
+
FROM context_evidence e WHERE e.conversation_id IS NOT NULL AND e.message_id IS NOT NULL
|
|
68
|
+
AND (e.evidence_id IN (SELECT evidence_id FROM knowledge_evidence_links WHERE knowledge_id=?)
|
|
69
|
+
OR e.message_id IN (SELECT value FROM knowledge_revisions r, json_each(r.source_json, '$.evidenceRefs')
|
|
70
|
+
WHERE r.knowledge_id=?))`).all(item.id, item.id).flatMap((entry) => [`${entry.conversation_id}:${entry.message_id}`, `${entry.conversation_id}:*`]));
|
|
71
|
+
for (const item of targets) {
|
|
72
|
+
deleteKnowledgeItem(item.id, Date.now(), { includeRelated: false });
|
|
73
|
+
outputs.push({
|
|
74
|
+
candidateKey: item.id,
|
|
75
|
+
objectType: "knowledge",
|
|
76
|
+
objectId: item.id,
|
|
77
|
+
outcome: "rejected"
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
for (const scope of new Map(targets.map((item) => [JSON.stringify(item.scope), item.scope])).values()) {
|
|
81
|
+
if (scope.type === "agent") continue;
|
|
82
|
+
if (!listCollaborationRules().some((rule) => rule.category === "boundary" && rule.status === "active" && JSON.stringify(rule.scope) === JSON.stringify(scope) && rule.statement === operation.boundary && forgottenEntries.every((entry) => Array.isArray(rule.conditions.forgottenHistoryEntries) && rule.conditions.forgottenHistoryEntries.includes(entry)))) createCollaborationRule({
|
|
83
|
+
category: "boundary",
|
|
84
|
+
priority: 0,
|
|
85
|
+
scope,
|
|
86
|
+
conditions: {
|
|
87
|
+
enforcementLevel: "prompt",
|
|
88
|
+
forgottenHistoryEntries: [...new Set(forgottenEntries)],
|
|
89
|
+
reread: operation.reread
|
|
90
|
+
},
|
|
91
|
+
statement: operation.boundary,
|
|
92
|
+
status: "active"
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return outputs;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
export { applyKnowledgeDeletions };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CaptureScopeContext } from '../user-model/capture/executor.js';
|
|
2
|
+
/** A later source-specific read request does not authorize saving forgotten facts again. */
|
|
3
|
+
export declare function explicitlyReadsOriginalHistory(text: string): boolean;
|
|
4
|
+
export declare function forgottenHistoryEntries(context: CaptureScopeContext, userText?: string): Set<string>;
|
|
5
|
+
export declare const FORGOTTEN_HISTORY_EXCERPT = "[This entry supported information the user asked to forget. Automatic recall is withheld. A later explicit request to re-read original records is required; do not infer the hidden values.]";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isXopcDatabaseOpen } from "../storage/sqlite/connection.js";
|
|
2
|
+
import { init_collaboration_rule_repository, listCollaborationRules } from "../storage/sqlite/collaboration-rule-repository.js";
|
|
3
|
+
import { init_sqlite } from "../storage/sqlite/index.js";
|
|
4
|
+
//#region src/memory-learning/forgotten-history.ts
|
|
5
|
+
init_sqlite();
|
|
6
|
+
init_collaboration_rule_repository();
|
|
7
|
+
/** A later source-specific read request does not authorize saving forgotten facts again. */
|
|
8
|
+
function explicitlyReadsOriginalHistory(text) {
|
|
9
|
+
if (/(?:引文|引用|改写|措辞|不是要|quoted|rephrase)/iu.test(text)) return false;
|
|
10
|
+
if (/(?:不要|别|不必|禁止)\s*(?:再|去)?(?:恢复|查阅|查出|读取|找回|查)|(?:do not|don't)\s+(?:read|look|recover|retrieve|restore)/iu.test(text)) return false;
|
|
11
|
+
return /(?:原始|旧|先前|过去).{0,8}(?:记录|对话|原文)|(?:original|old|previous|past).{0,20}(?:record|conversation|transcript|history)/iu.test(text) && /(?:^|\n)\s*(?:(?:请|麻烦|本次|这次|现在|please|this time)[,,\s]*){0,3}[^。\n!?]{0,150}(?:恢复|查阅|查出|读取|找回|read|look up|retrieve|recover|restore)/iu.test(text);
|
|
12
|
+
}
|
|
13
|
+
function forgottenHistoryEntries(context, userText = "") {
|
|
14
|
+
if (!isXopcDatabaseOpen()) return /* @__PURE__ */ new Set();
|
|
15
|
+
const entries = /* @__PURE__ */ new Set();
|
|
16
|
+
for (const rule of listCollaborationRules()) {
|
|
17
|
+
if (rule.status !== "active" || rule.category !== "boundary") continue;
|
|
18
|
+
if (!(rule.scope.type === "global" || rule.scope.id === {
|
|
19
|
+
agent: context.agentId,
|
|
20
|
+
workspace: context.workspaceId,
|
|
21
|
+
project: context.projectId,
|
|
22
|
+
session: context.conversationId
|
|
23
|
+
}[rule.scope.type]) || !Array.isArray(rule.conditions.forgottenHistoryEntries)) continue;
|
|
24
|
+
const binding = rule.conditions.reread;
|
|
25
|
+
const property = binding?.property === "date_time" ? /日期|时间|哪天|哪日|几点|日程|\b(?:date|time|when|schedule)\b/iu : binding?.property === "price" ? /票价|报价|价格|总价|金额|费用|花费|价钱|\b(?:price|cost|amount)\b/iu : void 0;
|
|
26
|
+
if (typeof binding?.subject === "string" && binding.subject.length > 0 && property && userText.split(/[。;;,,!?!?\n]/u).some((clause) => explicitlyReadsOriginalHistory(clause) && clause.normalize("NFKC").toLowerCase().includes(binding.subject.normalize("NFKC").toLowerCase()) && property.test(clause))) continue;
|
|
27
|
+
for (const entry of rule.conditions.forgottenHistoryEntries) if (typeof entry === "string") entries.add(entry);
|
|
28
|
+
}
|
|
29
|
+
return entries;
|
|
30
|
+
}
|
|
31
|
+
const FORGOTTEN_HISTORY_EXCERPT = "[This entry supported information the user asked to forget. Automatic recall is withheld. A later explicit request to re-read original records is required; do not infer the hidden values.]";
|
|
32
|
+
//#endregion
|
|
33
|
+
export { FORGOTTEN_HISTORY_EXCERPT, explicitlyReadsOriginalHistory, forgottenHistoryEntries };
|
|
@@ -112,7 +112,8 @@ function applyWorkCandidates(input) {
|
|
|
112
112
|
replaceExisting: true,
|
|
113
113
|
source: {
|
|
114
114
|
extractor: "work-synthesis",
|
|
115
|
-
evidenceRefs: candidate.evidence.map((claim) => claim.ref),
|
|
115
|
+
evidenceRefs: [...new Set(candidate.evidence.map((claim) => claim.ref))],
|
|
116
|
+
evidenceQuotes: candidate.evidence,
|
|
116
117
|
...input.source ? {
|
|
117
118
|
sourceInstanceId: input.source.instanceId,
|
|
118
119
|
sourceItemId: input.source.itemId,
|
|
@@ -209,6 +209,7 @@ export declare class SessionStore {
|
|
|
209
209
|
limit?: number;
|
|
210
210
|
beforeSeq?: number;
|
|
211
211
|
orderBySeq?: boolean;
|
|
212
|
+
matchMode?: 'keyword' | 'phrase';
|
|
212
213
|
}): import("../storage/sqlite/transcript-repository.js").SessionTranscriptRecallMatch[];
|
|
213
214
|
exportSession(key: string, format: ExportFormat): Promise<string>;
|
|
214
215
|
importSessionExport(targetKey: string, jsonContent: string): Promise<{
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/** Quote a user query for FTS5 phrase/prefix-safe matching. */
|
|
2
2
|
export declare function escapeFts5Query(raw: string): string;
|
|
3
3
|
/** Build a syntax-safe, recall-oriented query for natural-language memory search. */
|
|
4
|
+
export declare function sessionSearchTerms(raw: string): string[];
|
|
4
5
|
export declare function buildFts5SearchQuery(raw: string): string;
|
|
6
|
+
/** unicode61 does not split Han words; use literal substrings for those terms. */
|
|
7
|
+
export declare function sessionHanSearchTerms(raw: string): string[];
|
|
8
|
+
export declare function escapeSqlLike(raw: string): string;
|
|
5
9
|
/** Normalize FTS5 BM25 ranks, where a more-negative rank is a better match. */
|
|
6
10
|
export declare function fts5RankToScore(rank: number, bestRank: number, worstRank: number): number;
|