@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.
Files changed (117) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-lMrejpsz.js → agent-avatar-dicebear-ColV_Z1C.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-lXwR3Rff.js → agent-defaults-settings-panel-rgRXzBCX.js} +1 -1
  5. package/dist/gateway/static/root/assets/{agents-DFwBf95X.js → agents-C1rSl_yE.js} +3 -3
  6. package/dist/gateway/static/root/assets/{app-management-settings-panel-Cja3eV7H.js → app-management-settings-panel-B-Cl1lzt.js} +1 -1
  7. package/dist/gateway/static/root/assets/{appearance-settings-Dzhmj_ZO.js → appearance-settings-byEnJz9H.js} +1 -1
  8. package/dist/gateway/static/root/assets/{apps-page-C3xbCdYa.js → apps-page-BfVMfjkC.js} +1 -1
  9. package/dist/gateway/static/root/assets/{archive-plugin-D4r4P5uG.js → archive-plugin-BXssEuq3.js} +1 -1
  10. package/dist/gateway/static/root/assets/{automations-page-BlNe3DXU.js → automations-page-C-puATkS.js} +1 -1
  11. package/dist/gateway/static/root/assets/{automations-workspace-CWvHbJBK.js → automations-workspace-b7MTHd-X.js} +1 -1
  12. package/dist/gateway/static/root/assets/{binary-plugins-DuN5L6lm.js → binary-plugins-DBz_2AhG.js} +1 -1
  13. package/dist/gateway/static/root/assets/{block-editor-DUSVn51h.js → block-editor-BOO5kjIl.js} +1 -1
  14. package/dist/gateway/static/root/assets/{browser-automations-page-BtvOURHQ.js → browser-automations-page-DMqDOJgO.js} +1 -1
  15. package/dist/gateway/static/root/assets/{browser-settings-page-C_OeE79S.js → browser-settings-page-KZStwhX7.js} +1 -1
  16. package/dist/gateway/static/root/assets/{capabilities-page-BKF6q_tC.js → capabilities-page-DeJuO2rf.js} +2 -2
  17. package/dist/gateway/static/root/assets/{capabilities-settings-panel-B7qOaUZs.js → capabilities-settings-panel-DPxL_3Km.js} +1 -1
  18. package/dist/gateway/static/root/assets/{capability-header-actions-oBPqUlbj.js → capability-header-actions-yfPUz5Wt.js} +1 -1
  19. package/dist/gateway/static/root/assets/{channels-3M9-anCN.js → channels-Cp4F9zPz.js} +1 -1
  20. package/dist/gateway/static/root/assets/{chat-terminal-dock-BLs3NaOP.js → chat-terminal-dock-Dlzn80cK.js} +1 -1
  21. package/dist/gateway/static/root/assets/{computer-settings-page-LaekhDG6.js → computer-settings-page-BXhmUWbA.js} +1 -1
  22. package/dist/gateway/static/root/assets/{connector-service-page-DJP1n1_n.js → connector-service-page-B1oJCj74.js} +1 -1
  23. package/dist/gateway/static/root/assets/{connectors-page-DmQqMy6l.js → connectors-page-Da_M7h3m.js} +1 -1
  24. package/dist/gateway/static/root/assets/{date-picker-DVlbwrYg.js → date-picker-DGjpqOKx.js} +1 -1
  25. package/dist/gateway/static/root/assets/{dependency-picker-BhElpD_6.js → dependency-picker-BfWUD3LG.js} +1 -1
  26. package/dist/gateway/static/root/assets/{desktop-pet-ByvV-cDD.js → desktop-pet-CPEi55Vt.js} +1 -1
  27. package/dist/gateway/static/root/assets/{desktop-pet-settings-BBruQu-_.js → desktop-pet-settings-CmQ_H-nM.js} +1 -1
  28. package/dist/gateway/static/root/assets/{device-pairing-wizard-C3zOy8PO.js → device-pairing-wizard-BeTjXc3C.js} +1 -1
  29. package/dist/gateway/static/root/assets/{directory-picker-path-field-QZ0p-uNa.js → directory-picker-path-field-CwT1LcLR.js} +1 -1
  30. package/dist/gateway/static/root/assets/{extension-debug-page-DoVPUHu_.js → extension-debug-page-DX-hX_d0.js} +1 -1
  31. package/dist/gateway/static/root/assets/{extension-page-BNe8n3iJ.js → extension-page-Cji87mN9.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-settings-page-DxNpjGJD.js → extension-settings-page-BulzpwBD.js} +1 -1
  33. package/dist/gateway/static/root/assets/{gateway-settings-CzrFZ0lB.js → gateway-settings-CIdEroWr.js} +1 -1
  34. package/dist/gateway/static/root/assets/{highlighted-body-KQOG7T2V-DrZq5KTj.js → highlighted-body-KQOG7T2V-BgTqMa_d.js} +1 -1
  35. package/dist/gateway/static/root/assets/{home-page-cXmd8x0S.js → home-page-BSbYrVda.js} +1 -1
  36. package/dist/gateway/static/root/assets/{image-generation-api-C6su4n9B.js → image-generation-api-BfPYnMoc.js} +1 -1
  37. package/dist/gateway/static/root/assets/{imports-page-CsXYryWS.js → imports-page-DYSOfHfw.js} +1 -1
  38. package/dist/gateway/static/root/assets/{index-uAJlrGvo.js → index-D2hEKnTM.js} +116 -116
  39. package/dist/gateway/static/root/assets/index-SmKNPEmJ.css +1 -0
  40. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-GYKLt0oF.js → keyboard-shortcuts-settings-BwsGM6Yd.js} +1 -1
  41. package/dist/gateway/static/root/assets/{local-app-workbench-page-Dw-i6VkX.js → local-app-workbench-page-K6UR6cAx.js} +1 -1
  42. package/dist/gateway/static/root/assets/{local-apps-page-DslilwNE.js → local-apps-page-gkdwtjDE.js} +1 -1
  43. package/dist/gateway/static/root/assets/{logs-page-4N8bGPIE.js → logs-page-BAQNYk0e.js} +1 -1
  44. package/dist/gateway/static/root/assets/{management-settings-CPmxI1k0.js → management-settings-CMVRLGox.js} +1 -1
  45. package/dist/gateway/static/root/assets/{media-plugins-s6EqtAUQ.js → media-plugins-DQJXvbr_.js} +1 -1
  46. package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-Cx0bXBqu.js +1 -0
  47. package/dist/gateway/static/root/assets/{note-detail-panel-CjTm5gxS.js → note-detail-panel-1GCqp26U.js} +3 -3
  48. package/dist/gateway/static/root/assets/note-image-lightbox-B7RA-WsY.js +1 -0
  49. package/dist/gateway/static/root/assets/{notes-page-DkESStKc.js → notes-page-DnjlEdzM.js} +1 -1
  50. package/dist/gateway/static/root/assets/{page-context-capture-button-eyzTNrCe.js → page-context-capture-button-BAwI_3uh.js} +1 -1
  51. package/dist/gateway/static/root/assets/{personal-page-DZxbFQvF.js → personal-page-XoLrcR0U.js} +1 -1
  52. package/dist/gateway/static/root/assets/{plugin-details-dialog-BjVLbxZM.js → plugin-details-dialog-BjzIp3T5.js} +1 -1
  53. package/dist/gateway/static/root/assets/{preview-open-alternatives-7pUtUL4O.js → preview-open-alternatives-2YjwlRHu.js} +1 -1
  54. package/dist/gateway/static/root/assets/{product-open-page-BPWSqtRt.js → product-open-page-Bfqez8Je.js} +1 -1
  55. package/dist/gateway/static/root/assets/{product-preview-content-COo4zwp9.js → product-preview-content-D7wLwrqj.js} +1 -1
  56. package/dist/gateway/static/root/assets/{project-detail-page-CrIH6nEy.js → project-detail-page-1ueRCmXg.js} +1 -1
  57. package/dist/gateway/static/root/assets/{projects-page-D4swvBmH.js → projects-page-CDQycMPo.js} +1 -1
  58. package/dist/gateway/static/root/assets/{remote-access-hub-DltfIMtv.js → remote-access-hub-YvVmZA4K.js} +1 -1
  59. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-CNCx2RnO.js → runtime-tools-settings-panel-DSsANtha.js} +1 -1
  60. package/dist/gateway/static/root/assets/{schema-form-nRDA2XfI.js → schema-form-vbhv4JE6.js} +1 -1
  61. package/dist/gateway/static/root/assets/{sessions-page-CfYU53aw.js → sessions-page-B3rPljgr.js} +1 -1
  62. package/dist/gateway/static/root/assets/settings-advanced-gate-Ch20Ptzp.js +1 -0
  63. package/dist/gateway/static/root/assets/{settings-form-section-_ALUREPr.js → settings-form-section-4nn0sb6v.js} +1 -1
  64. package/dist/gateway/static/root/assets/{settings-page-29ptuMYZ.js → settings-page-_HB9FC08.js} +1 -1
  65. package/dist/gateway/static/root/assets/{setup-status-panel-DZgVMRXr.js → setup-status-panel-L1HHrvzi.js} +1 -1
  66. package/dist/gateway/static/root/assets/{share-preview-page-C2LHd_Nl.js → share-preview-page-C4lC0W3d.js} +1 -1
  67. package/dist/gateway/static/root/assets/{shares-settings-C5MTc6t-.js → shares-settings-DtHZ9z5K.js} +1 -1
  68. package/dist/gateway/static/root/assets/{skill-api-C-T0K7Oo.js → skill-api-CyO89DKl.js} +1 -1
  69. package/dist/gateway/static/root/assets/{skill-reload-api-BcdA9ezU.js → skill-reload-api-Drg356Pb.js} +1 -1
  70. package/dist/gateway/static/root/assets/{skills-page-BMw8XbKL.js → skills-page-XhCLHKlK.js} +1 -1
  71. package/dist/gateway/static/root/assets/{style-DR0-RKaL.js → style-DTLpCnuA.js} +1 -1
  72. package/dist/gateway/static/root/assets/{system-settings-panel-2Kr9Y-FJ.js → system-settings-panel-bSogz4S-.js} +1 -1
  73. package/dist/gateway/static/root/assets/{task-detail-page-7kPco8Ys.js → task-detail-page-CSv2x4q-.js} +1 -1
  74. package/dist/gateway/static/root/assets/{tracing-settings-page-D53wxq60.js → tracing-settings-page-CZ5XLQmX.js} +1 -1
  75. package/dist/gateway/static/root/assets/{tunnel-api-DcDUSAjv.js → tunnel-api-DOUyF9Ew.js} +1 -1
  76. package/dist/gateway/static/root/assets/{usage-page-Hut0SDqn.js → usage-page-Bv3fdfw1.js} +1 -1
  77. package/dist/gateway/static/root/assets/{use-autosave-C4TUlpsu.js → use-autosave-D0MhLMD2.js} +1 -1
  78. package/dist/gateway/static/root/assets/{user-model-page-KRy50OBd.js → user-model-page-BD7WvhI0.js} +1 -1
  79. package/dist/gateway/static/root/assets/{voice-api-key-field-CMdihbPb.js → voice-api-key-field-Ds_ADBQH.js} +1 -1
  80. package/dist/gateway/static/root/assets/{work-discovery-overlay-Bd9HLsds.js → work-discovery-overlay-CDJp2_l2.js} +1 -1
  81. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-C9mAiHBd.js → workflow-run-setup-panel-CR4fRhZZ.js} +1 -1
  82. package/dist/gateway/static/root/assets/{workflows-page-CGg4GWwt.js → workflows-page-BeJw5liC.js} +1 -1
  83. package/dist/gateway/static/root/assets/{xopc-cloud-account-card-ClQKM2RA.js → xopc-cloud-account-card-Di-oEpCt.js} +1 -1
  84. package/dist/gateway/static/root/index.html +2 -2
  85. package/dist/package.js +1 -1
  86. package/dist/src/agent/background-review/prompts.d.ts +8 -0
  87. package/dist/src/agent/background-review/prompts.js +9 -2
  88. package/dist/src/agent/background-review/run-background-review.js +73 -11
  89. package/dist/src/agent/inbound/attachment-pipeline.d.ts +2 -0
  90. package/dist/src/agent/inbound/attachment-pipeline.js +10 -5
  91. package/dist/src/agent/tools/factory.js +15 -1
  92. package/dist/src/agent/tools/session-recall-tool.d.ts +3 -0
  93. package/dist/src/agent/tools/session-recall-tool.js +3 -0
  94. package/dist/src/agent/tools/session-search-tool.d.ts +3 -0
  95. package/dist/src/agent/tools/session-search-tool.js +20 -4
  96. package/dist/src/gateway/service.js +11 -11
  97. package/dist/src/home-intelligence/host.js +2 -2
  98. package/dist/src/knowledge-memory/repository.d.ts +3 -1
  99. package/dist/src/knowledge-memory/repository.js +10 -3
  100. package/dist/src/memory-learning/forget-capture.d.ts +14 -0
  101. package/dist/src/memory-learning/forget-capture.js +100 -0
  102. package/dist/src/memory-learning/forgotten-history.d.ts +5 -0
  103. package/dist/src/memory-learning/forgotten-history.js +33 -0
  104. package/dist/src/memory-learning/work-capture.js +2 -1
  105. package/dist/src/session/store.d.ts +1 -0
  106. package/dist/src/storage/sqlite/fts.d.ts +4 -0
  107. package/dist/src/storage/sqlite/fts.js +13 -4
  108. package/dist/src/storage/sqlite/session-repository.js +6 -3
  109. package/dist/src/storage/sqlite/transcript-repository.d.ts +1 -0
  110. package/dist/src/storage/sqlite/transcript-repository.js +14 -12
  111. package/dist/src/user-model/capture/semantic.d.ts +18 -1
  112. package/dist/src/user-model/capture/semantic.js +26 -1
  113. package/package.json +3 -3
  114. package/dist/gateway/static/root/assets/index-BZlryZm8.css +0 -1
  115. package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-Ba-WQCki.js +0 -1
  116. package/dist/gateway/static/root/assets/note-image-lightbox-hbSbEEfN.js +0 -1
  117. 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
