@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.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CjGZBs4C.js → agent-avatar-dicebear-Bp_wmuGW.js} +1 -1
- package/dist/gateway/static/root/assets/{agents-B0NI4_NI.js → agents-CKG93dlc.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel--SjycrXk.js → app-management-settings-panel-CwAPK4Jm.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-8G-TVUtg.js → appearance-settings-DsGLvqTb.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-2NiqD-YP.js → apps-page-Ee9qOQEx.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-Bx44zSpz.js → attachment-load-B4qqigAQ.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-CrKAHqc0.js → automations-page-B8ImehSW.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-workspace-Q3g9cf0h.js → automations-workspace-BNFAfNwC.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-DjDwnHAQ.js → binary-plugins-DaJbyYc2.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-7aMOKoZM.js → block-editor-Kl25XWJc.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-DTwPkmAC.js → browser-settings-page-Cg6NgwE_.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-workflows-page-DVLrWKz_.js → browser-workflows-page-DjMxs33B.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-YcP4FbN7.js → capabilities-settings-panel-NvJKgF1u.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-settings-panel-AcPjHHia.js → capability-presets-settings-panel-C2uBdALe.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-Ca94Q1D-.js → channels-settings-CIcYKMEs.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-BR-_7KC0.js → chat-terminal-dock-BiU_4Rq6.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-SGxfLTd6.js → connectors-page-CEEcmUiP.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-expression-D8-c-Tkw.js → cron-expression-Cs_5pUPT.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-kV-Jyl9J.js → date-picker-y1kWrFVU.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-CwKcrfGj.js → dependency-picker-skc62zrn.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-CTs5WGzk.js → desktop-pet-settings-BVVCBgYK.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-D9rnchu3.js → desktop-pet-ux9DxchT.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-CCl0aaDm.js → directory-picker-path-field-V8vZP0Dy.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-Cau40SvC.js → extension-debug-page-Dh6DDuvQ.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BsJXdvIL.js → extension-page-8OKQDEat.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-qWqJyF7-.js → extension-settings-page-BgSV0Nny.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-BI0SELXm.js → gateway-config-api-Dlc6eaTG.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-6ZdOIC7F.js → gateway-settings-DqQgiAUv.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-KBwhXeFD.js → heartbeat-settings-Pxqk5GFH.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-BA2M4CLi.js → home-page-1xjo6wE4.js} +1 -1
- package/dist/gateway/static/root/assets/{index-BX0dz1JK.js → index-Bbd_dXui.js} +17 -17
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-kHtpnyEm.js → keyboard-shortcuts-settings-BSFNqxzY.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-D2nkn09R.js → local-app-workbench-page-heQ4-PJ7.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-BCXa0oUa.js → local-apps-page-DzU6yZ9-.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-D3sNst0K.js → logs-page-BblnS6Jy.js} +1 -1
- package/dist/gateway/static/root/assets/{management-settings-tHnlfOAQ.js → management-settings-B8hM56HS.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-split-BMLqHHr5.js → markdown-split-DRbZjxMK.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-DybwqTgv.js → media-plugins-B8vMe5VX.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-C24SfF6r.js → notes-page-B-YZ7iuK.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-workbench-CTdZc3ov.js → notes-workbench-CAM09C8b.js} +2 -2
- package/dist/gateway/static/root/assets/{product-open-page-DBfCVlJq.js → product-open-page-DkWcyki5.js} +1 -1
- package/dist/gateway/static/root/assets/{project-detail-page-COHaEpbA.js → project-detail-page-BIPIwgU2.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-lMnST7D_.js → projects-page-B_kzTn1K.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-BIRKBvO_.js → remote-access-hub-CtD8DsD-.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BPEGTip5.js → runtime-tools-settings-panel-Bq_ETeal.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-Dxlu60Td.js → schema-form-BogTlrsP.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-CATO9G3y.js → sessions-page-B8n9HI1y.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-xlhdRTME.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-ChXh3Bwk.js → settings-form-section-Coy1Cbp6.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-BCAUqIe2.js → settings-loading-skeleton-B5GU7iCG.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-D25W9Okr.js → settings-page-Dd-BRu12.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-DsN7Z4Fl.js → setup-status-panel-CwEFAVWx.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-CRFnUaNb.js → share-preview-page-DzXw9UQJ.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-CAX0eNaP.js → shares-settings-Bk6UQOwS.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-reload-api-Doy1gghe.js → skill-reload-api-DrmQZ3Au.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-Cl7jjTK7.js → skills-page-Brxb6QnW.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page.utils-BiCl4nb5.js → skills-page.utils-FB5LtEGm.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-BuESR7ZT.js → system-settings-panel-ChosWt8n.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-BRiy8NL3.js → task-detail-page-CW2sJbdc.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-Dg1mgJ_l.js → use-autosave-2SqnmSKD.js} +1 -1
- package/dist/gateway/static/root/assets/{user-context-page-CqoADNKu.js → user-context-page-Bq9KB-Oj.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-BDNexlVR.js → voice-api-key-field-DtTIF6M8.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-BHTfeaWL.js → work-discovery-overlay-CK3aZDdE.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Cf_w-NZy.js → workflow-run-setup-panel-BjOuddkX.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-j0P-uECz.js → workflows-page-CcbGEQol.js} +1 -1
- package/dist/gateway/static/root/index.html +1 -1
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-instance-gateway.d.ts +1 -1
- package/dist/src/agent/agent-manager.d.ts +1 -1
- package/dist/src/agent/agent-manager.js +18 -3
- package/dist/src/agent/background-review/prompts.d.ts +8 -2
- package/dist/src/agent/background-review/prompts.js +25 -12
- package/dist/src/agent/background-review/run-background-review.d.ts +7 -2
- package/dist/src/agent/background-review/run-background-review.js +175 -160
- package/dist/src/agent/inbound/turn-dispatcher.js +1 -1
- package/dist/src/agent/memory/index.d.ts +1 -1
- package/dist/src/agent/memory/index.js +2 -2
- package/dist/src/agent/memory/manager.d.ts +1 -11
- package/dist/src/agent/memory/manager.js +0 -16
- package/dist/src/agent/memory/understanding/service.d.ts +0 -11
- package/dist/src/agent/memory/understanding/service.js +7 -173
- package/dist/src/agent/memory/user-context-coordinator.d.ts +1 -3
- package/dist/src/agent/memory/user-context-coordinator.js +3 -66
- package/dist/src/agent/orchestration/agent-orchestrator.js +1 -1
- package/dist/src/agent/service/direct-turn-helpers.js +1 -1
- package/dist/src/agent/service/process-direct-streaming.js +4 -1
- package/dist/src/agent/service/webchat-tts.d.ts +2 -0
- package/dist/src/agent/service/webchat-tts.js +15 -2
- package/dist/src/agent/tools/tts-tool.js +19 -13
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/config/schema.d.ts +4 -0
- package/dist/src/config/schema.js +1 -0
- package/dist/src/connectors/connected-source-understanding.js +35 -21
- package/dist/src/connectors/learning-coordinator.js +1 -42
- package/dist/src/gateway/chat-stream/mapper.d.ts +2 -0
- package/dist/src/gateway/chat-stream/mapper.js +16 -0
- package/dist/src/gateway/hono/routes/understanding-sources.js +4 -1
- package/dist/src/gateway/hono/routes/voice.js +1 -1
- package/dist/src/gateway/hono/routes/you.js +29 -11
- package/dist/src/gateway/service/run-gateway-agent.js +5 -1
- package/dist/src/knowledge/index.d.ts +0 -1
- package/dist/src/knowledge/index.js +1 -2
- package/dist/src/notifications/planner.js +5 -1
- package/dist/src/storage/sqlite/index.d.ts +1 -1
- package/dist/src/storage/sqlite/index.js +2 -2
- package/dist/src/storage/sqlite/migrations/137_understanding_status_audit.sql +71 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/storage/sqlite/user-context-repository.d.ts +16 -4
- package/dist/src/storage/sqlite/user-context-repository.js +56 -12
- package/dist/src/user-context/config.d.ts +8 -0
- package/dist/src/user-context/config.js +3 -0
- package/dist/src/user-context/consolidation.js +16 -4
- package/dist/src/user-context/extraction/executor.d.ts +22 -0
- package/dist/src/user-context/extraction/executor.js +142 -0
- package/dist/src/user-context/extraction/registry.d.ts +1 -1
- package/dist/src/user-context/extraction/registry.js +5 -34
- package/dist/src/user-context/extraction/repair.js +4 -1
- package/dist/src/user-context/extraction/semantic.d.ts +18 -0
- package/dist/src/user-context/extraction/semantic.js +148 -0
- package/dist/src/user-context/understandingQuality.js +4 -0
- package/dist/src/work-discovery/analyzer.js +9 -4
- package/dist/src/work-discovery/service.d.ts +1 -0
- package/dist/src/work-discovery/service.js +5 -4
- package/dist/src/work-discovery/types.d.ts +1 -0
- package/package.json +14 -8
- package/dist/gateway/static/root/assets/settings-advanced-gate-Cco_W41x.js +0 -1
- package/dist/src/agent/memory/understanding/correction.d.ts +0 -3
- package/dist/src/agent/memory/understanding/correction.js +0 -39
- package/dist/src/knowledge/connected-understanding-pipeline.d.ts +0 -17
- 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 {
|
|
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 {
|
|
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
|
|
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<
|
|
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
|
|
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
|
|
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
|
|
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
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|
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,
|