@xopcai/xopc 0.0.228 → 0.0.230

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 (135) 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-CjGZBs4C.js → agent-avatar-dicebear-Bp_wmuGW.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agents-B0NI4_NI.js → agents-CKG93dlc.js} +1 -1
  5. package/dist/gateway/static/root/assets/{app-management-settings-panel--SjycrXk.js → app-management-settings-panel-CwAPK4Jm.js} +1 -1
  6. package/dist/gateway/static/root/assets/{appearance-settings-8G-TVUtg.js → appearance-settings-DsGLvqTb.js} +1 -1
  7. package/dist/gateway/static/root/assets/{apps-page-2NiqD-YP.js → apps-page-Ee9qOQEx.js} +1 -1
  8. package/dist/gateway/static/root/assets/{attachment-load-Bx44zSpz.js → attachment-load-B4qqigAQ.js} +1 -1
  9. package/dist/gateway/static/root/assets/{automations-page-CrKAHqc0.js → automations-page-B8ImehSW.js} +1 -1
  10. package/dist/gateway/static/root/assets/{automations-workspace-Q3g9cf0h.js → automations-workspace-BNFAfNwC.js} +1 -1
  11. package/dist/gateway/static/root/assets/{binary-plugins-DjDwnHAQ.js → binary-plugins-DaJbyYc2.js} +1 -1
  12. package/dist/gateway/static/root/assets/{block-editor-7aMOKoZM.js → block-editor-Kl25XWJc.js} +1 -1
  13. package/dist/gateway/static/root/assets/{browser-settings-page-DTwPkmAC.js → browser-settings-page-Cg6NgwE_.js} +1 -1
  14. package/dist/gateway/static/root/assets/{browser-workflows-page-DVLrWKz_.js → browser-workflows-page-DjMxs33B.js} +1 -1
  15. package/dist/gateway/static/root/assets/{capabilities-settings-panel-YcP4FbN7.js → capabilities-settings-panel-NvJKgF1u.js} +1 -1
  16. package/dist/gateway/static/root/assets/{capability-presets-settings-panel-AcPjHHia.js → capability-presets-settings-panel-C2uBdALe.js} +1 -1
  17. package/dist/gateway/static/root/assets/{channels-settings-Ca94Q1D-.js → channels-settings-CIcYKMEs.js} +1 -1
  18. package/dist/gateway/static/root/assets/{chat-terminal-dock-BR-_7KC0.js → chat-terminal-dock-BiU_4Rq6.js} +1 -1
  19. package/dist/gateway/static/root/assets/{connectors-page-SGxfLTd6.js → connectors-page-CEEcmUiP.js} +1 -1
  20. package/dist/gateway/static/root/assets/{cron-expression-D8-c-Tkw.js → cron-expression-Cs_5pUPT.js} +1 -1
  21. package/dist/gateway/static/root/assets/{date-picker-kV-Jyl9J.js → date-picker-y1kWrFVU.js} +1 -1
  22. package/dist/gateway/static/root/assets/{dependency-picker-CwKcrfGj.js → dependency-picker-skc62zrn.js} +1 -1
  23. package/dist/gateway/static/root/assets/{desktop-pet-settings-CTs5WGzk.js → desktop-pet-settings-BVVCBgYK.js} +1 -1
  24. package/dist/gateway/static/root/assets/{desktop-pet-D9rnchu3.js → desktop-pet-ux9DxchT.js} +1 -1
  25. package/dist/gateway/static/root/assets/{directory-picker-path-field-CCl0aaDm.js → directory-picker-path-field-V8vZP0Dy.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-debug-page-Cau40SvC.js → extension-debug-page-Dh6DDuvQ.js} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-page-BsJXdvIL.js → extension-page-8OKQDEat.js} +1 -1
  28. package/dist/gateway/static/root/assets/{extension-settings-page-qWqJyF7-.js → extension-settings-page-BgSV0Nny.js} +1 -1
  29. package/dist/gateway/static/root/assets/{gateway-config-api-BI0SELXm.js → gateway-config-api-Dlc6eaTG.js} +1 -1
  30. package/dist/gateway/static/root/assets/{gateway-settings-6ZdOIC7F.js → gateway-settings-DqQgiAUv.js} +1 -1
  31. package/dist/gateway/static/root/assets/{heartbeat-settings-KBwhXeFD.js → heartbeat-settings-Pxqk5GFH.js} +1 -1
  32. package/dist/gateway/static/root/assets/{home-page-BA2M4CLi.js → home-page-1xjo6wE4.js} +1 -1
  33. package/dist/gateway/static/root/assets/{index-BX0dz1JK.js → index-Bbd_dXui.js} +17 -17
  34. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-kHtpnyEm.js → keyboard-shortcuts-settings-BSFNqxzY.js} +1 -1
  35. package/dist/gateway/static/root/assets/{local-app-workbench-page-D2nkn09R.js → local-app-workbench-page-heQ4-PJ7.js} +1 -1
  36. package/dist/gateway/static/root/assets/{local-apps-page-BCXa0oUa.js → local-apps-page-DzU6yZ9-.js} +1 -1
  37. package/dist/gateway/static/root/assets/{logs-page-D3sNst0K.js → logs-page-BblnS6Jy.js} +1 -1
  38. package/dist/gateway/static/root/assets/{management-settings-tHnlfOAQ.js → management-settings-B8hM56HS.js} +1 -1
  39. package/dist/gateway/static/root/assets/{markdown-split-BMLqHHr5.js → markdown-split-DRbZjxMK.js} +1 -1
  40. package/dist/gateway/static/root/assets/{media-plugins-DybwqTgv.js → media-plugins-B8vMe5VX.js} +1 -1
  41. package/dist/gateway/static/root/assets/{notes-page-C24SfF6r.js → notes-page-B-YZ7iuK.js} +1 -1
  42. package/dist/gateway/static/root/assets/{notes-workbench-CTdZc3ov.js → notes-workbench-CAM09C8b.js} +2 -2
  43. package/dist/gateway/static/root/assets/{product-open-page-DBfCVlJq.js → product-open-page-DkWcyki5.js} +1 -1
  44. package/dist/gateway/static/root/assets/{project-detail-page-COHaEpbA.js → project-detail-page-BIPIwgU2.js} +1 -1
  45. package/dist/gateway/static/root/assets/{projects-page-lMnST7D_.js → projects-page-B_kzTn1K.js} +1 -1
  46. package/dist/gateway/static/root/assets/{remote-access-hub-BIRKBvO_.js → remote-access-hub-CtD8DsD-.js} +1 -1
  47. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BPEGTip5.js → runtime-tools-settings-panel-Bq_ETeal.js} +1 -1
  48. package/dist/gateway/static/root/assets/{schema-form-Dxlu60Td.js → schema-form-BogTlrsP.js} +1 -1
  49. package/dist/gateway/static/root/assets/{sessions-page-CATO9G3y.js → sessions-page-B8n9HI1y.js} +1 -1
  50. package/dist/gateway/static/root/assets/settings-advanced-gate-xlhdRTME.js +1 -0
  51. package/dist/gateway/static/root/assets/{settings-form-section-ChXh3Bwk.js → settings-form-section-Coy1Cbp6.js} +1 -1
  52. package/dist/gateway/static/root/assets/{settings-loading-skeleton-BCAUqIe2.js → settings-loading-skeleton-B5GU7iCG.js} +1 -1
  53. package/dist/gateway/static/root/assets/{settings-page-D25W9Okr.js → settings-page-Dd-BRu12.js} +1 -1
  54. package/dist/gateway/static/root/assets/{setup-status-panel-DsN7Z4Fl.js → setup-status-panel-CwEFAVWx.js} +1 -1
  55. package/dist/gateway/static/root/assets/{share-preview-page-CRFnUaNb.js → share-preview-page-DzXw9UQJ.js} +1 -1
  56. package/dist/gateway/static/root/assets/{shares-settings-CAX0eNaP.js → shares-settings-Bk6UQOwS.js} +1 -1
  57. package/dist/gateway/static/root/assets/{skill-reload-api-Doy1gghe.js → skill-reload-api-DrmQZ3Au.js} +1 -1
  58. package/dist/gateway/static/root/assets/{skills-page-Cl7jjTK7.js → skills-page-Brxb6QnW.js} +1 -1
  59. package/dist/gateway/static/root/assets/{skills-page.utils-BiCl4nb5.js → skills-page.utils-FB5LtEGm.js} +1 -1
  60. package/dist/gateway/static/root/assets/{system-settings-panel-BuESR7ZT.js → system-settings-panel-ChosWt8n.js} +1 -1
  61. package/dist/gateway/static/root/assets/{task-detail-page-BRiy8NL3.js → task-detail-page-CW2sJbdc.js} +1 -1
  62. package/dist/gateway/static/root/assets/{use-autosave-Dg1mgJ_l.js → use-autosave-2SqnmSKD.js} +1 -1
  63. package/dist/gateway/static/root/assets/{user-context-page-CqoADNKu.js → user-context-page-Bq9KB-Oj.js} +1 -1
  64. package/dist/gateway/static/root/assets/{voice-api-key-field-BDNexlVR.js → voice-api-key-field-DtTIF6M8.js} +1 -1
  65. package/dist/gateway/static/root/assets/{work-discovery-overlay-BHTfeaWL.js → work-discovery-overlay-CK3aZDdE.js} +1 -1
  66. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Cf_w-NZy.js → workflow-run-setup-panel-BjOuddkX.js} +1 -1
  67. package/dist/gateway/static/root/assets/{workflows-page-j0P-uECz.js → workflows-page-CcbGEQol.js} +1 -1
  68. package/dist/gateway/static/root/index.html +1 -1
  69. package/dist/package.js +1 -1
  70. package/dist/src/agent/agent-instance-gateway.d.ts +1 -1
  71. package/dist/src/agent/agent-manager.d.ts +1 -1
  72. package/dist/src/agent/agent-manager.js +18 -3
  73. package/dist/src/agent/background-review/prompts.d.ts +8 -2
  74. package/dist/src/agent/background-review/prompts.js +25 -12
  75. package/dist/src/agent/background-review/run-background-review.d.ts +7 -2
  76. package/dist/src/agent/background-review/run-background-review.js +175 -160
  77. package/dist/src/agent/inbound/turn-dispatcher.js +1 -1
  78. package/dist/src/agent/memory/index.d.ts +1 -1
  79. package/dist/src/agent/memory/index.js +2 -2
  80. package/dist/src/agent/memory/manager.d.ts +1 -11
  81. package/dist/src/agent/memory/manager.js +0 -16
  82. package/dist/src/agent/memory/understanding/service.d.ts +0 -11
  83. package/dist/src/agent/memory/understanding/service.js +7 -173
  84. package/dist/src/agent/memory/user-context-coordinator.d.ts +1 -3
  85. package/dist/src/agent/memory/user-context-coordinator.js +3 -66
  86. package/dist/src/agent/orchestration/agent-orchestrator.js +1 -1
  87. package/dist/src/agent/service/direct-turn-helpers.js +1 -1
  88. package/dist/src/agent/service/process-direct-streaming.js +4 -1
  89. package/dist/src/agent/service/webchat-tts.d.ts +2 -0
  90. package/dist/src/agent/service/webchat-tts.js +15 -2
  91. package/dist/src/agent/tools/tts-tool.js +19 -13
  92. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  93. package/dist/src/config/schema.d.ts +4 -0
  94. package/dist/src/config/schema.js +1 -0
  95. package/dist/src/connectors/connected-source-understanding.js +35 -21
  96. package/dist/src/connectors/learning-coordinator.js +1 -42
  97. package/dist/src/gateway/chat-stream/mapper.d.ts +2 -0
  98. package/dist/src/gateway/chat-stream/mapper.js +16 -0
  99. package/dist/src/gateway/hono/routes/understanding-sources.js +4 -1
  100. package/dist/src/gateway/hono/routes/voice.js +1 -1
  101. package/dist/src/gateway/hono/routes/you.js +29 -11
  102. package/dist/src/gateway/service/run-gateway-agent.js +5 -1
  103. package/dist/src/knowledge/index.d.ts +0 -1
  104. package/dist/src/knowledge/index.js +1 -2
  105. package/dist/src/notifications/planner.js +5 -1
  106. package/dist/src/storage/sqlite/index.d.ts +1 -1
  107. package/dist/src/storage/sqlite/index.js +2 -2
  108. package/dist/src/storage/sqlite/migrations/137_understanding_status_audit.sql +71 -0
  109. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  110. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  111. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  112. package/dist/src/storage/sqlite/schema.js +1 -1
  113. package/dist/src/storage/sqlite/user-context-repository.d.ts +16 -4
  114. package/dist/src/storage/sqlite/user-context-repository.js +56 -12
  115. package/dist/src/user-context/config.d.ts +8 -0
  116. package/dist/src/user-context/config.js +3 -0
  117. package/dist/src/user-context/consolidation.js +16 -4
  118. package/dist/src/user-context/extraction/executor.d.ts +22 -0
  119. package/dist/src/user-context/extraction/executor.js +142 -0
  120. package/dist/src/user-context/extraction/registry.d.ts +1 -1
  121. package/dist/src/user-context/extraction/registry.js +5 -34
  122. package/dist/src/user-context/extraction/repair.js +4 -1
  123. package/dist/src/user-context/extraction/semantic.d.ts +18 -0
  124. package/dist/src/user-context/extraction/semantic.js +148 -0
  125. package/dist/src/user-context/understandingQuality.js +4 -0
  126. package/dist/src/work-discovery/analyzer.js +9 -4
  127. package/dist/src/work-discovery/service.d.ts +1 -0
  128. package/dist/src/work-discovery/service.js +5 -4
  129. package/dist/src/work-discovery/types.d.ts +1 -0
  130. package/package.json +14 -8
  131. package/dist/gateway/static/root/assets/settings-advanced-gate-Cco_W41x.js +0 -1
  132. package/dist/src/agent/memory/understanding/correction.d.ts +0 -3
  133. package/dist/src/agent/memory/understanding/correction.js +0 -39
  134. package/dist/src/knowledge/connected-understanding-pipeline.d.ts +0 -17
  135. package/dist/src/knowledge/connected-understanding-pipeline.js +0 -278
