@xopcai/xopc 0.0.225 → 0.0.228
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/{activity-store-CtTYnku4.js → activity-store-M0nAY5XB.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-dizuqu_4.js → agent-avatar-dicebear-CjGZBs4C.js} +1 -1
- package/dist/gateway/static/root/assets/agents-B0NI4_NI.js +68 -0
- package/dist/gateway/static/root/assets/{api-CLRMz2BT.js → api-BywYxpaS.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-Cnk-DtQh.js → app-management-settings-panel--SjycrXk.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-wP0rogVI.js → appearance-settings-8G-TVUtg.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-2NiqD-YP.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-Cpzsm-DM.js → archive-plugin-7_DXaMTP.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-Bd2OOoEK.js → attachment-load-Bx44zSpz.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-DNqhguyk.js → automations-page-CrKAHqc0.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-Q3g9cf0h.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-Dcma5HdI.js → binary-plugins-DjDwnHAQ.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-D-B8ThwK.js → block-editor-7aMOKoZM.js} +3 -3
- package/dist/gateway/static/root/assets/browser-settings-page-DTwPkmAC.js +8 -0
- package/dist/gateway/static/root/assets/{browser-workflow-inputs-BAPZIXrs.js → browser-workflow-inputs-8NC2qDRF.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-workflows-page-DHtaSKEh.js → browser-workflows-page-DVLrWKz_.js} +2 -2
- package/dist/gateway/static/root/assets/capabilities-settings-panel-YcP4FbN7.js +7 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-zsZQ7im3.js → capability-presets-api-o0y9WgAI.js} +1 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-AcPjHHia.js +3 -0
- package/dist/gateway/static/root/assets/{channel-recipient-api-Cp8iaaXp.js → channel-recipient-api-C5ABr73b.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-Ca94Q1D-.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-38FsOnR0.js +1 -0
- package/dist/gateway/static/root/assets/{chat-terminal-dock-Dr6Rq2ln.js → chat-terminal-dock-BR-_7KC0.js} +2 -2
- package/dist/gateway/static/root/assets/connectors-page-SGxfLTd6.js +2 -0
- package/dist/gateway/static/root/assets/{cron-expression-BiXrpzmo.js → cron-expression-D8-c-Tkw.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-BPuRtwKt.js → date-picker-kV-Jyl9J.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-BNC_W7wI.js → dependency-picker-CwKcrfGj.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-DzOpt5nI.js → desktop-pet-D9rnchu3.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-BW0brCvH.js → desktop-pet-settings-CTs5WGzk.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-Baml0uTS.js → directory-picker-path-field-CCl0aaDm.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-CBXyKmfS.js → extension-debug-page-Cau40SvC.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BBhcr_SZ.js → extension-page-BsJXdvIL.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-4rwWpN9_.js → extension-settings-page-qWqJyF7-.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-Dg-BlBe4.js → gateway-config-api-BI0SELXm.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-Du0MvK9s.js → gateway-config-swr-BWGjnmDl.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-BOuYZOYe.js → gateway-settings-6ZdOIC7F.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-JbjcHCaN.js → gateway-startup-retry-B_zrtccP.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-KBwhXeFD.js +1 -0
- package/dist/gateway/static/root/assets/home-page-BA2M4CLi.js +1 -0
- package/dist/gateway/static/root/assets/{image-generation-api-G2tfPvKP.js → image-generation-api-YKP2me5Z.js} +1 -1
- package/dist/gateway/static/root/assets/index-BX0dz1JK.js +70 -0
- package/dist/gateway/static/root/assets/index-D_hrEKXM.css +2 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-L1Vtxgp9.js → keyboard-shortcuts-settings-kHtpnyEm.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-D2nkn09R.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page--thlkBLS.js → local-apps-page-BCXa0oUa.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-DqJz-cOR.js → locale-store-CXhjfe4q.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-D3sNst0K.js +2 -0
- package/dist/gateway/static/root/assets/{management-settings-p55fR4XO.js → management-settings-tHnlfOAQ.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-split-DwMcRlBR.js → markdown-split-BMLqHHr5.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-view-DD_PgVue.css → markdown-view-CLzTdVLW.css} +1 -1
- package/dist/gateway/static/root/assets/markdown-view-DvM5PWEH.js +201 -0
- package/dist/gateway/static/root/assets/{media-plugins-O6H3GZn7.js → media-plugins-DybwqTgv.js} +1 -1
- package/dist/gateway/static/root/assets/messages-BMdD0LWk.js +3 -0
- package/dist/gateway/static/root/assets/{notes-api-CrjQbmk1.js → notes-api-BFsOGYkd.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-miO9ig9C.js → notes-page-C24SfF6r.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-CTdZc3ov.js +4 -0
- package/dist/gateway/static/root/assets/{product-open-page-BOu07nXf.js → product-open-page-DBfCVlJq.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-COHaEpbA.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-lMnST7D_.js +1 -0
- package/dist/gateway/static/root/assets/{registry-api-Bf_HDLUJ.js → registry-api-BFZrVYfy.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BIRKBvO_.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DthuRqUH.js → runtime-tools-settings-panel-BPEGTip5.js} +2 -2
- package/dist/gateway/static/root/assets/{schema-form-8W8PhXxa.js → schema-form-Dxlu60Td.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-CATO9G3y.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-Cco_W41x.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-CrsKGYiG.js → settings-form-section-ChXh3Bwk.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-WJtI0AAq.js → settings-loading-skeleton-BCAUqIe2.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-PCjwRF47.js → settings-page-D25W9Okr.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-D65CwlNC.js → setup-status-panel-DsN7Z4Fl.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-BGtvLLeZ.js → share-preview-page-CRFnUaNb.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-CAX0eNaP.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-Doy1gghe.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-C_npyryI.js → skills-page-Cl7jjTK7.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page.utils-C46Sd78k.js → skills-page.utils-BiCl4nb5.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-DomTLwki.js → system-settings-panel-BuESR7ZT.js} +1 -1
- package/dist/gateway/static/root/assets/task-detail-page-BRiy8NL3.js +1 -0
- package/dist/gateway/static/root/assets/{theme-store-Ce7wq__X.js → theme-store-DOdCjyuw.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-Do9mCRcY.js → use-autosave-Dg1mgJ_l.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-CqoADNKu.js +1 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-Bc5p-0tX.js → voice-api-key-field-BDNexlVR.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-BieqsmT1.js → work-discovery-overlay-BHTfeaWL.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-DVj8iYvm.js → workflow-run-setup-panel-Cf_w-NZy.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-j0P-uECz.js +5 -0
- package/dist/gateway/static/root/index.html +12 -12
- package/dist/gateway/static/root/notification-sw.js +1 -1
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/notifications.js +56 -0
- package/dist/packages/gateway-contract/src/turn-outcome.js +114 -0
- package/dist/src/agent/agent-manager.d.ts +3 -1
- package/dist/src/agent/agent-manager.js +22 -5
- package/dist/src/agent/lifecycle/timeout-wrapper.d.ts +1 -0
- package/dist/src/agent/lifecycle/timeout-wrapper.js +7 -6
- package/dist/src/agent/mcp/mcp-transport-config.d.ts +1 -1
- package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
- package/dist/src/agent/memory/understanding/service.js +1 -0
- package/dist/src/agent/memory/understanding/types.d.ts +1 -0
- package/dist/src/agent/orchestration/run-agent-turn-with-timeout.d.ts +2 -2
- package/dist/src/agent/orchestration/run-agent-turn-with-timeout.js +4 -4
- package/dist/src/agent/service.d.ts +2 -0
- package/dist/src/agent/service.js +6 -0
- package/dist/src/agent/skills/index.d.ts +22 -2
- package/dist/src/agent/skills/index.js +74 -32
- package/dist/src/agent/skills/skill-sources.d.ts +0 -1
- package/dist/src/agent/skills/skill-sources.js +1 -11
- package/dist/src/agent/tools/create-share-tool.js +14 -1
- package/dist/src/agent/tools/exec-command.js +3 -3
- package/dist/src/agent/tools/execute-code-tool.js +1 -1
- package/dist/src/agent/tools/executor.js +2 -2
- package/dist/src/agent/tools/image-generate-tool.js +12 -0
- package/dist/src/agent/tools/send-media.js +11 -0
- package/dist/src/agent/tools/xopc-use-tool.js +1 -1
- package/dist/src/cli/command-loaders.js +1 -0
- package/dist/src/cli/command-manifest.js +4 -0
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/history.d.ts +1 -0
- package/dist/src/cli/commands/history.js +50 -0
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/connectors/connected-source-understanding.js +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/chat-stream/protocol.d.ts +3 -2
- package/dist/src/gateway/chat-stream/turn-outcome-collector.d.ts +16 -0
- package/dist/src/gateway/chat-stream/turn-outcome-collector.js +111 -0
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/lib/static-ui.js +7 -5
- package/dist/src/gateway/hono/routes/connectors.js +18 -16
- package/dist/src/gateway/hono/routes/index.js +2 -0
- package/dist/src/gateway/hono/routes/mobile.js +18 -40
- package/dist/src/gateway/hono/routes/notifications.d.ts +3 -0
- package/dist/src/gateway/hono/routes/notifications.js +45 -0
- package/dist/src/gateway/hono/routes/project-skills.js +30 -2
- package/dist/src/gateway/hono/routes/sessions.js +31 -0
- package/dist/src/gateway/hono/routes/tasks.js +1 -1
- package/dist/src/gateway/hono/routes/voice.js +1 -1
- package/dist/src/gateway/hono/routes/you.js +52 -0
- package/dist/src/gateway/service/agent-runner.js +6 -2
- package/dist/src/gateway/service/run-gateway-agent.js +53 -1
- package/dist/src/gateway/service/sessions-api.d.ts +6 -0
- package/dist/src/gateway/service/sessions-api.js +60 -3
- package/dist/src/gateway/service.d.ts +2 -2
- package/dist/src/gateway/service.js +8 -7
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/history/ctx/exporter.d.ts +14 -0
- package/dist/src/history/ctx/exporter.js +99 -0
- package/dist/src/history/ctx/format.d.ts +25 -0
- package/dist/src/history/ctx/format.js +282 -0
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/knowledge/connected-understanding-pipeline.js +12 -3
- package/dist/src/knowledge/index.d.ts +0 -1
- package/dist/src/knowledge/index.js +1 -2
- package/dist/src/media/session-references.d.ts +6 -0
- package/dist/src/media/session-references.js +24 -3
- package/dist/src/notifications/device-store.d.ts +16 -0
- package/dist/src/notifications/device-store.js +96 -0
- package/dist/src/notifications/planner.d.ts +6 -0
- package/dist/src/notifications/planner.js +156 -0
- package/dist/src/notifications/service.d.ts +18 -0
- package/dist/src/notifications/service.js +184 -0
- package/dist/src/notifications/store.d.ts +53 -0
- package/dist/src/notifications/store.js +184 -0
- package/dist/src/notifications/types.d.ts +29 -0
- package/dist/src/notifications/types.js +14 -0
- package/dist/src/projects/project-skill-service.d.ts +28 -6
- package/dist/src/projects/project-skill-service.js +76 -29
- package/dist/src/session/client-history.d.ts +3 -1
- package/dist/src/session/client-history.js +13 -0
- package/dist/src/session/index.d.ts +1 -1
- package/dist/src/session/manager.d.ts +1 -0
- package/dist/src/session/manager.js +6 -0
- package/dist/src/session/store.d.ts +15 -0
- package/dist/src/session/store.js +58 -0
- package/dist/src/storage/sqlite/index.d.ts +2 -1
- package/dist/src/storage/sqlite/index.js +3 -2
- package/dist/src/storage/sqlite/migrations/135_user_people.sql +59 -0
- package/dist/src/storage/sqlite/migrations/136_product_notifications.sql +79 -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/session-input-repository.d.ts +5 -0
- package/dist/src/storage/sqlite/session-input-repository.js +9 -1
- package/dist/src/storage/sqlite/user-people-repository.d.ts +32 -0
- package/dist/src/storage/sqlite/user-people-repository.js +246 -0
- package/dist/src/tasks/index.js +2 -2
- package/dist/src/tasks/task-application-service.js +42 -6
- package/dist/src/tasks/task-change-events.d.ts +9 -0
- package/dist/src/tasks/task-change-events.js +17 -1
- package/dist/src/tasks/task-outbox-dispatcher.js +5 -1
- package/dist/src/tasks/task-run-dispatcher.js +4 -2
- package/dist/src/tasks/task-run-repository.js +13 -0
- package/dist/src/user-context/relationships/indexer.d.ts +13 -0
- package/dist/src/user-context/relationships/indexer.js +185 -0
- package/dist/src/user-context/relationships/service.d.ts +22 -0
- package/dist/src/user-context/relationships/service.js +50 -0
- package/dist/src/user-context/relationships/types.d.ts +55 -0
- package/dist/src/user-context/relationships/types.js +10 -0
- package/dist/src/user-context/sources/repository.d.ts +1 -1
- package/dist/src/user-context/sources/repository.js +8 -2
- package/dist/src/work-discovery/service.js +1 -1
- package/package.json +2 -2
- package/dist/gateway/static/root/assets/agents-DeEEFZJ4.js +0 -68
- package/dist/gateway/static/root/assets/apps-page-Db5YNHVM.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace-CNT05YTb.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-UbDj3LIq.js +0 -8
- package/dist/gateway/static/root/assets/capabilities-settings-panel-CiO152QS.js +0 -7
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-Bc_hRwfL.js +0 -3
- package/dist/gateway/static/root/assets/channels-settings-CFW7zBoT.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-5rNVq7iI.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-DLBr5nre.js +0 -2
- package/dist/gateway/static/root/assets/heartbeat-settings-Dq4YAlSx.js +0 -1
- package/dist/gateway/static/root/assets/home-page-BIXkO0Ck.js +0 -1
- package/dist/gateway/static/root/assets/index-Csccu8sa.js +0 -74
- package/dist/gateway/static/root/assets/index-D3_xSYKd.css +0 -2
- package/dist/gateway/static/root/assets/local-app-workbench-page-D-eacBcO.js +0 -2
- package/dist/gateway/static/root/assets/logs-page-BRCglHvd.js +0 -2
- package/dist/gateway/static/root/assets/markdown-view-CTB9u15e.js +0 -69
- package/dist/gateway/static/root/assets/mermaid-render-BbqDc_LQ.js +0 -134
- package/dist/gateway/static/root/assets/messages-CeydamsI.js +0 -3
- package/dist/gateway/static/root/assets/notes-workbench-_13lmvmF.js +0 -4
- package/dist/gateway/static/root/assets/project-detail-page-BStRkOQr.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-CY03RxVr.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-B19llkld.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-BDA2qbTd.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BONIDX4v.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-CdzVKrBl.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-CsRWid5h.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-B6YFX7kK.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-CiCQA40E.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-AG04TTuH.js +0 -5
- package/dist/src/knowledge/people-graph.d.ts +0 -25
- package/dist/src/knowledge/people-graph.js +0 -92
- package/dist/src/mobile/notification-service.d.ts +0 -9
- package/dist/src/mobile/notification-service.js +0 -156
- package/dist/src/mobile/notification-store.d.ts +0 -28
- package/dist/src/mobile/notification-store.js +0 -132
- package/dist/src/mobile/notification-types.d.ts +0 -37
- package/dist/src/mobile/notification-types.js +0 -9
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
//#region src/history/ctx/format.ts
|
|
3
|
+
const CTX_HISTORY_SCHEMA_VERSION = "ctx-history-jsonl-v2";
|
|
4
|
+
const CTX_PROVIDER_KEY = "xopc";
|
|
5
|
+
const CTX_SOURCE_ID = "default";
|
|
6
|
+
const CTX_SOURCE_FORMAT = "xopc-session-history-v1";
|
|
7
|
+
const EVENT_INDEX_STRIDE = 65536;
|
|
8
|
+
const MAX_TEXT_CHUNK_BYTES = 1024 * 1024;
|
|
9
|
+
const MAX_JSONL_LINE_BYTES = 16 * 1024 * 1024;
|
|
10
|
+
function asRecord(value) {
|
|
11
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
12
|
+
}
|
|
13
|
+
function stringValue(value) {
|
|
14
|
+
return typeof value === "string" ? value : void 0;
|
|
15
|
+
}
|
|
16
|
+
function timestamp(value, label) {
|
|
17
|
+
const date = new Date(value);
|
|
18
|
+
if (!Number.isFinite(value) || Number.isNaN(date.getTime())) throw new Error(`Invalid ${label}: ${String(value)}`);
|
|
19
|
+
return date.toISOString();
|
|
20
|
+
}
|
|
21
|
+
function stringifyValue(value) {
|
|
22
|
+
if (typeof value === "string") return value;
|
|
23
|
+
if (value === void 0) return "";
|
|
24
|
+
return JSON.stringify(value) ?? "";
|
|
25
|
+
}
|
|
26
|
+
function textParts(content) {
|
|
27
|
+
if (typeof content === "string") return content.length > 0 ? [content] : [];
|
|
28
|
+
if (!Array.isArray(content)) return [];
|
|
29
|
+
return content.flatMap((block) => {
|
|
30
|
+
const record = asRecord(block);
|
|
31
|
+
return record?.type === "text" && typeof record.text === "string" && record.text.length > 0 ? [record.text] : [];
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function outputText(output) {
|
|
35
|
+
if (typeof output === "string") return output;
|
|
36
|
+
return textParts(output).join("");
|
|
37
|
+
}
|
|
38
|
+
function toolCallDetails(block) {
|
|
39
|
+
const toolName = stringValue(block.name);
|
|
40
|
+
if (!toolName) return null;
|
|
41
|
+
const toolCallId = stringValue(block.id);
|
|
42
|
+
const argumentsText = stringifyValue(block.arguments);
|
|
43
|
+
return {
|
|
44
|
+
eventType: "tool_call",
|
|
45
|
+
role: "assistant",
|
|
46
|
+
text: argumentsText ? `${toolName} ${argumentsText}` : toolName,
|
|
47
|
+
details: {
|
|
48
|
+
tool_name: toolName,
|
|
49
|
+
...toolCallId ? { tool_call_id: toolCallId } : {}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function normalizeAssistant(row) {
|
|
54
|
+
if (typeof row.content === "string") return row.content.length > 0 ? [{
|
|
55
|
+
eventType: "message",
|
|
56
|
+
role: "assistant",
|
|
57
|
+
text: row.content
|
|
58
|
+
}] : [];
|
|
59
|
+
if (!Array.isArray(row.content)) return [];
|
|
60
|
+
const events = [];
|
|
61
|
+
for (const block of row.content) {
|
|
62
|
+
const record = asRecord(block);
|
|
63
|
+
if (!record) continue;
|
|
64
|
+
if (record.type === "text" && typeof record.text === "string" && record.text.length > 0) {
|
|
65
|
+
events.push({
|
|
66
|
+
eventType: "message",
|
|
67
|
+
role: "assistant",
|
|
68
|
+
text: record.text
|
|
69
|
+
});
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (record.type === "toolCall") {
|
|
73
|
+
const event = toolCallDetails(record);
|
|
74
|
+
if (event) events.push(event);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return events;
|
|
78
|
+
}
|
|
79
|
+
function normalizeToolResult(row) {
|
|
80
|
+
const text = textParts(row.content).join("");
|
|
81
|
+
if (!text) return [];
|
|
82
|
+
const toolName = stringValue(row.toolName);
|
|
83
|
+
const toolCallId = stringValue(row.toolCallId);
|
|
84
|
+
return [{
|
|
85
|
+
eventType: "tool_output",
|
|
86
|
+
role: "tool",
|
|
87
|
+
text,
|
|
88
|
+
details: {
|
|
89
|
+
...toolName ? { tool_name: toolName } : {},
|
|
90
|
+
...toolCallId ? { tool_call_id: toolCallId } : {},
|
|
91
|
+
...typeof row.isError === "boolean" ? { is_error: row.isError } : {}
|
|
92
|
+
}
|
|
93
|
+
}];
|
|
94
|
+
}
|
|
95
|
+
function normalizeBashExecution(row) {
|
|
96
|
+
const command = stringValue(row.command);
|
|
97
|
+
if (!command) return [];
|
|
98
|
+
const events = [{
|
|
99
|
+
eventType: "command_started",
|
|
100
|
+
role: "user",
|
|
101
|
+
text: command
|
|
102
|
+
}];
|
|
103
|
+
const output = outputText(row.output);
|
|
104
|
+
if (output) events.push({
|
|
105
|
+
eventType: "command_output",
|
|
106
|
+
role: "tool",
|
|
107
|
+
text: output
|
|
108
|
+
});
|
|
109
|
+
events.push({
|
|
110
|
+
eventType: "command_finished",
|
|
111
|
+
role: "tool",
|
|
112
|
+
text: row.exitCode == null ? "Command finished" : `Command exited with code ${String(row.exitCode)}`,
|
|
113
|
+
details: {
|
|
114
|
+
...typeof row.exitCode === "number" ? { exit_code: row.exitCode } : {},
|
|
115
|
+
...typeof row.signal === "string" ? { signal: row.signal } : {},
|
|
116
|
+
...typeof row.truncated === "boolean" ? { truncated: row.truncated } : {}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
return events;
|
|
120
|
+
}
|
|
121
|
+
function normalizeStoredRow(row) {
|
|
122
|
+
if (row.kind === "context" || row.role === "system") return [];
|
|
123
|
+
if (row.type === "compaction" && typeof row.summary === "string" && row.summary.length > 0) return [{
|
|
124
|
+
eventType: "summary",
|
|
125
|
+
role: "assistant",
|
|
126
|
+
text: row.summary
|
|
127
|
+
}];
|
|
128
|
+
if ((row.role === "branchSummary" || row.role === "compactionSummary") && typeof row.summary === "string" && row.summary.length > 0) return [{
|
|
129
|
+
eventType: "summary",
|
|
130
|
+
role: "assistant",
|
|
131
|
+
text: row.summary
|
|
132
|
+
}];
|
|
133
|
+
if (row.role === "bashExecution") return normalizeBashExecution(row);
|
|
134
|
+
if (row.role === "custom" || row.type === "custom_message") {
|
|
135
|
+
if (row.display === false) return [];
|
|
136
|
+
return textParts(row.content).map((text) => ({
|
|
137
|
+
eventType: "message",
|
|
138
|
+
role: "user",
|
|
139
|
+
text
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
if (row.role === "user") return textParts(row.content).map((text) => ({
|
|
143
|
+
eventType: "message",
|
|
144
|
+
role: "user",
|
|
145
|
+
text
|
|
146
|
+
}));
|
|
147
|
+
if (row.role === "assistant") return normalizeAssistant(row);
|
|
148
|
+
if (row.role === "toolResult" || row.role === "tool") return normalizeToolResult(row);
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
function splitTextByUtf8Bytes(text) {
|
|
152
|
+
if (Buffer.byteLength(text, "utf8") <= MAX_TEXT_CHUNK_BYTES) return [text];
|
|
153
|
+
const chunks = [];
|
|
154
|
+
let start = 0;
|
|
155
|
+
while (start < text.length) {
|
|
156
|
+
let low = start + 1;
|
|
157
|
+
let high = text.length;
|
|
158
|
+
let end = low;
|
|
159
|
+
while (low <= high) {
|
|
160
|
+
const middle = Math.floor((low + high) / 2);
|
|
161
|
+
if (Buffer.byteLength(text.slice(start, middle), "utf8") <= MAX_TEXT_CHUNK_BYTES) {
|
|
162
|
+
end = middle;
|
|
163
|
+
low = middle + 1;
|
|
164
|
+
} else high = middle - 1;
|
|
165
|
+
}
|
|
166
|
+
const previous = text.charCodeAt(end - 1);
|
|
167
|
+
const next = text.charCodeAt(end);
|
|
168
|
+
if (previous >= 55296 && previous <= 56319 && next >= 56320 && next <= 57343) end -= 1;
|
|
169
|
+
chunks.push(text.slice(start, end));
|
|
170
|
+
start = end;
|
|
171
|
+
}
|
|
172
|
+
return chunks;
|
|
173
|
+
}
|
|
174
|
+
function stableEventId(sessionId, entryId, eventOffset) {
|
|
175
|
+
return `xopc:${createHash("sha256").update(sessionId).update("\0").update(entryId).update("\0").update(String(eventOffset)).digest("hex")}`;
|
|
176
|
+
}
|
|
177
|
+
function serializeRecord(record) {
|
|
178
|
+
const line = JSON.stringify(record);
|
|
179
|
+
const size = Buffer.byteLength(line, "utf8");
|
|
180
|
+
if (size > MAX_JSONL_LINE_BYTES) throw new Error(`ctx history record exceeds 16 MiB (${size} bytes)`);
|
|
181
|
+
return line;
|
|
182
|
+
}
|
|
183
|
+
function sessionRecord(session) {
|
|
184
|
+
const active = session.status === "active";
|
|
185
|
+
return {
|
|
186
|
+
record_type: "session",
|
|
187
|
+
source_id: CTX_SOURCE_ID,
|
|
188
|
+
provider_session_id: session.sessionId,
|
|
189
|
+
started_at: timestamp(session.createdAt, `session timestamp for ${session.sessionId}`),
|
|
190
|
+
external_agent_id: session.agentId,
|
|
191
|
+
cwd: session.cwd,
|
|
192
|
+
...session.archivedAt === null ? {} : { ended_at: timestamp(session.archivedAt, `session end timestamp for ${session.sessionId}`) },
|
|
193
|
+
status: active ? "active" : "completed",
|
|
194
|
+
agent_scope: session.sessionType === "workflow-subagent" ? "subagent" : "primary"
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function entryRecords(session, entry) {
|
|
198
|
+
let parsed;
|
|
199
|
+
try {
|
|
200
|
+
parsed = JSON.parse(entry.payloadJson);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
203
|
+
throw new Error(`Invalid transcript payload for entry ${entry.entryId}: ${message}`);
|
|
204
|
+
}
|
|
205
|
+
const row = asRecord(parsed);
|
|
206
|
+
if (!row) throw new Error(`Transcript payload for entry ${entry.entryId} is not an object`);
|
|
207
|
+
const occurredAt = timestamp(entry.createdAt, `entry timestamp for ${entry.entryId}`);
|
|
208
|
+
const records = [];
|
|
209
|
+
let offset = 0;
|
|
210
|
+
for (const event of normalizeStoredRow(row)) {
|
|
211
|
+
const chunks = splitTextByUtf8Bytes(event.text);
|
|
212
|
+
for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex += 1) {
|
|
213
|
+
if (offset >= EVENT_INDEX_STRIDE) throw new Error(`Transcript entry ${entry.entryId} expands to too many ctx events`);
|
|
214
|
+
const eventIndex = entry.seq * EVENT_INDEX_STRIDE + offset;
|
|
215
|
+
if (!Number.isSafeInteger(eventIndex) || eventIndex < 0) throw new Error(`Transcript sequence is outside the supported range: ${entry.seq}`);
|
|
216
|
+
records.push({
|
|
217
|
+
record_type: "event",
|
|
218
|
+
source_id: CTX_SOURCE_ID,
|
|
219
|
+
provider_session_id: session.sessionId,
|
|
220
|
+
event_index: eventIndex,
|
|
221
|
+
event_id: stableEventId(session.sessionId, entry.entryId, offset),
|
|
222
|
+
occurred_at: occurredAt,
|
|
223
|
+
event_type: event.eventType,
|
|
224
|
+
...event.role ? { role: event.role } : {},
|
|
225
|
+
payload: {
|
|
226
|
+
text: chunks[chunkIndex],
|
|
227
|
+
...event.details,
|
|
228
|
+
...chunks.length > 1 ? {
|
|
229
|
+
chunk_index: chunkIndex,
|
|
230
|
+
chunk_count: chunks.length
|
|
231
|
+
} : {}
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
offset += 1;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return records;
|
|
238
|
+
}
|
|
239
|
+
function buildCtxHistoryJsonl(sessions) {
|
|
240
|
+
const lines = [serializeRecord({
|
|
241
|
+
record_type: "manifest",
|
|
242
|
+
schema_version: CTX_HISTORY_SCHEMA_VERSION,
|
|
243
|
+
producer: "xopc"
|
|
244
|
+
}), serializeRecord({
|
|
245
|
+
record_type: "source",
|
|
246
|
+
source_id: CTX_SOURCE_ID,
|
|
247
|
+
provider_key: CTX_PROVIDER_KEY,
|
|
248
|
+
source_format: CTX_SOURCE_FORMAT
|
|
249
|
+
})];
|
|
250
|
+
let eventCount = 0;
|
|
251
|
+
for (const session of sessions) {
|
|
252
|
+
lines.push(serializeRecord(sessionRecord(session)));
|
|
253
|
+
for (const entry of session.entries) {
|
|
254
|
+
const records = entryRecords(session, entry);
|
|
255
|
+
eventCount += records.length;
|
|
256
|
+
lines.push(...records.map(serializeRecord));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
contents: `${lines.join("\n")}\n`,
|
|
261
|
+
eventCount
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function buildCtxPluginManifest() {
|
|
265
|
+
return `${JSON.stringify({
|
|
266
|
+
schema_version: 1,
|
|
267
|
+
name: "xopc",
|
|
268
|
+
display_name: "XOPC history",
|
|
269
|
+
version: "1.0.0",
|
|
270
|
+
history_sources: [{
|
|
271
|
+
id: CTX_SOURCE_ID,
|
|
272
|
+
provider_key: CTX_PROVIDER_KEY,
|
|
273
|
+
source_id: CTX_SOURCE_ID,
|
|
274
|
+
source_format: CTX_SOURCE_FORMAT,
|
|
275
|
+
path: "history.jsonl",
|
|
276
|
+
enabled: true,
|
|
277
|
+
refresh: "manual"
|
|
278
|
+
}]
|
|
279
|
+
}, null, 2)}\n`;
|
|
280
|
+
}
|
|
281
|
+
//#endregion
|
|
282
|
+
export { CTX_HISTORY_SCHEMA_VERSION, CTX_PROVIDER_KEY, CTX_SOURCE_FORMAT, CTX_SOURCE_ID, buildCtxHistoryJsonl, buildCtxPluginManifest };
|
|
@@ -2,8 +2,8 @@ import { createLogger } from "../utils/logger/index.js";
|
|
|
2
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
3
|
import { init_paths, resolveConfigPath } from "../config/paths.js";
|
|
4
4
|
import { init_loader, loadConfig } from "../config/loader.js";
|
|
5
|
-
import { resolveGatewayService } from "../daemon/service.js";
|
|
6
5
|
import { isRestartEnabled } from "../config/commands.flags.js";
|
|
6
|
+
import { resolveGatewayService } from "../daemon/service.js";
|
|
7
7
|
import { authorizeGatewaySigusr1Restart, writeGatewayRestartIntentSync } from "./restart.js";
|
|
8
8
|
import { findVerifiedGatewayListenerPidsOnPortSync, signalVerifiedGatewayPidSync } from "./gateway-processes.js";
|
|
9
9
|
import { refreshGatewayServiceAfterUpdate } from "./update-service-refresh.js";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { listKnowledgeSourceItems } from "../storage/sqlite/knowledge-repository.js";
|
|
2
2
|
import "../storage/sqlite/index.js";
|
|
3
3
|
import { clusterUnderstandingSignals } from "../user-context/sources/signal-clustering.js";
|
|
4
|
+
import { personIdForIdentity, personIdentityKey, rebuildUserPeopleIndex } from "../user-context/relationships/indexer.js";
|
|
4
5
|
//#region src/knowledge/connected-understanding-pipeline.ts
|
|
5
6
|
function logicalEventKey(item) {
|
|
6
7
|
const value = item.metadata.logicalEventKey;
|
|
@@ -96,9 +97,12 @@ function relationshipObservations(items) {
|
|
|
96
97
|
const strongIdentity = (signal.email ?? signal.username)?.toLowerCase();
|
|
97
98
|
if (strongIdentity && owners.has(strongIdentity)) continue;
|
|
98
99
|
const label = signal.name ?? signal.email ?? signal.username;
|
|
99
|
-
const identityKey = signal
|
|
100
|
+
const identityKey = personIdentityKey(signal, item.sourceInstanceId);
|
|
101
|
+
const personId = personIdForIdentity(signal, item.sourceInstanceId);
|
|
102
|
+
if (!identityKey || !personId) continue;
|
|
100
103
|
const entry = grouped.get(identityKey) ?? {
|
|
101
104
|
label,
|
|
105
|
+
personId,
|
|
102
106
|
events: /* @__PURE__ */ new Map()
|
|
103
107
|
};
|
|
104
108
|
const eventKey = logicalEventKey(item);
|
|
@@ -108,8 +112,11 @@ function relationshipObservations(items) {
|
|
|
108
112
|
}
|
|
109
113
|
return [...grouped.values()].sort((left, right) => right.events.size - left.events.size).slice(0, 8).map((entry) => ({
|
|
110
114
|
class: "relationship",
|
|
111
|
-
key: `person:${entry.
|
|
112
|
-
value: {
|
|
115
|
+
key: `person:${entry.personId}`,
|
|
116
|
+
value: {
|
|
117
|
+
label: entry.label,
|
|
118
|
+
personId: entry.personId
|
|
119
|
+
},
|
|
113
120
|
items: [...entry.events.values()],
|
|
114
121
|
windowDays: 90
|
|
115
122
|
}));
|
|
@@ -182,6 +189,7 @@ function renderConnectedObservation(observation) {
|
|
|
182
189
|
kind: "relationship",
|
|
183
190
|
content: `Frequently collaborates with ${label}.`,
|
|
184
191
|
canonicalKey: `connected:${observation.key}`,
|
|
192
|
+
payload: typeof observation.value.personId === "string" ? { personId: observation.value.personId } : void 0,
|
|
185
193
|
confidence,
|
|
186
194
|
importance: .65,
|
|
187
195
|
explicitness: "inferred",
|
|
@@ -227,6 +235,7 @@ var ConnectedUnderstandingPipeline = class {
|
|
|
227
235
|
this.memoryManager = memoryManager;
|
|
228
236
|
}
|
|
229
237
|
async process(agentId, extractionRunId) {
|
|
238
|
+
rebuildUserPeopleIndex();
|
|
230
239
|
const observations = deriveConnectedClaimObservations(listKnowledgeSourceItems({
|
|
231
240
|
agentId,
|
|
232
241
|
includeDeleted: false,
|
|
@@ -3,5 +3,4 @@ import { ConnectedKnowledgePipeline } from "./connected-knowledge-pipeline.js";
|
|
|
3
3
|
import { ConnectedUnderstandingPipeline, deriveConnectedClaimObservations, renderConnectedObservation } from "./connected-understanding-pipeline.js";
|
|
4
4
|
import { startConnectedKnowledgeCoordinator } from "./coordinator.js";
|
|
5
5
|
import { LocalFolderKnowledgeSourceAdapter } from "./local-folder-adapter.js";
|
|
6
|
-
|
|
7
|
-
export { ConnectedKnowledgePipeline, ConnectedUnderstandingPipeline, KnowledgeIngestionService, LocalFolderKnowledgeSourceAdapter, SqliteKnowledgeSourceStateStore, buildConnectedPeopleGraph, deriveConnectedClaimObservations, renderConnectedObservation, startConnectedKnowledgeCoordinator };
|
|
6
|
+
export { ConnectedKnowledgePipeline, ConnectedUnderstandingPipeline, KnowledgeIngestionService, LocalFolderKnowledgeSourceAdapter, SqliteKnowledgeSourceStateStore, deriveConnectedClaimObservations, renderConnectedObservation, startConnectedKnowledgeCoordinator };
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { AgentMessage } from '@earendil-works/pi-agent-core';
|
|
2
|
+
export declare function collectMediaUrisFromValues(values: readonly unknown[]): Set<string>;
|
|
2
3
|
export declare function collectMediaUrisFromMessages(messages: readonly AgentMessage[]): Set<string>;
|
|
3
4
|
export declare function messagesReferenceMediaUri(messages: readonly AgentMessage[], uri: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* A media object remains live while any non-deleted session references it,
|
|
7
|
+
* including archived transcripts retained by a reset of that session key.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isMediaUriReferencedByLiveSession(uri: string): boolean;
|
|
4
10
|
export declare function deleteMediaUris(uris: Iterable<string>): Promise<void>;
|
|
5
11
|
export declare function deleteMediaUrisNoLongerReferenced(params: {
|
|
6
12
|
removed: readonly AgentMessage[];
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getSqliteDatabase } from "../storage/sqlite/transaction.js";
|
|
2
|
+
import "../storage/sqlite/index.js";
|
|
1
3
|
import { tryParseMediaUri } from "./uri.js";
|
|
2
4
|
import { deleteMediaBuffer } from "./store.js";
|
|
3
5
|
//#region src/media/session-references.ts
|
|
@@ -19,19 +21,38 @@ function collectFromValue(value, out, seen) {
|
|
|
19
21
|
const record = value;
|
|
20
22
|
for (const nested of Object.values(record)) collectFromValue(nested, out, seen);
|
|
21
23
|
}
|
|
22
|
-
function
|
|
24
|
+
function collectMediaUrisFromValues(values) {
|
|
23
25
|
const out = /* @__PURE__ */ new Set();
|
|
24
26
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
25
|
-
for (const
|
|
27
|
+
for (const value of values) collectFromValue(value, out, seen);
|
|
26
28
|
return out;
|
|
27
29
|
}
|
|
30
|
+
function collectMediaUrisFromMessages(messages) {
|
|
31
|
+
return collectMediaUrisFromValues(messages);
|
|
32
|
+
}
|
|
28
33
|
function messagesReferenceMediaUri(messages, uri) {
|
|
29
34
|
return collectMediaUrisFromMessages(messages).has(uri.trim());
|
|
30
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* A media object remains live while any non-deleted session references it,
|
|
38
|
+
* including archived transcripts retained by a reset of that session key.
|
|
39
|
+
*/
|
|
40
|
+
function isMediaUriReferencedByLiveSession(uri) {
|
|
41
|
+
const normalized = uri.trim();
|
|
42
|
+
if (!normalized) return false;
|
|
43
|
+
const escaped = JSON.stringify(normalized).replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_");
|
|
44
|
+
return getSqliteDatabase().prepare(`SELECT 1 AS referenced
|
|
45
|
+
FROM transcript_entries e
|
|
46
|
+
JOIN transcripts t ON t.session_id = e.session_id
|
|
47
|
+
JOIN sessions s ON s.session_key = t.session_key
|
|
48
|
+
WHERE e.payload_json LIKE ? ESCAPE '\\'
|
|
49
|
+
LIMIT 1`).get(`%${escaped}%`)?.referenced === 1;
|
|
50
|
+
}
|
|
31
51
|
async function deleteMediaUris(uris) {
|
|
32
52
|
for (const uri of new Set(uris)) {
|
|
33
53
|
const parsed = tryParseMediaUri(uri);
|
|
34
54
|
if (!parsed) continue;
|
|
55
|
+
if (isMediaUriReferencedByLiveSession(uri)) continue;
|
|
35
56
|
await deleteMediaBuffer(parsed.id, parsed.bucket).catch(() => {});
|
|
36
57
|
}
|
|
37
58
|
}
|
|
@@ -43,4 +64,4 @@ async function deleteMediaUrisNoLongerReferenced(params) {
|
|
|
43
64
|
await deleteMediaUris(removedUris);
|
|
44
65
|
}
|
|
45
66
|
//#endregion
|
|
46
|
-
export { collectMediaUrisFromMessages, deleteMediaUris, deleteMediaUrisNoLongerReferenced, messagesReferenceMediaUri };
|
|
67
|
+
export { collectMediaUrisFromMessages, collectMediaUrisFromValues, deleteMediaUris, deleteMediaUrisNoLongerReferenced, isMediaUriReferencedByLiveSession, messagesReferenceMediaUri };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type NotificationDevice, type NotificationDevicePlatform, type NotificationLanguage, type NotificationPermission, type NotificationPreferences } from './types.js';
|
|
2
|
+
export type RegisterNotificationDeviceInput = {
|
|
3
|
+
id: string;
|
|
4
|
+
platform: NotificationDevicePlatform;
|
|
5
|
+
pushToken: string;
|
|
6
|
+
permissions: NotificationPermission;
|
|
7
|
+
preferences?: Partial<NotificationPreferences>;
|
|
8
|
+
locale: NotificationLanguage;
|
|
9
|
+
appVersion?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function registerNotificationDevice(input: RegisterNotificationDeviceInput): NotificationDevice;
|
|
12
|
+
export declare function getNotificationDevice(deviceId: string): NotificationDevice | null;
|
|
13
|
+
export declare function listDeliverableNotificationDevices(now?: number): NotificationDevice[];
|
|
14
|
+
export declare function updateNotificationDevicePreferences(deviceId: string, patch: Partial<NotificationPreferences>): NotificationDevice | null;
|
|
15
|
+
export declare function removeNotificationDevice(deviceId: string): boolean;
|
|
16
|
+
export declare function disableNotificationDeviceForPushToken(pushToken: string): void;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { getSqliteDatabase, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
|
|
2
|
+
import { DEFAULT_NOTIFICATION_PREFERENCES } from "./types.js";
|
|
3
|
+
//#region src/notifications/device-store.ts
|
|
4
|
+
const DEVICE_LEASE_MS = 10080 * 60 * 1e3;
|
|
5
|
+
function parsePreferences(raw) {
|
|
6
|
+
try {
|
|
7
|
+
const value = JSON.parse(raw);
|
|
8
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
9
|
+
} catch {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function normalizePreferences(input) {
|
|
14
|
+
return {
|
|
15
|
+
chatCompleted: input?.chatCompleted ?? DEFAULT_NOTIFICATION_PREFERENCES.chatCompleted,
|
|
16
|
+
chatFailed: input?.chatFailed ?? DEFAULT_NOTIFICATION_PREFERENCES.chatFailed,
|
|
17
|
+
taskNeedsInput: input?.taskNeedsInput ?? DEFAULT_NOTIFICATION_PREFERENCES.taskNeedsInput,
|
|
18
|
+
taskBlocked: input?.taskBlocked ?? DEFAULT_NOTIFICATION_PREFERENCES.taskBlocked,
|
|
19
|
+
taskFailed: input?.taskFailed ?? DEFAULT_NOTIFICATION_PREFERENCES.taskFailed,
|
|
20
|
+
taskCompleted: input?.taskCompleted ?? DEFAULT_NOTIFICATION_PREFERENCES.taskCompleted,
|
|
21
|
+
automationCompleted: input?.automationCompleted ?? DEFAULT_NOTIFICATION_PREFERENCES.automationCompleted,
|
|
22
|
+
automationFailed: input?.automationFailed ?? DEFAULT_NOTIFICATION_PREFERENCES.automationFailed,
|
|
23
|
+
proactiveInsight: input?.proactiveInsight ?? DEFAULT_NOTIFICATION_PREFERENCES.proactiveInsight
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function deviceFromRow(row) {
|
|
27
|
+
return {
|
|
28
|
+
id: row.device_id,
|
|
29
|
+
platform: row.platform === "android" ? "android" : "ios",
|
|
30
|
+
pushToken: row.push_token,
|
|
31
|
+
enabled: row.enabled !== 0,
|
|
32
|
+
permissions: row.permissions === "granted" || row.permissions === "denied" ? row.permissions : "unknown",
|
|
33
|
+
preferences: normalizePreferences(parsePreferences(row.preferences_json)),
|
|
34
|
+
locale: row.locale === "zh" ? "zh" : "en",
|
|
35
|
+
appVersion: row.app_version ?? void 0,
|
|
36
|
+
leaseExpiresAt: row.lease_expires_at,
|
|
37
|
+
lastSeenAt: row.last_seen_at,
|
|
38
|
+
createdAt: row.created_at,
|
|
39
|
+
updatedAt: row.updated_at
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function registerNotificationDevice(input) {
|
|
43
|
+
const now = Date.now();
|
|
44
|
+
const preferences = input.preferences ? normalizePreferences(input.preferences) : getNotificationDevice(input.id)?.preferences ?? normalizePreferences();
|
|
45
|
+
runSqliteWriteTransaction((db) => {
|
|
46
|
+
db.prepare(`DELETE FROM notification_devices
|
|
47
|
+
WHERE push_provider = 'expo' AND push_token = ? AND device_id <> ?`).run(input.pushToken, input.id);
|
|
48
|
+
db.prepare(`INSERT INTO notification_devices (
|
|
49
|
+
device_id, platform, push_token, enabled, permissions, preferences_json,
|
|
50
|
+
locale, app_version, lease_expires_at, last_seen_at, created_at, updated_at
|
|
51
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
52
|
+
ON CONFLICT(device_id) DO UPDATE SET
|
|
53
|
+
platform = excluded.platform,
|
|
54
|
+
push_token = excluded.push_token,
|
|
55
|
+
enabled = excluded.enabled,
|
|
56
|
+
permissions = excluded.permissions,
|
|
57
|
+
preferences_json = excluded.preferences_json,
|
|
58
|
+
locale = excluded.locale,
|
|
59
|
+
app_version = excluded.app_version,
|
|
60
|
+
lease_expires_at = excluded.lease_expires_at,
|
|
61
|
+
last_seen_at = excluded.last_seen_at,
|
|
62
|
+
updated_at = excluded.updated_at`).run(input.id, input.platform, input.pushToken, input.permissions === "granted" ? 1 : 0, input.permissions, JSON.stringify(preferences), input.locale, input.appVersion?.trim() || null, now + DEVICE_LEASE_MS, now, now, now);
|
|
63
|
+
});
|
|
64
|
+
return getNotificationDevice(input.id);
|
|
65
|
+
}
|
|
66
|
+
function getNotificationDevice(deviceId) {
|
|
67
|
+
const row = getSqliteDatabase().prepare("SELECT * FROM notification_devices WHERE device_id = ?").get(deviceId);
|
|
68
|
+
return row ? deviceFromRow(row) : null;
|
|
69
|
+
}
|
|
70
|
+
function listDeliverableNotificationDevices(now = Date.now()) {
|
|
71
|
+
return getSqliteDatabase().prepare(`SELECT * FROM notification_devices
|
|
72
|
+
WHERE enabled = 1 AND permissions = 'granted' AND lease_expires_at > ?
|
|
73
|
+
ORDER BY updated_at DESC`).all(now).map(deviceFromRow);
|
|
74
|
+
}
|
|
75
|
+
function updateNotificationDevicePreferences(deviceId, patch) {
|
|
76
|
+
const current = getNotificationDevice(deviceId);
|
|
77
|
+
if (!current) return null;
|
|
78
|
+
const preferences = normalizePreferences({
|
|
79
|
+
...current.preferences,
|
|
80
|
+
...patch
|
|
81
|
+
});
|
|
82
|
+
runSqliteWriteTransaction((db) => {
|
|
83
|
+
db.prepare("UPDATE notification_devices SET preferences_json = ?, updated_at = ? WHERE device_id = ?").run(JSON.stringify(preferences), Date.now(), deviceId);
|
|
84
|
+
});
|
|
85
|
+
return getNotificationDevice(deviceId);
|
|
86
|
+
}
|
|
87
|
+
function removeNotificationDevice(deviceId) {
|
|
88
|
+
return runSqliteWriteTransaction((db) => db.prepare("DELETE FROM notification_devices WHERE device_id = ?").run(deviceId).changes > 0);
|
|
89
|
+
}
|
|
90
|
+
function disableNotificationDeviceForPushToken(pushToken) {
|
|
91
|
+
runSqliteWriteTransaction((db) => {
|
|
92
|
+
db.prepare("UPDATE notification_devices SET enabled = 0, updated_at = ? WHERE push_token = ?").run(Date.now(), pushToken);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
export { disableNotificationDeviceForPushToken, getNotificationDevice, listDeliverableNotificationDevices, registerNotificationDevice, removeNotificationDevice, updateNotificationDevicePreferences };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ProductNotification } from '@xopcai/gateway-contract';
|
|
2
|
+
export type NotificationPlan = {
|
|
3
|
+
dedupeKey: string;
|
|
4
|
+
notification: Omit<ProductNotification, 'schemaVersion' | 'id' | 'createdAt'>;
|
|
5
|
+
};
|
|
6
|
+
export declare function notificationPlanFromGatewayEvent(type: string, payload: unknown): NotificationPlan | null;
|