- if (Array.isArray(message.metadata?.sourceContexts) && message.metadata.sourceContexts.length > 0) continue;
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 work = access.knowledge && current.userContext.knowledgeMemory.writePolicy === "allow" && access.knowledgePolicy.contentSources.includes("memory") ? applyWorkCandidates({
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: [...result.outputs.map((output) => ({
456
- candidateKey: output.candidateKey,
457
- ...output.assertionId ? {
458
- objectType: "assertion",
459
- objectId: output.assertionId
460
- } : {},
461
- outcome: output.outcome === "rejected" ? "rejected" : output.outcome === "deduplicated" ? "deduplicated" : "created"
462
- })), ...work]
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
- const existing = pendingTranscriptBySession.get(conversationId);
175
- if (!existing) return;
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, { limit: 2 }).filter((match) => !p.roleFilter || match.role === p.roleFilter).map((match) => {
172
- const at = match.content.toLowerCase().indexOf(query.toLowerCase());
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: match.content.slice(start, start + 800),
178
- truncated: start > 0 || match.content.length > start + 800
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 { ManagedComposioEventPoller } from "../connectors/composio-managed-events.js";
100
- import { startConnectedKnowledgeCoordinator } from "../knowledge/coordinator.js";
101
- import "../knowledge/index.js";
102
- import { PersonalReplyComposer } from "../personal-agent/reply-composer.js";
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): boolean;
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
- ) SELECT * FROM knowledge_items WHERE knowledge_id IN (SELECT id FROM deleted)`).all(id);
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
- for (const evidence of input.evidence ?? []) 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
+ 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;