@@ -39,17 +39,6 @@ export declare class MemoryManager {
39
39
  queuePrefetchAll(query: string, options?: {
40
40
  sessionId?: string;
41
41
  }): void;
42
- syncAll(userContent: string, assistantContent: string, options?: {
43
- sessionId?: string;
44
- }): Promise<void>;
45
- captureTurnUnderstanding(userContent: string, assistantContent: string, options?: {
46
- agentId?: string;
47
- sessionId?: string;
48
- turnId?: string;
49
- workspaceId?: string;
50
- projectId?: string;
51
- correctionTargetRecordIds?: string[];
52
- }): Promise<import('./understanding/types.js').UnderstandingReviewResult>;
53
42
  syncProvidersForTurn(userContent: string, assistantContent: string, options?: {
54
43
  sessionId?: string;
55
44
  turnId?: string;
@@ -67,6 +56,7 @@ export declare class MemoryManager {
67
56
  source?: MemoryRecord['source'];
68
57
  reviewSource?: 'turn' | 'background';
69
58
  extractionRunId?: string;
59
+ supersedesRecordIds?: string[];
70
60
  }): Promise<import('./understanding/types.js').UnderstandingReviewResult>;
71
61
  getExternalToolEntries(): Array<{
72
62
  providerId: string;
@@ -81,22 +81,6 @@ var MemoryManager = class {
81
81
  }, "queuePrefetch failed (non-fatal)");
82
82
  }
83
83
  }
84
- async syncAll(userContent, assistantContent, options) {
85
- await this.captureTurnUnderstanding(userContent, assistantContent, options);
86
- await this.syncProvidersForTurn(userContent, assistantContent, options);
87
- }
88
- async captureTurnUnderstanding(userContent, assistantContent, options) {
89
- const sessionContext = options?.sessionId ? this.sessionContexts.get(options.sessionId) : void 0;
90
- return await this.understanding.reviewTurn({
91
- userContent,
92
- assistantContent,
93
- sessionKey: options?.sessionId,
94
- turnId: options?.turnId,
95
- workspaceId: options?.workspaceId ?? sessionContext?.workspace ?? sessionContext?.agentWorkspace,
96
- projectId: options?.projectId,
97
- correctionTargetRecordIds: options?.correctionTargetRecordIds
98
- });
99
- }
100
84
  async syncProvidersForTurn(userContent, assistantContent, options) {
101
85
  const provenance = options?.provenance;
102
86
  const event = {
@@ -1,16 +1,5 @@
1
1
  import type { UnderstandingCandidate, UnderstandingReviewResult } from './types.js';
2
- export declare function extractExplicitUnderstandingCandidates(userContent: string): UnderstandingCandidate[];
3
- export declare function extractHighSignalUnderstandingCandidates(userContent: string): UnderstandingCandidate[];
4
2
  export declare class UserUnderstandingService {
5
- reviewTurn(params: {
6
- userContent: string;
7
- assistantContent: string;
8
- sessionKey?: string;
9
- turnId?: string;
10
- workspaceId?: string;
11
- projectId?: string;
12
- correctionTargetRecordIds?: string[];
13
- }): Promise<UnderstandingReviewResult>;
14
3
  applyCandidates(candidates: UnderstandingCandidate[], context: {
15
4
  sessionKey?: string;
16
5
  workspaceId?: string;
@@ -1,27 +1,12 @@
1
- import { finishContextExtractionRun, getContextExtractionRun, listContextExtractionOutputs } from "../../../storage/sqlite/context-extraction-repository.js";
1
+ import { getContextExtractionRun } from "../../../storage/sqlite/context-extraction-repository.js";
2
2
  import { closeTemporalAssertions, createTemporalAssertion, linkContextObjects } from "../../../storage/sqlite/context-relation-repository.js";
3
3
  import { closeUnderstandingValidity, createContextEvidence, createUnderstanding, getUnderstanding, isUnderstandingSuppressed, linkUnderstandingEvidence, listUnderstandings, setUnderstandingStatus } from "../../../storage/sqlite/user-context-repository.js";
4
4
  import "../../../storage/sqlite/index.js";
5
- import { extractExplicitUnderstandingCorrectionContent } from "./correction.js";
6
- import { claimRegisteredExtraction, extractionInputHash, getRegisteredExtractorDefinition } from "../../../user-context/extraction/registry.js";
5
+ import { getRegisteredExtractorDefinition } from "../../../user-context/extraction/registry.js";
7
6
  import { canonicalUnderstandingKey, findContradictoryUnderstanding, findDuplicateUnderstanding } from "../../../user-context/understanding.js";
8
7
  import { inferMemorySensitivity, redactSensitiveMemoryText } from "../sensitivity.js";
9
8
  //#region src/agent/memory/understanding/service.ts
10
9
  const MAX_CANDIDATE_CHARS = 600;
11
- const EXPLICIT_PATTERNS = [
12
- /\b(?:please\s+)?remember(?:\s+that)?\s+(.+)/i,
13
- /\bkeep\s+in\s+mind(?:\s+that)?\s+(.+)/i,
14
- /(?:请)?记住[::\s]*(.+)/
15
- ];
16
- const HIGH_SIGNAL_PATTERNS = [
17
- /(?:从现在起|今后|以后)(?:都)?(?:请)?[::\s]*(.+)/,
18
- /下次(?:请)?[::\s]*(.+)/,
19
- /(?:默认|每次|每回)(?:都)?(?:请)?[::\s]*(.+)/,
20
- /((?:我|本人)(?:一直|通常|一般|更)?(?:喜欢|偏好|习惯|希望|不希望|不喜欢).+)/,
21
- /\b(?:from now on|going forward|next time|by default|whenever)\b[,:\s]*(.+)/i,
22
- /(\bi\s+(?:always|usually|generally)\s+(?:prefer|like|want|need|avoid)\b.+)/i,
23
- /(\bi\s+(?:prefer|like|want|need)\s+.+\s+(?:every time|by default|going forward)\b.*)/i
24
- ];
25
10
  const SENSITIVITY_RANK = {
26
11
  normal: 0,
27
12
  personal: 1,
@@ -40,50 +25,6 @@ function candidateTime(value) {
40
25
  const parsed = Date.parse(value);
41
26
  return Number.isFinite(parsed) ? parsed : void 0;
42
27
  }
43
- function inferKind(content) {
44
- if (/\b(don't|do not|never|avoid)\b/i.test(content) || /不要|不用|无需|别再|禁止|底线/.test(content)) return "boundary";
45
- if (/\b(prefer|preference|like)\b/i.test(content) || /喜欢|偏好|习惯/.test(content)) return "preference";
46
- if (/\b(project|repo|codebase|workspace)\b/i.test(content) || /项目|仓库|代码库/.test(content)) return "project_context";
47
- if (/\b(goal|plan|deadline)\b/i.test(content) || /目标|计划|截止/.test(content)) return "long_term_goal";
48
- if (/\b(next time|lesson|failed|error)\b/i.test(content) || /教训|失败|错误/.test(content)) return "task_lesson";
49
- if (/习惯|每周|每天|usually|routine/i.test(content)) return "routine";
50
- return "current_state";
51
- }
52
- function extractExplicitUnderstandingCandidates(userContent) {
53
- const correction = extractExplicitUnderstandingCorrectionContent(userContent);
54
- const matched = correction ?? userContent.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).flatMap((line) => EXPLICIT_PATTERNS.map((pattern) => pattern.exec(line)?.[1] ?? "")).map(normalizeContent).find((content) => content.length >= 8);
55
- if (!matched) return [];
56
- const content = normalizeContent(matched);
57
- const kind = inferKind(content);
58
- return [{
59
- kind,
60
- content,
61
- canonicalKey: canonicalUnderstandingKey(kind, content),
62
- confidence: correction ? .98 : .95,
63
- importance: kind === "boundary" ? .9 : .8,
64
- explicitness: "explicit",
65
- durability: "durable",
66
- sensitivity: inferMemorySensitivity(content),
67
- disclosurePolicy: kind === "boundary" ? "silent" : "referenceable"
68
- }];
69
- }
70
- function extractHighSignalUnderstandingCandidates(userContent) {
71
- if (extractExplicitUnderstandingCorrectionContent(userContent)) return [];
72
- const matched = userContent.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).flatMap((line) => HIGH_SIGNAL_PATTERNS.map((pattern) => pattern.exec(line)?.[1] ?? "")).map(normalizeContent).find((content) => content.length >= 6);
73
- if (!matched) return [];
74
- const kind = inferKind(matched);
75
- return [{
76
- kind,
77
- content: matched,
78
- canonicalKey: canonicalUnderstandingKey(kind, matched),
79
- confidence: .85,
80
- importance: kind === "boundary" ? .9 : .75,
81
- explicitness: "observed",
82
- durability: "durable",
83
- sensitivity: inferMemorySensitivity(matched),
84
- disclosurePolicy: kind === "boundary" ? "silent" : "referenceable"
85
- }];
86
- }
87
28
  function scopeForCandidate(candidate, context) {
88
29
  if (candidate.durability === "ephemeral" && context.sessionKey) return {
89
30
  type: "session",
@@ -100,116 +41,6 @@ function scopeForCandidate(candidate, context) {
100
41
  return { type: "global" };
101
42
  }
102
43
  var UserUnderstandingService = class {
103
- async reviewTurn(params) {
104
- params.assistantContent;
105
- const explicitCandidates = extractExplicitUnderstandingCandidates(params.userContent);
106
- const candidates = explicitCandidates.length > 0 ? explicitCandidates : extractHighSignalUnderstandingCandidates(params.userContent);
107
- const turnRef = params.turnId ?? `untracked-${extractionInputHash(params.userContent).slice(0, 16)}`;
108
- const extraction = claimRegisteredExtraction({
109
- extractorId: explicitCandidates.length > 0 ? "explicit-command" : "deterministic-signal",
110
- sourceRef: params.sessionKey ? `session:${params.sessionKey}:turn:${turnRef}` : `turn:${turnRef}`,
111
- contentForHash: params.userContent,
112
- processingPolicy: "local_only",
113
- destination: "deterministic"
114
- });
115
- if (!extraction.shouldExecute) {
116
- const previous = listContextExtractionOutputs(extraction.run.id);
117
- return {
118
- proposed: candidates.length,
119
- created: 0,
120
- deduplicated: previous.filter((output) => output.outcome !== "rejected").length,
121
- rejected: previous.filter((output) => output.outcome === "rejected").length,
122
- createdRecords: []
123
- };
124
- }
125
- if (candidates.length === 0) {
126
- finishContextExtractionRun({
127
- runId: extraction.run.id,
128
- status: "completed",
129
- outputs: []
130
- });
131
- return {
132
- proposed: 0,
133
- created: 0,
134
- deduplicated: 0,
135
- rejected: 0,
136
- createdRecords: []
137
- };
138
- }
139
- const safeCandidates = candidates.filter((candidate) => {
140
- const content = normalizeContent(candidate.content);
141
- const sensitivity = effectiveSensitivity(candidate.sensitivity, content);
142
- return sensitivity !== "secret" && sensitivity !== "regulated";
143
- });
144
- if (!safeCandidates.length) {
145
- finishContextExtractionRun({
146
- runId: extraction.run.id,
147
- status: "completed",
148
- outputs: candidates.map((candidate) => ({
149
- candidateKey: candidate.canonicalKey ?? canonicalUnderstandingKey(candidate.kind, candidate.content),
150
- outcome: "rejected"
151
- }))
152
- });
153
- return {
154
- proposed: candidates.length,
155
- created: 0,
156
- deduplicated: 0,
157
- rejected: candidates.length,
158
- createdRecords: []
159
- };
160
- }
161
- const evidence = createContextEvidence({
162
- sourceType: "conversation",
163
- sourceRef: params.sessionKey ? `session:${params.sessionKey}:turn:${turnRef}` : `turn:${turnRef}`,
164
- sourceRunId: extraction.run.id,
165
- ...params.sessionKey ? { sessionId: params.sessionKey } : {},
166
- turnId: turnRef,
167
- contentHash: extractionInputHash(params.userContent),
168
- retentionPolicy: "derived_only",
169
- processingPolicy: "local_only",
170
- extractorId: extraction.run.extractorId,
171
- extractorVersion: extraction.run.extractorVersion,
172
- redactedExcerpt: redactSensitiveMemoryText(safeCandidates.map((candidate) => candidate.content).join("\n")).slice(0, MAX_CANDIDATE_CHARS),
173
- trustLevel: "owner",
174
- observedAt: Date.now()
175
- });
176
- let result;
177
- try {
178
- result = await this.applyCandidates(safeCandidates, {
179
- sessionKey: params.sessionKey,
180
- workspaceId: params.workspaceId,
181
- projectId: params.projectId,
182
- evidenceIds: [evidence.id],
183
- reviewSource: "turn",
184
- supersedesRecordIds: params.correctionTargetRecordIds,
185
- extractionRunId: extraction.run.id
186
- });
187
- } catch (error) {
188
- finishContextExtractionRun({
189
- runId: extraction.run.id,
190
- status: "failed",
191
- errorCode: "write_failed"
192
- });
193
- throw error;
194
- }
195
- finishContextExtractionRun({
196
- runId: extraction.run.id,
197
- status: "completed",
198
- outputs: result.writeOutputs?.map((output) => ({
199
- candidateKey: output.candidateKey,
200
- objectType: output.objectId ? "understanding" : void 0,
201
- objectId: output.objectId,
202
- versionId: output.versionId,
203
- outcome: output.outcome
204
- })) ?? []
205
- });
206
- return {
207
- ...result,
208
- proposed: candidates.length,
209
- rejected: result.rejected + candidates.length - safeCandidates.length,
210
- sourceItemId: evidence.id
211
- };
212
- }
213
44
  async applyCandidates(candidates, context) {
214
45
  let created = 0;
215
46
  let deduplicated = 0;
@@ -332,7 +163,10 @@ var UserUnderstandingService = class {
332
163
  objectId: supersededId,
333
164
  validTo: closedAt
334
165
  });
335
- setUnderstandingStatus(supersededId, "archived");
166
+ setUnderstandingStatus(supersededId, "archived", {
167
+ actorType: "user",
168
+ source: "semantic-memory-superseded"
169
+ });
336
170
  }
337
171
  const assertionType = record.kind === "current_state" ? "current_state" : record.kind === "routine" ? "routine" : record.kind === "relationship" ? "relationship" : record.kind === "project_context" ? "project_status" : void 0;
338
172
  if (assertionType) createTemporalAssertion({
@@ -372,4 +206,4 @@ var UserUnderstandingService = class {
372
206
  }
373
207
  };
374
208
  //#endregion
375
- export { UserUnderstandingService, extractExplicitUnderstandingCandidates, extractHighSignalUnderstandingCandidates };
209
+ export { UserUnderstandingService };
@@ -5,7 +5,6 @@ import type { MemoryManager } from './manager.js';
5
5
  export interface UserContextCoordinatorOptions {
6
6
  getConfig: () => Config | undefined;
7
7
  isEnabledForSession: (sessionKey: string) => boolean;
8
- getAgentIdForSession: (sessionKey: string) => string;
9
8
  getWorkspaceIdForSession?: (sessionKey: string) => string;
10
9
  getProjectIdForSession?: (sessionKey: string) => string | undefined;
11
10
  getMemoryManagerForSession: (sessionKey: string) => MemoryManager;
@@ -15,11 +14,10 @@ export declare class UserContextCoordinator {
15
14
  private readonly options;
16
15
  private readonly userTurn;
17
16
  private readonly lastTurnId;
18
- private readonly correctionTargets;
19
17
  private readonly planner;
20
18
  constructor(options: UserContextCoordinatorOptions);
21
19
  forgetSession(sessionKey: string): void;
22
20
  clear(): void;
23
21
  prepare(userMessage: AgentMessage, sessionKey: string, turnId: string): Promise<UserContextPlan>;
24
- afterTurn(sessionKey: string, userPlainText: string): Promise<import('./understanding/types.js').UnderstandingReviewResult | undefined>;
22
+ afterTurn(sessionKey: string, userPlainText: string): Promise<void>;
25
23
  }
@@ -1,19 +1,12 @@
1
- import { createLogger } from "../../utils/logger/index.js";
2
- import { init_logger } from "../../utils/logger.js";
3
- import { getTurnPersonalization, recordContextFeedback, setUnderstandingStatus } from "../../storage/sqlite/user-context-repository.js";
4
- import "../../storage/sqlite/index.js";
5
1
  import { init_session_key, parseSessionKey } from "../../routing/session-key.js";
6
2
  import { assembleTaskContext } from "../../tasks/task-context-assembler.js";
7
3
  import { shouldPlanUserContextThisTurn } from "./memory-config.js";
8
4
  import { UserContextPlanner, prependAgentContext } from "../../user-context/planner.js";
9
- import { isExplicitUnderstandingCorrection } from "./understanding/correction.js";
10
5
  import { extractAgentUserPlainText } from "./user-message-text.js";
11
6
  import { consumeTurnMemoryProvenance, markTurnRecalledContext } from "./turn-provenance.js";
12
7
  import { planTrustedRecall } from "./trusted-recall.js";
13
8
  //#region src/agent/memory/user-context-coordinator.ts
14
9
  init_session_key();
15
- init_logger();
16
- const log = createLogger("UserContextCoordinator");
17
10
  function isPrivateSession(sessionKey) {
18
11
  const parsed = parseSessionKey(sessionKey);
19
12
  return !parsed || parsed.peerKind === "direct";
@@ -21,7 +14,6 @@ function isPrivateSession(sessionKey) {
21
14
  var UserContextCoordinator = class {
22
15
  userTurn = /* @__PURE__ */ new Map();
23
16
  lastTurnId = /* @__PURE__ */ new Map();
24
- correctionTargets = /* @__PURE__ */ new Map();
25
17
  planner = new UserContextPlanner();
26
18
  constructor(options) {
27
19
  this.options = options;
@@ -29,12 +21,10 @@ var UserContextCoordinator = class {
29
21
  forgetSession(sessionKey) {
30
22
  this.userTurn.delete(sessionKey);
31
23
  this.lastTurnId.delete(sessionKey);
32
- this.correctionTargets.delete(sessionKey);
33
24
  }
34
25
  clear() {
35
26
  this.userTurn.clear();
36
27
  this.lastTurnId.clear();
37
- this.correctionTargets.clear();
38
28
  }
39
29
  async prepare(userMessage, sessionKey, turnId) {
40
30
  const empty = () => ({
@@ -50,37 +40,9 @@ var UserContextCoordinator = class {
50
40
  if (!this.options.isEnabledForSession(sessionKey)) return empty();
51
41
  const config = this.options.getConfig();
52
42
  if (!isPrivateSession(sessionKey)) return empty();
53
- const userQuery = extractAgentUserPlainText(userMessage);
54
- const taskContext = assembleTaskContext(sessionKey, userQuery);
43
+ const taskContext = assembleTaskContext(sessionKey, extractAgentUserPlainText(userMessage));
55
44
  const query = taskContext.retrievalQuery;
56
- const previousTurnId = this.lastTurnId.get(sessionKey);
57
45
  this.lastTurnId.set(sessionKey, turnId);
58
- this.correctionTargets.delete(sessionKey);
59
- let excludedRecordIds;
60
- if (previousTurnId && isExplicitUnderstandingCorrection(userQuery)) try {
61
- const personalization = getTurnPersonalization(previousTurnId);
62
- const selectedIds = personalization?.items.filter((item) => item.objectType === "understanding" && item.decision === "selected").map((item) => item.objectId) ?? [];
63
- if (personalization && selectedIds.length) {
64
- for (const recordId of selectedIds) {
65
- recordContextFeedback({
66
- turnId: previousTurnId,
67
- runId: personalization.runId,
68
- objectType: "understanding",
69
- objectId: recordId,
70
- rating: "wrong",
71
- reason: "detected_explicit_user_correction"
72
- });
73
- setUnderstandingStatus(recordId, "needs_review");
74
- }
75
- this.correctionTargets.set(sessionKey, selectedIds);
76
- excludedRecordIds = selectedIds;
77
- }
78
- } catch (err) {
79
- log.debug({
80
- err,
81
- sessionKey
82
- }, "Understanding correction feedback was not persisted");
83
- }
84
46
  const turn = (this.userTurn.get(sessionKey) ?? 0) + 1;
85
47
  this.userTurn.set(sessionKey, turn);
86
48
  if (!shouldPlanUserContextThisTurn(config, turn)) return empty();
@@ -92,7 +54,6 @@ var UserContextCoordinator = class {
92
54
  turnId,
93
55
  query,
94
56
  userMessage,
95
- excludedRecordIds,
96
57
  allocation: taskContext.allocation
97
58
  });
98
59
  const totalChars = taskContext.allocation.maxChars;
@@ -134,36 +95,12 @@ var UserContextCoordinator = class {
134
95
  };
135
96
  }
136
97
  async afterTurn(sessionKey, userPlainText) {
137
- if (!this.options.isEnabledForSession(sessionKey)) return void 0;
98
+ if (!this.options.isEnabledForSession(sessionKey)) return;
138
99
  const memoryManager = this.options.getMemoryManagerForSession(sessionKey);
139
100
  const assistantContent = this.options.getLastAssistantContent(sessionKey) ?? "";
140
- if (!isPrivateSession(sessionKey)) return void 0;
141
- const correctionTargetRecordIds = this.correctionTargets.get(sessionKey);
101
+ if (!isPrivateSession(sessionKey)) return;
142
102
  const turnId = this.lastTurnId.get(sessionKey);
143
103
  const provenance = turnId ? consumeTurnMemoryProvenance(sessionKey, turnId) : void 0;
144
- this.correctionTargets.delete(sessionKey);
145
- try {
146
- const review = await memoryManager.captureTurnUnderstanding(userPlainText, assistantContent, {
147
- agentId: this.options.getAgentIdForSession(sessionKey),
148
- sessionId: sessionKey,
149
- turnId,
150
- workspaceId: this.options.getWorkspaceIdForSession?.(sessionKey),
151
- projectId: this.options.getProjectIdForSession?.(sessionKey),
152
- correctionTargetRecordIds
153
- });
154
- memoryManager.syncProvidersForTurn(userPlainText, assistantContent, {
155
- sessionId: sessionKey,
156
- turnId,
157
- provenance
158
- });
159
- memoryManager.queuePrefetchAll(userPlainText, { sessionId: sessionKey });
160
- return review;
161
- } catch (err) {
162
- log.warn({
163
- err,
164
- sessionKey
165
- }, "Turn understanding capture failed");
166
- }
167
104
  memoryManager.syncProvidersForTurn(userPlainText, assistantContent, {
168
105
  sessionId: sessionKey,
169
106
  turnId,
@@ -110,7 +110,7 @@ var AgentOrchestrator = class {
110
110
  clearPendingTranscriptUserMessage(sessionKey, userMessage);
111
111
  }
112
112
  })();
113
- const understandingReview = await this.agentManager.afterAgentTurn(sessionKey, userPlainForMemory);
113
+ const understandingReview = await this.agentManager.afterAgentTurn(sessionKey, userPlainForMemory, turnId);
114
114
  if (understandingReview?.createdRecords.length) {
115
115
  const captured = understandingReview.createdRecords.filter((record) => record.status === "active");
116
116
  const candidates = understandingReview.createdRecords.filter((record) => record.status === "candidate");
@@ -116,7 +116,7 @@ async function runDirectAgentTurn(deps, input) {
116
116
  beforeTurn: () => deps.agentManager.beginBackgroundReviewUserTurn(input.sessionKey),
117
117
  onEvent: input.onEvent
118
118
  });
119
- const understandingReview = await deps.agentManager.afterAgentTurn(input.sessionKey, userPlain);
119
+ const understandingReview = await deps.agentManager.afterAgentTurn(input.sessionKey, userPlain, turnId);
120
120
  if (understandingReview?.createdRecords.length) {
121
121
  const captured = understandingReview.createdRecords.filter((record) => record.status === "active");
122
122
  const candidates = understandingReview.createdRecords.filter((record) => record.status === "candidate");
@@ -13,6 +13,7 @@ import { isSessionSourceBinding, summarizeSourceContext } from "../source-contex
13
13
  import { injectSourceContextsIntoUserMessage } from "../source-context/injector.js";
14
14
  import { AsyncQueue, AsyncQueueOverflowError } from "./async-queue.js";
15
15
  import { hydratePerTurnState, runDirectAgentTurn, tryRunSlashCommand } from "./direct-turn-helpers.js";
16
+ import { isSuccessfulWebchatTtsToolEvent } from "./webchat-tts.js";
16
17
  //#region src/agent/service/process-direct-streaming.ts
17
18
  const MAX_BUFFERED_STREAM_EVENTS = 512;
18
19
  const LOSSY_STREAM_EVENT_TYPES = new Set([
@@ -151,6 +152,7 @@ async function* runProcessDirectStreaming(deps, input) {
151
152
  let userAborted = false;
152
153
  let abortHandled = false;
153
154
  let inboundVoice = false;
155
+ let explicitTtsEmitted = false;
154
156
  let ranSlashCommand = false;
155
157
  let mergedUserText = input.content;
156
158
  let webchatSlashReceipt;
@@ -299,6 +301,7 @@ async function* runProcessDirectStreaming(deps, input) {
299
301
  runId: input.runId,
300
302
  onEvent: (embeddedEvent) => {
301
303
  const event = { ...embeddedEvent };
304
+ if (isSuccessfulWebchatTtsToolEvent(event)) explicitTtsEmitted = true;
302
305
  if (event.type === "error" && typeof event.content === "string") event.content = formatStreamError(event.content);
303
306
  pushVisible(event);
304
307
  }
@@ -329,7 +332,7 @@ async function* runProcessDirectStreaming(deps, input) {
329
332
  sessionKey,
330
333
  droppedEvents: queue.droppedCount
331
334
  }, "Coalesced stream progress events because the consumer fell behind");
332
- if (!userAborted && !streamOverflowed && channel === "webchat") try {
335
+ if (!userAborted && !streamOverflowed && channel === "webchat" && !explicitTtsEmitted) try {
333
336
  const ttsAudioEvent = await deps.maybeEmitWebchatTts(sessionKey, inboundVoice);
334
337
  if (ttsAudioEvent) queue.push(ttsAudioEvent);
335
338
  } catch (ttsErr) {
@@ -15,6 +15,8 @@ export type WebchatTtsDeps = {
15
15
  warn: (obj: Record<string, unknown>, msg: string) => void;
16
16
  };
17
17
  };
18
+ /** True when the explicit TTS tool already produced durable Webchat audio. */
19
+ export declare function isSuccessfulWebchatTtsToolEvent(event: unknown): boolean;
18
20
  /**
19
21
  * Generate TTS for webchat when config allows; persist under `{stateDir}/media/tts/`.
20
22
  */
@@ -3,9 +3,22 @@ import { speak } from "../../voice/tts/speak-core.js";
3
3
  import { mergeTtsConfigFromAppConfig } from "../../voice/tts/merge-config.js";
4
4
  import { getChannelOutputFormat, shouldUseTTS } from "../../voice/tts/service.js";
5
5
  import { compressAudio } from "../../voice/tts/audio.js";
6
- import "../../voice/tts/index.js";
7
6
  import { persistOutboundTtsAudio } from "../../channels/attachments/outbound-tts-persist.js";
7
+ import "../../voice/tts/index.js";
8
8
  //#region src/agent/service/webchat-tts.ts
9
+ /** True when the explicit TTS tool already produced durable Webchat audio. */
10
+ function isSuccessfulWebchatTtsToolEvent(event) {
11
+ if (!event || typeof event !== "object" || Array.isArray(event)) return false;
12
+ const row = event;
13
+ if (row.type !== "tool_execution_end" || row.toolName !== "text_to_speech" || row.isError === true) return false;
14
+ const result = row.result && typeof row.result === "object" && !Array.isArray(row.result) ? row.result : null;
15
+ const details = result?.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : null;
16
+ return Array.isArray(details?.media) && details.media.some((item) => {
17
+ if (!item || typeof item !== "object" || Array.isArray(item)) return false;
18
+ const media = item;
19
+ return typeof media.uri === "string" && media.uri.trim().length > 0;
20
+ });
21
+ }
9
22
  /**
10
23
  * Generate TTS for webchat when config allows; persist under `{stateDir}/media/tts/`.
11
24
  */
@@ -57,4 +70,4 @@ async function appendMediaToLastAssistant(sessionStore, sessionKey, ref) {
57
70
  }
58
71
  }
59
72
  //#endregion
60
- export { appendMediaToLastAssistant, maybeEmitWebchatTts };
73
+ export { appendMediaToLastAssistant, isSuccessfulWebchatTtsToolEvent, maybeEmitWebchatTts };
@@ -4,6 +4,7 @@ import { speak } from "../../voice/tts/speak-core.js";
4
4
  import { mergeTtsConfigFromAppConfig } from "../../voice/tts/merge-config.js";
5
5
  import { getChannelOutputFormat } from "../../voice/tts/service.js";
6
6
  import { compressAudio } from "../../voice/tts/audio.js";
7
+ import { persistOutboundTtsAudio } from "../../channels/attachments/outbound-tts-persist.js";
7
8
  import { Type } from "@sinclair/typebox";
8
9
  //#region src/agent/tools/tts-tool.ts
9
10
  init_logger();
@@ -72,30 +73,35 @@ function createTextToSpeechTool(deps) {
72
73
  });
73
74
  const wavTarget = outFmt.format === "mp3" ? "mp3" : "opus";
74
75
  const { buffer: compressedAudio, format: compressedFormat } = await compressAudio(Buffer.from(result.audio), result.format, wavTarget);
75
- const dataUrl = `data:${compressedFormat === "opus" ? "audio/ogg" : compressedFormat === "mp3" || compressedFormat === "mpeg" ? "audio/mpeg" : `audio/${compressedFormat}`};base64,${compressedAudio.toString("base64")}`;
76
- const msg = {
77
- channel: ctx.channel,
78
- chat_id: ctx.chatId,
79
- content: "",
80
- mediaUrl: dataUrl,
81
- mediaType: "audio",
82
- audioAsVoice: outFmt.voiceCompatible
83
- };
84
- await deps.bus.publishOutbound(msg);
76
+ const mimeType = compressedFormat === "opus" ? "audio/ogg" : compressedFormat === "mp3" || compressedFormat === "mpeg" ? "audio/mpeg" : `audio/${compressedFormat}`;
77
+ const media = ctx.channel === "webchat" ? await persistOutboundTtsAudio(compressedAudio, compressedFormat) : void 0;
78
+ if (!media) {
79
+ const msg = {
80
+ channel: ctx.channel,
81
+ chat_id: ctx.chatId,
82
+ content: "",
83
+ mediaUrl: `data:${mimeType};base64,${compressedAudio.toString("base64")}`,
84
+ mediaType: "audio",
85
+ audioAsVoice: outFmt.voiceCompatible
86
+ };
87
+ await deps.bus.publishOutbound(msg);
88
+ }
85
89
  log.info({
86
90
  provider: result.provider,
87
91
  format: compressedFormat,
92
+ channel: ctx.channel,
88
93
  textLength: params.text.length,
89
94
  audioSize: compressedAudio.length
90
- }, "TTS tool sent audio");
95
+ }, ctx.channel === "webchat" ? "TTS tool attached audio" : "TTS tool sent audio");
91
96
  return {
92
97
  content: [{
93
98
  type: "text",
94
- text: `✅ Sent voice message (${result.provider}, ${compressedFormat}).`
99
+ text: ctx.channel === "webchat" ? `✅ Attached voice message (${result.provider}, ${compressedFormat}).` : `✅ Sent voice message (${result.provider}, ${compressedFormat}).`
95
100
  }],
96
101
  details: {
97
102
  provider: result.provider,
98
- format: compressedFormat
103
+ format: compressedFormat,
104
+ ...media ? { media: [media] } : {}
99
105
  }
100
106
  };
101
107
  } catch (error) {
@@ -52,7 +52,7 @@ async function checkDatabaseSchema(ctx) {
52
52
  id: "database-schema",
53
53
  label: "Database schema",
54
54
  status: "pass",
55
- message: `SQLite schema v${status.dbVersion} (current release v136).`,
55
+ message: `SQLite schema v${status.dbVersion} (current release v137).`,
56
56
  hints: []
57
57
  };
58
58
  }
@@ -1572,6 +1572,10 @@ export declare const ConfigSchema: z.ZodDefault<z.ZodObject<{
1572
1572
  }, z.core.$strict>>;
1573
1573
  understanding: z.ZodDefault<z.ZodObject<{
1574
1574
  enabled: z.ZodDefault<z.ZodBoolean>;
1575
+ processingPolicy: z.ZodDefault<z.ZodEnum<{
1576
+ local_only: "local_only";
1577
+ remote_allowed: "remote_allowed";
1578
+ }>>;
1575
1579
  adaptiveCadence: z.ZodDefault<z.ZodBoolean>;
1576
1580
  reviewIntervalTurns: z.ZodDefault<z.ZodNumber>;
1577
1581
  maxHistoryMessages: z.ZodDefault<z.ZodNumber>;
@@ -1059,6 +1059,7 @@ workDiscoveryOnboarding: z.boolean().default(true) }).strict().default({ workDis
1059
1059
  },
1060
1060
  understanding: {
1061
1061
  enabled: true,
1062
+ processingPolicy: "remote_allowed",
1062
1063
  adaptiveCadence: true,
1063
1064
  reviewIntervalTurns: 10,
1064
1065
  maxHistoryMessages: 80,