@xopcai/xopc 0.0.224 → 0.0.227
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/demo-memory/src/index.js +10 -1
- package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store--GBciH10.js → activity-store-BevwE16n.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-K7V4XXt7.js → agent-avatar-dicebear-BggVQrEZ.js} +1 -1
- package/dist/gateway/static/root/assets/agents-TJpjRHnr.js +68 -0
- package/dist/gateway/static/root/assets/{api-HoIz_ija.js → api-Bx_BpIt8.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-xQCmkAHh.js → app-management-settings-panel-BtKP9n2r.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-BRoeA4kU.js → appearance-settings-BoH2NAUX.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-CZ4Da51l.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-CteCVsBP.js → archive-plugin-DnceUyxm.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-D7xNA2jB.js → attachment-load-Bh8_9gkr.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-BD1vK7r5.js → automations-page-ZKeAAVxD.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-2Y00PvJU.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-C3DaJa0W.js → binary-plugins-Cy9er6w-.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-lTkcHTve.js → block-editor-Cc0wWvNX.js} +3 -3
- package/dist/gateway/static/root/assets/browser-settings-page-CbJD27_j.js +8 -0
- package/dist/gateway/static/root/assets/{browser-workflow-inputs-1UZ0vnIy.js → browser-workflow-inputs-BS7rpLLl.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-workflows-page-BHuJOF4M.js → browser-workflows-page-BdBVUZXm.js} +2 -2
- package/dist/gateway/static/root/assets/capabilities-settings-panel-Dv8y6OuP.js +7 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-yNqTIuyd.js → capability-presets-api-D1Lqz0Tt.js} +1 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-TZmjzWzt.js +3 -0
- package/dist/gateway/static/root/assets/{channel-recipient-api-krezGsKc.js → channel-recipient-api-CGuEl-l2.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-B4AtvQHk.js → channels-settings-g-68dutA.js} +1 -1
- package/dist/gateway/static/root/assets/channels-status-swr-D7KsAeWh.js +1 -0
- package/dist/gateway/static/root/assets/{chat-terminal-dock-BRrL2iHA.js → chat-terminal-dock-CdX_7aQh.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-DXA0rAvQ.js +2 -0
- package/dist/gateway/static/root/assets/{cron-expression-Bl-2DzBQ.js → cron-expression-BnSQc4Ek.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-CxTl6Wlz.js → date-picker-BbuG7zQ_.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-DSl0mOPa.js → dependency-picker-snZeqOmG.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-DTeCmCdb.js → desktop-pet-DJQ8JoR1.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-BNebxg0V.js → desktop-pet-settings-B9txsRtv.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-Dn9XpmXY.js → directory-picker-path-field-D7WKWkNB.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-D-J-eOTb.js → extension-debug-page-D_7cOCj2.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CpST24nU.js → extension-page-Bwu47iUb.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-4nQXFnFq.js → extension-settings-page-Dwg4dSZM.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-BHWHduDi.js → gateway-config-api-jURUSte_.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-yUxMMVjK.js → gateway-config-swr-6SQXF1yR.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-BimMKmkM.js → gateway-settings-UklJOMLX.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-9mrsL84j.js → gateway-startup-retry-DqWaxyvZ.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-Djs_nduD.js +1 -0
- package/dist/gateway/static/root/assets/{home-page-DSM6aPHF.js → home-page-i6bXCHdo.js} +1 -1
- package/dist/gateway/static/root/assets/{image-generation-api-CgFAGDi8.js → image-generation-api-Bxmt6LEr.js} +1 -1
- package/dist/gateway/static/root/assets/index-BBBE89vm.css +2 -0
- package/dist/gateway/static/root/assets/index-BsD5wygl.js +74 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-6Ar5rfX4.js → keyboard-shortcuts-settings-ByVsCH3d.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-tzM0wTL8.js → local-app-workbench-page-BCKEDMlP.js} +2 -2
- package/dist/gateway/static/root/assets/{local-apps-page-B5n-x544.js → local-apps-page-D19R-xsz.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-CPylQjH9.js → locale-store-Ci1Qi0Q-.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-CgoQlera.js → logs-page-Cy9YQK7j.js} +2 -2
- package/dist/gateway/static/root/assets/{management-settings-CXBLyU-0.js → management-settings-G0y7zmfY.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-split-CtmQemJi.js → markdown-split-B69d2HWt.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-view-CUsob4Pz.js → markdown-view-CBM-v2ph.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-Dsd-IEOW.js → media-plugins-6TWEQgN_.js} +1 -1
- package/dist/gateway/static/root/assets/messages-CtOJwGW3.js +3 -0
- package/dist/gateway/static/root/assets/{notes-api-CfKZYrBC.js → notes-api-DLsnnZCS.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-BKbOTE2n.js → notes-page-CkRJJKDm.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-B10T3hGX.js +4 -0
- package/dist/gateway/static/root/assets/{product-open-page-DqB9bsDB.js → product-open-page-HxArIvoN.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-DsqRlgsy.js +1 -0
- package/dist/gateway/static/root/assets/{projects-page-PeXUdGdO.js → projects-page-Bu2PN8OK.js} +1 -1
- package/dist/gateway/static/root/assets/{registry-api-B_5D8epF.js → registry-api-lEar5FOV.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CXACdJ2f.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DBTH_t0O.js → runtime-tools-settings-panel-Bf03UNGD.js} +2 -2
- package/dist/gateway/static/root/assets/{schema-form-CqMKAwV0.js → schema-form-CIXMBgx5.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-CS2knWDT.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-ByFGk0q_.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-dPriuuYq.js → settings-form-section-DgdB26mE.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-B-Mbm8Va.js → settings-loading-skeleton-efk5W6lG.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-Dyj3w-Ml.js → settings-page-BHVf4gEP.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-C4FxY5J3.js → setup-status-panel-WK0dGNMv.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-emEdx7YL.js → share-preview-page-Cod-w2qm.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-CUqQ31gt.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-DweAPjI8.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-BNtYnrRT.js → skills-page-DwCcipqK.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page.utils-DjnbNFs5.js → skills-page.utils-3WSbtl3S.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-DDStf-jZ.js → system-settings-panel-C5VxqC_s.js} +1 -1
- package/dist/gateway/static/root/assets/task-detail-page-DEj-BFMP.js +1 -0
- package/dist/gateway/static/root/assets/{theme-store-BN-32Fz5.js → theme-store-Dd89g_ki.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-C2mKZ-ua.js → use-autosave-CeV8VJh2.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-f7yh7aD5.js +1 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-D7OHeQby.js → voice-api-key-field-EmJ-8aZu.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-CIiXie7F.js → work-discovery-overlay-zsk6E3Mi.js} +1 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-CBAYvm3G.js +3 -0
- package/dist/gateway/static/root/assets/workflows-page-BN0JmHrG.js +5 -0
- package/dist/gateway/static/root/index.html +11 -11
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +3 -1
- package/dist/src/agent/agent-manager.js +22 -5
- package/dist/src/agent/embedded/run-turn.js +2 -0
- package/dist/src/agent/mcp/bundle-mcp-config.d.ts +9 -1
- package/dist/src/agent/mcp/bundle-mcp-config.js +11 -15
- package/dist/src/agent/mcp/embedded-mcp.d.ts +1 -1
- package/dist/src/agent/mcp/index.d.ts +1 -0
- package/dist/src/agent/mcp/index.js +7 -1
- package/dist/src/agent/mcp/mcp-http.d.ts +4 -0
- package/dist/src/agent/mcp/mcp-http.js +25 -1
- package/dist/src/agent/mcp/mcp-transport-config.d.ts +4 -0
- package/dist/src/agent/mcp/mcp-transport-config.js +1 -0
- package/dist/src/agent/mcp/mcp-transport.d.ts +7 -1
- package/dist/src/agent/mcp/mcp-transport.js +38 -11
- package/dist/src/agent/mcp/oauth/index.d.ts +6 -0
- package/dist/src/agent/mcp/oauth/index.js +6 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-errors.d.ts +5 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-errors.js +14 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-manager.d.ts +25 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-manager.js +219 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-provider.d.ts +33 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-provider.js +103 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-session.d.ts +29 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-session.js +169 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-store.d.ts +9 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-store.js +79 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-types.d.ts +22 -0
- package/dist/src/agent/mcp/oauth/mcp-oauth-types.js +1 -0
- package/dist/src/agent/memory/builtin-provider.js +23 -4
- package/dist/src/agent/memory/compaction-promotion.d.ts +18 -0
- package/dist/src/agent/memory/compaction-promotion.js +211 -0
- package/dist/src/agent/memory/context/types.d.ts +5 -0
- package/dist/src/agent/memory/manager.d.ts +3 -0
- package/dist/src/agent/memory/manager.js +12 -2
- package/dist/src/agent/memory/trusted-recall.d.ts +7 -0
- package/dist/src/agent/memory/trusted-recall.js +53 -0
- package/dist/src/agent/memory/turn-provenance.d.ts +14 -0
- package/dist/src/agent/memory/turn-provenance.js +60 -0
- package/dist/src/agent/memory/types.d.ts +18 -3
- 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/memory/user-context-coordinator.js +57 -6
- 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/marketplace/adapters/store/store-api-client.d.ts +2 -0
- 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/memory-tool.js +5 -1
- package/dist/src/capabilities/store-connector.js +99 -10
- package/dist/src/cli/commands/channels.js +1 -1
- package/dist/src/cli/commands/connectors.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/config/config-mutation.d.ts +14 -0
- package/dist/src/config/config-mutation.js +33 -0
- package/dist/src/config/index.js +3 -3
- package/dist/src/config/paths.d.ts +2 -0
- package/dist/src/config/paths.js +5 -1
- package/dist/src/config/schema.d.ts +17 -0
- package/dist/src/config/schema.js +26 -2
- package/dist/src/connectors/auth-provider-registry.d.ts +1 -1
- package/dist/src/connectors/auth-provider-registry.js +23 -2
- package/dist/src/connectors/connected-source-understanding.js +1 -1
- package/dist/src/connectors/install.js +3 -3
- package/dist/src/connectors/instances.d.ts +2 -1
- package/dist/src/connectors/instances.js +15 -5
- package/dist/src/connectors/materialize.d.ts +1 -0
- package/dist/src/connectors/materialize.js +9 -1
- package/dist/src/connectors/runtime-adapter-registry.js +1 -0
- package/dist/src/connectors/types.d.ts +4 -1
- package/dist/src/extensions/connector-dependencies.d.ts +10 -0
- package/dist/src/extensions/connector-dependencies.js +65 -0
- package/dist/src/extensions/install.js +27 -0
- package/dist/src/extensions/normalize-manifest.js +1 -0
- package/dist/src/extensions/sdk/index.js +1 -1
- package/dist/src/extensions/types/manifest.d.ts +2 -0
- package/dist/src/gateway/hono/routes/capabilities.js +8 -8
- package/dist/src/gateway/hono/routes/connectors.js +104 -70
- package/dist/src/gateway/hono/routes/mcp.js +77 -5
- package/dist/src/gateway/hono/routes/project-skills.js +30 -2
- package/dist/src/gateway/hono/routes/you.js +52 -0
- package/dist/src/gateway/service.js +8 -8
- package/dist/src/knowledge/connected-knowledge-pipeline.js +26 -4
- 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/projects/project-skill-service.d.ts +28 -6
- package/dist/src/projects/project-skill-service.js +76 -29
- package/dist/src/session/store.js +27 -0
- package/dist/src/storage/sqlite/index.d.ts +1 -0
- package/dist/src/storage/sqlite/index.js +2 -1
- package/dist/src/storage/sqlite/memory-records-repository.d.ts +9 -1
- package/dist/src/storage/sqlite/memory-records-repository.js +40 -6
- package/dist/src/storage/sqlite/migrations/134_memory_provenance.sql +18 -0
- package/dist/src/storage/sqlite/migrations/135_user_people.sql +59 -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-people-repository.d.ts +32 -0
- package/dist/src/storage/sqlite/user-people-repository.js +246 -0
- package/dist/src/tui/backends/embedded-backend.js +1 -1
- package/dist/src/user-context/planner.js +8 -3
- 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-BsxTpXyq.js +0 -68
- package/dist/gateway/static/root/assets/apps-page-BXgpBA8w.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace-wrgf-z7D.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-zhzNkeVG.js +0 -8
- package/dist/gateway/static/root/assets/capabilities-settings-panel-DuCmg2Ej.js +0 -7
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-DjiP3mQQ.js +0 -3
- package/dist/gateway/static/root/assets/channels-status-swr-DMxB7slL.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-fmda2NEP.js +0 -2
- package/dist/gateway/static/root/assets/heartbeat-settings-MUJ3mEC9.js +0 -1
- package/dist/gateway/static/root/assets/index-B3zYJBhK.js +0 -74
- package/dist/gateway/static/root/assets/index-D3_xSYKd.css +0 -2
- package/dist/gateway/static/root/assets/messages-B82nsi9k.js +0 -3
- package/dist/gateway/static/root/assets/notes-workbench-BScELo01.js +0 -4
- package/dist/gateway/static/root/assets/project-detail-page-DUoyty8X.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-sHho-uXP.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-DyvNgaP9.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-CnYf1R7z.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-BQBVq-sN.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-BFoEpQ4I.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-ZVwr8KbS.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-CHjIinCW.js +0 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-9_dAGW3_.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-DXpYJxao.js +0 -5
- package/dist/src/extensions/bundle-mcp.d.ts +0 -18
- package/dist/src/extensions/bundle-mcp.js +0 -69
- package/dist/src/knowledge/people-graph.d.ts +0 -25
- package/dist/src/knowledge/people-graph.js +0 -92
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { getSqliteDatabase, runSqliteWriteTransaction } from "./transaction.js";
|
|
2
|
+
import { USER_CONTEXT_PRINCIPAL_ID } from "../../user-context/domain.js";
|
|
3
|
+
//#region src/storage/sqlite/user-people-repository.ts
|
|
4
|
+
function sourceMetadata(sourceInstanceId) {
|
|
5
|
+
if (!sourceInstanceId.startsWith("composio:")) return {};
|
|
6
|
+
const connectorId = sourceInstanceId.split(":")[1];
|
|
7
|
+
if (!connectorId) return {};
|
|
8
|
+
return {
|
|
9
|
+
connectorId,
|
|
10
|
+
toolkit: connectorId.replace(/^composio-/, "")
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function handlesFor(personId) {
|
|
14
|
+
return getSqliteDatabase().prepare(`SELECT handle_id, handle_type, display_value,
|
|
15
|
+
source_instance_id, verification, first_observed_at, last_observed_at
|
|
16
|
+
FROM user_person_handles WHERE person_id = ?
|
|
17
|
+
ORDER BY CASE handle_type WHEN 'email' THEN 0 WHEN 'provider_user' THEN 1 WHEN 'username' THEN 2 ELSE 3 END,
|
|
18
|
+
last_observed_at DESC`).all(personId).map((row) => ({
|
|
19
|
+
id: row.handle_id,
|
|
20
|
+
type: row.handle_type,
|
|
21
|
+
value: row.display_value,
|
|
22
|
+
sourceInstanceId: row.source_instance_id,
|
|
23
|
+
verification: row.verification,
|
|
24
|
+
firstObservedAt: row.first_observed_at,
|
|
25
|
+
lastObservedAt: row.last_observed_at
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
function sourcesFor(personId) {
|
|
29
|
+
return getSqliteDatabase().prepare(`SELECT source_instance_id, interaction_count,
|
|
30
|
+
first_observed_at, last_observed_at FROM user_person_source_stats
|
|
31
|
+
WHERE person_id = ? ORDER BY last_observed_at DESC`).all(personId).map((row) => ({
|
|
32
|
+
sourceInstanceId: row.source_instance_id,
|
|
33
|
+
...sourceMetadata(row.source_instance_id),
|
|
34
|
+
interactionCount: row.interaction_count,
|
|
35
|
+
firstObservedAt: row.first_observed_at,
|
|
36
|
+
lastObservedAt: row.last_observed_at
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
function fromRow(row, includeDetails = false) {
|
|
40
|
+
const handles = handlesFor(row.person_id);
|
|
41
|
+
const sources = sourcesFor(row.person_id);
|
|
42
|
+
const primaryHandle = handles.find((handle) => handle.type === "email")?.value ?? handles.find((handle) => handle.type === "provider_user" || handle.type === "username")?.value;
|
|
43
|
+
return {
|
|
44
|
+
id: row.person_id,
|
|
45
|
+
displayName: row.user_display_name ?? row.display_name,
|
|
46
|
+
kind: row.user_kind ?? row.inferred_kind,
|
|
47
|
+
hidden: row.hidden === 1,
|
|
48
|
+
confidence: row.confidence,
|
|
49
|
+
...primaryHandle ? { primaryHandle } : {},
|
|
50
|
+
interactionCount: sources.reduce((total, source) => total + source.interactionCount, 0),
|
|
51
|
+
firstObservedAt: row.first_observed_at,
|
|
52
|
+
lastObservedAt: row.last_observed_at,
|
|
53
|
+
handles: includeDetails ? handles : [],
|
|
54
|
+
sources
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function replaceUserPeopleIndex(entries, revision, principalId = USER_CONTEXT_PRINCIPAL_ID, nowMs = Date.now()) {
|
|
58
|
+
runSqliteWriteTransaction((db) => {
|
|
59
|
+
const mergedRows = db.prepare(`SELECT person_id, merged_into_person_id FROM user_people
|
|
60
|
+
WHERE principal_id = ? AND merged_into_person_id IS NOT NULL`).all(principalId);
|
|
61
|
+
const mergedTargets = new Map(mergedRows.map((row) => [row.person_id, row.merged_into_person_id]));
|
|
62
|
+
const targetFor = (personId) => {
|
|
63
|
+
const seen = /* @__PURE__ */ new Set();
|
|
64
|
+
let current = personId;
|
|
65
|
+
while (mergedTargets.has(current) && !seen.has(current)) {
|
|
66
|
+
seen.add(current);
|
|
67
|
+
current = mergedTargets.get(current);
|
|
68
|
+
}
|
|
69
|
+
return current;
|
|
70
|
+
};
|
|
71
|
+
db.prepare(`DELETE FROM user_person_source_stats
|
|
72
|
+
WHERE person_id IN (SELECT person_id FROM user_people WHERE principal_id = ?)`).run(principalId);
|
|
73
|
+
db.prepare(`DELETE FROM user_person_handles
|
|
74
|
+
WHERE person_id IN (SELECT person_id FROM user_people WHERE principal_id = ?)
|
|
75
|
+
AND verification != 'user_confirmed'`).run(principalId);
|
|
76
|
+
for (const entry of entries) {
|
|
77
|
+
const personId = targetFor(entry.id);
|
|
78
|
+
if (personId === entry.id) db.prepare(`INSERT INTO user_people (
|
|
79
|
+
person_id, principal_id, display_name, inferred_kind, confidence,
|
|
80
|
+
first_observed_at, last_observed_at, created_at, updated_at
|
|
81
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
82
|
+
ON CONFLICT(person_id) DO UPDATE SET
|
|
83
|
+
display_name = excluded.display_name,
|
|
84
|
+
inferred_kind = excluded.inferred_kind,
|
|
85
|
+
confidence = excluded.confidence,
|
|
86
|
+
first_observed_at = MIN(user_people.first_observed_at, excluded.first_observed_at),
|
|
87
|
+
last_observed_at = excluded.last_observed_at,
|
|
88
|
+
updated_at = excluded.updated_at`).run(entry.id, principalId, entry.displayName, entry.inferredKind, entry.confidence, entry.firstObservedAt, entry.lastObservedAt, nowMs, nowMs);
|
|
89
|
+
for (const handle of entry.handles) db.prepare(`INSERT INTO user_person_handles (
|
|
90
|
+
handle_id, person_id, handle_type, normalized_value, display_value,
|
|
91
|
+
source_instance_id, verification, first_observed_at, last_observed_at, created_at, updated_at
|
|
92
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
93
|
+
ON CONFLICT(handle_type, normalized_value, source_instance_id) DO UPDATE SET
|
|
94
|
+
person_id = excluded.person_id,
|
|
95
|
+
display_value = excluded.display_value,
|
|
96
|
+
verification = CASE WHEN user_person_handles.verification = 'user_confirmed'
|
|
97
|
+
THEN 'user_confirmed' ELSE excluded.verification END,
|
|
98
|
+
first_observed_at = MIN(user_person_handles.first_observed_at, excluded.first_observed_at),
|
|
99
|
+
last_observed_at = excluded.last_observed_at,
|
|
100
|
+
updated_at = excluded.updated_at`).run(handle.id, personId, handle.type, handle.value.toLocaleLowerCase(), handle.value, handle.sourceInstanceId, handle.verification, handle.firstObservedAt, handle.lastObservedAt, nowMs, nowMs);
|
|
101
|
+
for (const source of entry.sources) db.prepare(`INSERT INTO user_person_source_stats (
|
|
102
|
+
person_id, source_instance_id, interaction_count, first_observed_at,
|
|
103
|
+
last_observed_at, updated_at
|
|
104
|
+
) VALUES (?, ?, ?, ?, ?, ?)
|
|
105
|
+
ON CONFLICT(person_id, source_instance_id) DO UPDATE SET
|
|
106
|
+
interaction_count = user_person_source_stats.interaction_count + excluded.interaction_count,
|
|
107
|
+
first_observed_at = MIN(user_person_source_stats.first_observed_at, excluded.first_observed_at),
|
|
108
|
+
last_observed_at = MAX(user_person_source_stats.last_observed_at, excluded.last_observed_at),
|
|
109
|
+
updated_at = excluded.updated_at`).run(personId, source.sourceInstanceId, source.interactionCount, source.firstObservedAt, source.lastObservedAt, nowMs);
|
|
110
|
+
}
|
|
111
|
+
db.prepare(`DELETE FROM user_people
|
|
112
|
+
WHERE principal_id = ? AND merged_into_person_id IS NULL AND hidden = 0
|
|
113
|
+
AND user_display_name IS NULL AND user_kind IS NULL
|
|
114
|
+
AND NOT EXISTS (SELECT 1 FROM user_person_source_stats s WHERE s.person_id = user_people.person_id)
|
|
115
|
+
AND NOT EXISTS (SELECT 1 FROM user_person_handles h WHERE h.person_id = user_people.person_id)`).run(principalId);
|
|
116
|
+
db.prepare(`INSERT INTO user_people_index_state (
|
|
117
|
+
principal_id, source_change_sequence, source_grants_updated_at, rebuilt_at
|
|
118
|
+
) VALUES (?, ?, ?, ?)
|
|
119
|
+
ON CONFLICT(principal_id) DO UPDATE SET
|
|
120
|
+
source_change_sequence = excluded.source_change_sequence,
|
|
121
|
+
source_grants_updated_at = excluded.source_grants_updated_at,
|
|
122
|
+
rebuilt_at = excluded.rebuilt_at`).run(principalId, revision.sourceChangeSequence, revision.sourceGrantsUpdatedAt, nowMs);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function getUserPeopleIndexState(principalId = USER_CONTEXT_PRINCIPAL_ID) {
|
|
126
|
+
const row = getSqliteDatabase().prepare(`SELECT source_change_sequence, source_grants_updated_at, rebuilt_at
|
|
127
|
+
FROM user_people_index_state WHERE principal_id = ?`).get(principalId);
|
|
128
|
+
return row ? {
|
|
129
|
+
sourceChangeSequence: row.source_change_sequence,
|
|
130
|
+
sourceGrantsUpdatedAt: row.source_grants_updated_at,
|
|
131
|
+
rebuiltAt: row.rebuilt_at
|
|
132
|
+
} : null;
|
|
133
|
+
}
|
|
134
|
+
function listUserPeople(options = {}) {
|
|
135
|
+
const principalId = options.principalId ?? "local-owner";
|
|
136
|
+
const clauses = [
|
|
137
|
+
"p.principal_id = ?",
|
|
138
|
+
"p.merged_into_person_id IS NULL",
|
|
139
|
+
"EXISTS (SELECT 1 FROM user_person_source_stats active_source WHERE active_source.person_id = p.person_id)"
|
|
140
|
+
];
|
|
141
|
+
const params = [principalId];
|
|
142
|
+
if (options.hiddenOnly) clauses.push("p.hidden = 1");
|
|
143
|
+
else if (!options.includeHidden) clauses.push("p.hidden = 0");
|
|
144
|
+
if (options.kind) {
|
|
145
|
+
clauses.push("COALESCE(p.user_kind, p.inferred_kind) = ?");
|
|
146
|
+
params.push(options.kind);
|
|
147
|
+
}
|
|
148
|
+
if (options.sourceInstanceId) {
|
|
149
|
+
clauses.push("EXISTS (SELECT 1 FROM user_person_source_stats filtered_source WHERE filtered_source.person_id = p.person_id AND filtered_source.source_instance_id = ?)");
|
|
150
|
+
params.push(options.sourceInstanceId);
|
|
151
|
+
}
|
|
152
|
+
const query = options.query?.trim().toLocaleLowerCase();
|
|
153
|
+
if (query) {
|
|
154
|
+
clauses.push(`(LOWER(COALESCE(p.user_display_name, p.display_name)) LIKE ? OR EXISTS (
|
|
155
|
+
SELECT 1 FROM user_person_handles search_handle
|
|
156
|
+
WHERE search_handle.person_id = p.person_id AND search_handle.normalized_value LIKE ?
|
|
157
|
+
))`);
|
|
158
|
+
params.push(`%${query}%`, `%${query}%`);
|
|
159
|
+
}
|
|
160
|
+
const where = clauses.join(" AND ");
|
|
161
|
+
const total = Number(getSqliteDatabase().prepare(`SELECT COUNT(*) AS count FROM user_people p WHERE ${where}`).get(...params).count);
|
|
162
|
+
const limit = Math.max(1, Math.min(100, Math.floor(options.limit ?? 30)));
|
|
163
|
+
const offset = Math.max(0, Math.floor(options.offset ?? 0));
|
|
164
|
+
const rows = getSqliteDatabase().prepare(`SELECT p.* FROM user_people p WHERE ${where}
|
|
165
|
+
ORDER BY p.last_observed_at DESC, COALESCE(p.user_display_name, p.display_name) ASC
|
|
166
|
+
LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
167
|
+
return {
|
|
168
|
+
items: rows.map((row) => fromRow(row)),
|
|
169
|
+
total,
|
|
170
|
+
...offset + rows.length < total ? { nextOffset: offset + rows.length } : {}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function getUserPerson(personId, principalId = USER_CONTEXT_PRINCIPAL_ID) {
|
|
174
|
+
const row = getSqliteDatabase().prepare(`SELECT * FROM user_people
|
|
175
|
+
WHERE person_id = ? AND principal_id = ? AND merged_into_person_id IS NULL`).get(personId, principalId);
|
|
176
|
+
return row ? fromRow(row, true) : null;
|
|
177
|
+
}
|
|
178
|
+
function updateUserPerson(personId, patch, principalId = USER_CONTEXT_PRINCIPAL_ID) {
|
|
179
|
+
const current = getUserPerson(personId, principalId);
|
|
180
|
+
if (!current) return null;
|
|
181
|
+
runSqliteWriteTransaction((db) => db.prepare(`UPDATE user_people SET
|
|
182
|
+
user_display_name = ?, user_kind = ?, hidden = ?, updated_at = ?
|
|
183
|
+
WHERE person_id = ? AND principal_id = ?`).run(patch.displayName === void 0 ? getUserDisplayName(personId) : patch.displayName, patch.kind === void 0 ? getUserKind(personId) : patch.kind, patch.hidden === void 0 ? Number(current.hidden) : Number(patch.hidden), Date.now(), personId, principalId));
|
|
184
|
+
return getUserPerson(personId, principalId);
|
|
185
|
+
}
|
|
186
|
+
function getUserDisplayName(personId) {
|
|
187
|
+
return getSqliteDatabase().prepare("SELECT user_display_name AS value FROM user_people WHERE person_id = ?").get(personId)?.value ?? null;
|
|
188
|
+
}
|
|
189
|
+
function getUserKind(personId) {
|
|
190
|
+
return getSqliteDatabase().prepare("SELECT user_kind AS value FROM user_people WHERE person_id = ?").get(personId)?.value ?? null;
|
|
191
|
+
}
|
|
192
|
+
function summarizeUserPeople(principalId = USER_CONTEXT_PRINCIPAL_ID) {
|
|
193
|
+
const row = getSqliteDatabase().prepare(`SELECT
|
|
194
|
+
SUM(CASE WHEN hidden = 0 AND COALESCE(user_kind, inferred_kind) = 'person' THEN 1 ELSE 0 END) AS people,
|
|
195
|
+
SUM(CASE WHEN hidden = 0 AND COALESCE(user_kind, inferred_kind) IN ('bot', 'service', 'group') THEN 1 ELSE 0 END) AS automated,
|
|
196
|
+
SUM(CASE WHEN hidden = 0 AND COALESCE(user_kind, inferred_kind) = 'unknown' THEN 1 ELSE 0 END) AS needs_review,
|
|
197
|
+
SUM(CASE WHEN hidden = 1 THEN 1 ELSE 0 END) AS hidden,
|
|
198
|
+
MAX(last_observed_at) AS last_updated_at
|
|
199
|
+
FROM user_people p WHERE principal_id = ? AND merged_into_person_id IS NULL
|
|
200
|
+
AND EXISTS (SELECT 1 FROM user_person_source_stats s WHERE s.person_id = p.person_id)`).get(principalId);
|
|
201
|
+
const sources = Number(getSqliteDatabase().prepare(`SELECT COUNT(DISTINCT source_instance_id) AS count
|
|
202
|
+
FROM user_person_source_stats s JOIN user_people p ON p.person_id = s.person_id
|
|
203
|
+
WHERE p.principal_id = ? AND p.merged_into_person_id IS NULL`).get(principalId).count);
|
|
204
|
+
return {
|
|
205
|
+
people: Number(row.people ?? 0),
|
|
206
|
+
automatedAccounts: Number(row.automated ?? 0),
|
|
207
|
+
needsReview: Number(row.needs_review ?? 0),
|
|
208
|
+
hidden: Number(row.hidden ?? 0),
|
|
209
|
+
sources,
|
|
210
|
+
...row.last_updated_at == null ? {} : { lastUpdatedAt: row.last_updated_at }
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function mergeUserPeople(sourcePersonId, targetPersonId, principalId = USER_CONTEXT_PRINCIPAL_ID) {
|
|
214
|
+
if (sourcePersonId === targetPersonId) return getUserPerson(targetPersonId, principalId);
|
|
215
|
+
const source = getUserPerson(sourcePersonId, principalId);
|
|
216
|
+
const target = getUserPerson(targetPersonId, principalId);
|
|
217
|
+
if (!source || !target) return null;
|
|
218
|
+
runSqliteWriteTransaction((db) => {
|
|
219
|
+
const duplicateHandles = db.prepare(`SELECT source.handle_id FROM user_person_handles source
|
|
220
|
+
JOIN user_person_handles target
|
|
221
|
+
ON target.person_id = ? AND target.handle_type = source.handle_type
|
|
222
|
+
AND target.normalized_value = source.normalized_value
|
|
223
|
+
AND target.source_instance_id = source.source_instance_id
|
|
224
|
+
WHERE source.person_id = ?`).all(targetPersonId, sourcePersonId);
|
|
225
|
+
for (const duplicate of duplicateHandles) db.prepare("DELETE FROM user_person_handles WHERE handle_id = ?").run(duplicate.handle_id);
|
|
226
|
+
db.prepare("UPDATE user_person_handles SET person_id = ?, updated_at = ? WHERE person_id = ?").run(targetPersonId, Date.now(), sourcePersonId);
|
|
227
|
+
const sourceStats = db.prepare("SELECT * FROM user_person_source_stats WHERE person_id = ?").all(sourcePersonId);
|
|
228
|
+
for (const stat of sourceStats) db.prepare(`INSERT INTO user_person_source_stats (
|
|
229
|
+
person_id, source_instance_id, interaction_count, first_observed_at,
|
|
230
|
+
last_observed_at, last_source_item_id, updated_at
|
|
231
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
232
|
+
ON CONFLICT(person_id, source_instance_id) DO UPDATE SET
|
|
233
|
+
interaction_count = user_person_source_stats.interaction_count + excluded.interaction_count,
|
|
234
|
+
first_observed_at = MIN(user_person_source_stats.first_observed_at, excluded.first_observed_at),
|
|
235
|
+
last_observed_at = MAX(user_person_source_stats.last_observed_at, excluded.last_observed_at),
|
|
236
|
+
last_source_item_id = CASE WHEN excluded.last_observed_at >= user_person_source_stats.last_observed_at
|
|
237
|
+
THEN excluded.last_source_item_id ELSE user_person_source_stats.last_source_item_id END,
|
|
238
|
+
updated_at = excluded.updated_at`).run(targetPersonId, stat.source_instance_id, stat.interaction_count, stat.first_observed_at, stat.last_observed_at, stat.last_source_item_id, Date.now());
|
|
239
|
+
db.prepare("DELETE FROM user_person_source_stats WHERE person_id = ?").run(sourcePersonId);
|
|
240
|
+
db.prepare(`UPDATE user_people SET merged_into_person_id = ?, updated_at = ?
|
|
241
|
+
WHERE person_id = ? AND principal_id = ?`).run(targetPersonId, Date.now(), sourcePersonId, principalId);
|
|
242
|
+
});
|
|
243
|
+
return getUserPerson(targetPersonId, principalId);
|
|
244
|
+
}
|
|
245
|
+
//#endregion
|
|
246
|
+
export { getUserPeopleIndexState, getUserPerson, listUserPeople, mergeUserPeople, replaceUserPeopleIndex, summarizeUserPeople, updateUserPerson };
|
|
@@ -23,9 +23,9 @@ import { parseModelRef } from "../../agent/models/selection.js";
|
|
|
23
23
|
import { buildReviewContext, resolveGitRoot } from "../../review/review-git.js";
|
|
24
24
|
import { MessageBus, MessageBusShutdownError } from "../../infra/bus/queue.js";
|
|
25
25
|
import "../../infra/bus/index.js";
|
|
26
|
+
import { prependEnvelopeTimestamp } from "../../channels/envelope-timestamp.js";
|
|
26
27
|
import { getWorkspacePath } from "../../config/workspace-path-helpers.js";
|
|
27
28
|
import "../../config/index.js";
|
|
28
|
-
import { prependEnvelopeTimestamp } from "../../channels/envelope-timestamp.js";
|
|
29
29
|
import { buildWorkflowChildTools } from "../../agent/workflow/workflow-child-tools.js";
|
|
30
30
|
import { WorkflowRunService } from "../../workflows/service/workflow-run-service.js";
|
|
31
31
|
import { WorkflowSessionBridge } from "../../workflows/service/workflow-session-bridge.js";
|
|
@@ -160,6 +160,8 @@ var UserContextPlanner = class {
|
|
|
160
160
|
rejected: [],
|
|
161
161
|
consentRequests: [],
|
|
162
162
|
estimatedTokens: 0,
|
|
163
|
+
contextChars: 0,
|
|
164
|
+
contextItemCount: 0,
|
|
163
165
|
allocation: params.allocation
|
|
164
166
|
};
|
|
165
167
|
const started = Date.now();
|
|
@@ -174,7 +176,8 @@ var UserContextPlanner = class {
|
|
|
174
176
|
const queryProfile = buildRetrievalQueryProfile(query, scopeInput);
|
|
175
177
|
const selfReview = queryProfile.selfReview;
|
|
176
178
|
const maxResults = requestedMaxResults ?? (selfReview ? SELF_REVIEW_MAX_RESULTS : DEFAULT_MAX_RESULTS);
|
|
177
|
-
const
|
|
179
|
+
const budgetChars = requestedMaxChars ?? (selfReview ? SELF_REVIEW_MAX_CONTEXT_CHARS : DEFAULT_MAX_CONTEXT_CHARS);
|
|
180
|
+
const maxChars = Math.max(0, budgetChars - 2);
|
|
178
181
|
const projects = new ProjectStore();
|
|
179
182
|
const excluded = new Set(params.excludedRecordIds ?? []);
|
|
180
183
|
const rejected = [];
|
|
@@ -464,7 +467,7 @@ var UserContextPlanner = class {
|
|
|
464
467
|
turnId: params.turnId,
|
|
465
468
|
sessionKey: params.sessionKey,
|
|
466
469
|
query,
|
|
467
|
-
budget:
|
|
470
|
+
budget: budgetChars,
|
|
468
471
|
durationMs: Date.now() - started,
|
|
469
472
|
items: traceItems
|
|
470
473
|
});
|
|
@@ -474,7 +477,9 @@ var UserContextPlanner = class {
|
|
|
474
477
|
items,
|
|
475
478
|
rejected,
|
|
476
479
|
consentRequests,
|
|
477
|
-
estimatedTokens: Math.ceil(block.length / 4),
|
|
480
|
+
estimatedTokens: Math.ceil((block.length + (block ? 2 : 0)) / 4),
|
|
481
|
+
contextChars: block.length + (block ? 2 : 0),
|
|
482
|
+
contextItemCount: traceItems.filter((item) => item.decision === "selected").length,
|
|
478
483
|
allocation: params.allocation
|
|
479
484
|
};
|
|
480
485
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { KnowledgeSourceItem } from '../../knowledge/types.js';
|
|
2
|
+
import type { UserPersonIndexEntry } from './types.js';
|
|
3
|
+
type PersonSignal = {
|
|
4
|
+
name?: string;
|
|
5
|
+
email?: string;
|
|
6
|
+
username?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function personIdentityKey(signal: PersonSignal, sourceInstanceId: string): string | undefined;
|
|
9
|
+
export declare function personIdForIdentity(signal: PersonSignal, sourceInstanceId: string): string | undefined;
|
|
10
|
+
export declare function buildUserPeopleIndex(items: KnowledgeSourceItem[]): UserPersonIndexEntry[];
|
|
11
|
+
export declare function rebuildUserPeopleIndex(): UserPersonIndexEntry[];
|
|
12
|
+
export declare function ensureUserPeopleIndex(): void;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { getSqliteDatabase } from "../../storage/sqlite/transaction.js";
|
|
2
|
+
import { getUserPeopleIndexState, replaceUserPeopleIndex } from "../../storage/sqlite/user-people-repository.js";
|
|
3
|
+
import { listKnowledgeSourceItems } from "../../storage/sqlite/knowledge-repository.js";
|
|
4
|
+
import "../../storage/sqlite/index.js";
|
|
5
|
+
import { listUnderstandingSourceGrants } from "../sources/repository.js";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
//#region src/user-context/relationships/indexer.ts
|
|
8
|
+
function hash(value) {
|
|
9
|
+
return createHash("sha256").update(value).digest("hex").slice(0, 20);
|
|
10
|
+
}
|
|
11
|
+
function normalized(value) {
|
|
12
|
+
return value?.trim().toLocaleLowerCase() || void 0;
|
|
13
|
+
}
|
|
14
|
+
function readSignal(value) {
|
|
15
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
16
|
+
const row = value;
|
|
17
|
+
const read = (key) => typeof row[key] === "string" && row[key].trim() ? row[key].trim().slice(0, 160) : void 0;
|
|
18
|
+
const signal = {
|
|
19
|
+
name: read("name"),
|
|
20
|
+
email: read("email"),
|
|
21
|
+
username: read("username")
|
|
22
|
+
};
|
|
23
|
+
return signal.name || signal.email || signal.username ? signal : void 0;
|
|
24
|
+
}
|
|
25
|
+
function providerScope(sourceInstanceId) {
|
|
26
|
+
if (!sourceInstanceId.startsWith("composio:")) return sourceInstanceId.toLocaleLowerCase();
|
|
27
|
+
return sourceInstanceId.split(":")[1]?.toLocaleLowerCase() ?? sourceInstanceId.toLocaleLowerCase();
|
|
28
|
+
}
|
|
29
|
+
function personIdentityKey(signal, sourceInstanceId) {
|
|
30
|
+
const email = normalized(signal.email);
|
|
31
|
+
if (email) return `email:${email}`;
|
|
32
|
+
const username = normalized(signal.username);
|
|
33
|
+
if (username) return `provider:${providerScope(sourceInstanceId)}:${username}`;
|
|
34
|
+
const name = normalized(signal.name);
|
|
35
|
+
return name ? `name:${sourceInstanceId.toLocaleLowerCase()}:${name}` : void 0;
|
|
36
|
+
}
|
|
37
|
+
function personIdForIdentity(signal, sourceInstanceId) {
|
|
38
|
+
const key = personIdentityKey(signal, sourceInstanceId);
|
|
39
|
+
return key ? `person_${hash(key)}` : void 0;
|
|
40
|
+
}
|
|
41
|
+
function inferredKind(signal) {
|
|
42
|
+
const name = normalized(signal.name) ?? "";
|
|
43
|
+
const username = normalized(signal.username) ?? "";
|
|
44
|
+
const emailLocal = (normalized(signal.email) ?? "").split("@")[0] ?? "";
|
|
45
|
+
if (/\[bot\]|(?:^|[-_.])bot(?:$|[-_.])|bot$/.test(`${name} ${username}`)) return "bot";
|
|
46
|
+
if (/^(?:no[-_.]?reply|do[-_.]?not[-_.]?reply|notifications?|alerts?|mailer[-_.]?daemon)(?:$|[-+_.])/.test(emailLocal)) return "service";
|
|
47
|
+
if (/^(?:team|group|all|everyone|support|help|sales|contact)(?:$|[-+_.])/.test(emailLocal)) return "group";
|
|
48
|
+
if (!signal.name && !signal.email && signal.username) return "unknown";
|
|
49
|
+
return "person";
|
|
50
|
+
}
|
|
51
|
+
function confidence(signal) {
|
|
52
|
+
if (signal.email) return signal.name ? .95 : .88;
|
|
53
|
+
if (signal.username) return signal.name ? .86 : .72;
|
|
54
|
+
return .55;
|
|
55
|
+
}
|
|
56
|
+
function observedAt(item) {
|
|
57
|
+
const value = Date.parse(item.occurredAt ?? item.sourceUpdatedAt ?? item.updatedAt);
|
|
58
|
+
return Number.isFinite(value) ? value : Date.now();
|
|
59
|
+
}
|
|
60
|
+
function handle(personId, type, value, sourceInstanceId, time) {
|
|
61
|
+
return {
|
|
62
|
+
id: `handle_${hash(`${type}:${normalized(value)}:${sourceInstanceId}`)}`,
|
|
63
|
+
type,
|
|
64
|
+
value,
|
|
65
|
+
sourceInstanceId,
|
|
66
|
+
verification: type === "display_name" ? "inferred" : "observed",
|
|
67
|
+
firstObservedAt: time,
|
|
68
|
+
lastObservedAt: time
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function activeConnectorSources() {
|
|
72
|
+
return new Set(listUnderstandingSourceGrants().flatMap((grant) => {
|
|
73
|
+
const sourceInstanceId = typeof grant.config.sourceInstanceId === "string" ? grant.config.sourceInstanceId.trim() : "";
|
|
74
|
+
if (sourceInstanceId) return [sourceInstanceId];
|
|
75
|
+
const connectorId = typeof grant.config.connectorId === "string" ? grant.config.connectorId.trim() : "";
|
|
76
|
+
const accountId = typeof grant.config.accountId === "string" ? grant.config.accountId.trim() : "";
|
|
77
|
+
return connectorId && accountId ? [`composio:${connectorId}:${accountId}`] : [];
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
function loadActiveSourceItems() {
|
|
81
|
+
const activeSources = activeConnectorSources();
|
|
82
|
+
const items = [];
|
|
83
|
+
let offset = 0;
|
|
84
|
+
while (true) {
|
|
85
|
+
const page = listKnowledgeSourceItems({
|
|
86
|
+
includeDeleted: false,
|
|
87
|
+
limit: 500,
|
|
88
|
+
offset
|
|
89
|
+
});
|
|
90
|
+
items.push(...page.filter((item) => item.synthesisPipeline === "connected_knowledge" && item.sensitivity !== "secret" && item.sensitivity !== "regulated" && item.metadata.observationKind !== "inventory" && (!item.sourceInstanceId.startsWith("composio:") || activeSources.has(item.sourceInstanceId))));
|
|
91
|
+
if (page.length < 500) break;
|
|
92
|
+
offset += page.length;
|
|
93
|
+
}
|
|
94
|
+
return items;
|
|
95
|
+
}
|
|
96
|
+
function currentIndexRevision() {
|
|
97
|
+
const db = getSqliteDatabase();
|
|
98
|
+
return {
|
|
99
|
+
sourceChangeSequence: Number(db.prepare("SELECT COALESCE(MAX(sequence), 0) AS value FROM knowledge_source_changes").get().value),
|
|
100
|
+
sourceGrantsUpdatedAt: Number(db.prepare("SELECT COALESCE(MAX(updated_at), 0) AS value FROM understanding_source_grants").get().value)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function buildUserPeopleIndex(items) {
|
|
104
|
+
const owners = new Set(items.flatMap((item) => Array.isArray(item.metadata.ownerIdentities) ? item.metadata.ownerIdentities.filter((value) => typeof value === "string").map((value) => value.trim().toLocaleLowerCase()).filter(Boolean) : []));
|
|
105
|
+
const people = /* @__PURE__ */ new Map();
|
|
106
|
+
for (const item of items) {
|
|
107
|
+
const values = Array.isArray(item.metadata.personEntities) ? item.metadata.personEntities : [];
|
|
108
|
+
const seenInItem = /* @__PURE__ */ new Set();
|
|
109
|
+
for (const value of values) {
|
|
110
|
+
const signal = readSignal(value);
|
|
111
|
+
if (!signal) continue;
|
|
112
|
+
const strongIdentity = normalized(signal.email) ?? normalized(signal.username);
|
|
113
|
+
if (strongIdentity && owners.has(strongIdentity)) continue;
|
|
114
|
+
const identityKey = personIdentityKey(signal, item.sourceInstanceId);
|
|
115
|
+
const id = personIdForIdentity(signal, item.sourceInstanceId);
|
|
116
|
+
if (!identityKey || !id || seenInItem.has(identityKey)) continue;
|
|
117
|
+
seenInItem.add(identityKey);
|
|
118
|
+
const time = observedAt(item);
|
|
119
|
+
const name = signal.name ?? signal.email ?? signal.username;
|
|
120
|
+
const current = people.get(identityKey) ?? {
|
|
121
|
+
id,
|
|
122
|
+
displayName: name,
|
|
123
|
+
inferredKind: inferredKind(signal),
|
|
124
|
+
confidence: confidence(signal),
|
|
125
|
+
firstObservedAt: time,
|
|
126
|
+
lastObservedAt: time,
|
|
127
|
+
handles: /* @__PURE__ */ new Map(),
|
|
128
|
+
sources: /* @__PURE__ */ new Map()
|
|
129
|
+
};
|
|
130
|
+
if (signal.name && (!current.displayName || current.displayName.includes("@"))) current.displayName = signal.name;
|
|
131
|
+
current.inferredKind = current.inferredKind === "unknown" ? inferredKind(signal) : current.inferredKind;
|
|
132
|
+
current.confidence = Math.max(current.confidence, confidence(signal));
|
|
133
|
+
current.firstObservedAt = Math.min(current.firstObservedAt, time);
|
|
134
|
+
current.lastObservedAt = Math.max(current.lastObservedAt, time);
|
|
135
|
+
for (const [type, displayValue] of [
|
|
136
|
+
["email", signal.email],
|
|
137
|
+
["username", signal.username],
|
|
138
|
+
["display_name", signal.name]
|
|
139
|
+
]) {
|
|
140
|
+
if (!displayValue) continue;
|
|
141
|
+
const next = handle(id, type, displayValue, item.sourceInstanceId, time);
|
|
142
|
+
const key = `${type}:${normalized(displayValue)}:${item.sourceInstanceId}`;
|
|
143
|
+
const previous = current.handles.get(key);
|
|
144
|
+
if (previous) {
|
|
145
|
+
previous.firstObservedAt = Math.min(previous.firstObservedAt, time);
|
|
146
|
+
previous.lastObservedAt = Math.max(previous.lastObservedAt, time);
|
|
147
|
+
} else current.handles.set(key, next);
|
|
148
|
+
}
|
|
149
|
+
const source = current.sources.get(item.sourceInstanceId) ?? {
|
|
150
|
+
sourceInstanceId: item.sourceInstanceId,
|
|
151
|
+
interactionCount: 0,
|
|
152
|
+
firstObservedAt: time,
|
|
153
|
+
lastObservedAt: time
|
|
154
|
+
};
|
|
155
|
+
source.interactionCount += 1;
|
|
156
|
+
source.firstObservedAt = Math.min(source.firstObservedAt, time);
|
|
157
|
+
source.lastObservedAt = Math.max(source.lastObservedAt, time);
|
|
158
|
+
current.sources.set(item.sourceInstanceId, source);
|
|
159
|
+
people.set(identityKey, current);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return [...people.values()].map((person) => ({
|
|
163
|
+
id: person.id,
|
|
164
|
+
displayName: person.displayName,
|
|
165
|
+
inferredKind: person.inferredKind,
|
|
166
|
+
confidence: person.confidence,
|
|
167
|
+
firstObservedAt: person.firstObservedAt,
|
|
168
|
+
lastObservedAt: person.lastObservedAt,
|
|
169
|
+
handles: [...person.handles.values()],
|
|
170
|
+
sources: [...person.sources.values()]
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
function rebuildUserPeopleIndex() {
|
|
174
|
+
const revision = currentIndexRevision();
|
|
175
|
+
const entries = buildUserPeopleIndex(loadActiveSourceItems());
|
|
176
|
+
replaceUserPeopleIndex(entries, revision);
|
|
177
|
+
return entries;
|
|
178
|
+
}
|
|
179
|
+
function ensureUserPeopleIndex() {
|
|
180
|
+
const revision = currentIndexRevision();
|
|
181
|
+
const state = getUserPeopleIndexState();
|
|
182
|
+
if (!state || state.sourceChangeSequence !== revision.sourceChangeSequence || state.sourceGrantsUpdatedAt !== revision.sourceGrantsUpdatedAt) rebuildUserPeopleIndex();
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
export { buildUserPeopleIndex, ensureUserPeopleIndex, personIdForIdentity, personIdentityKey, rebuildUserPeopleIndex };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { UserPerson, UserPersonKind } from './types.js';
|
|
2
|
+
export declare function listUserRelationships(options?: {
|
|
3
|
+
query?: string;
|
|
4
|
+
kind?: UserPersonKind;
|
|
5
|
+
sourceInstanceId?: string;
|
|
6
|
+
includeHidden?: boolean;
|
|
7
|
+
hiddenOnly?: boolean;
|
|
8
|
+
cursor?: string;
|
|
9
|
+
limit?: number;
|
|
10
|
+
}): {
|
|
11
|
+
nextCursor?: string;
|
|
12
|
+
items: UserPerson[];
|
|
13
|
+
summary: import("./types.js").UserRelationshipSummary;
|
|
14
|
+
total: number;
|
|
15
|
+
};
|
|
16
|
+
export declare function getUserRelationship(personId: string): UserPerson | null;
|
|
17
|
+
export declare function patchUserRelationship(personId: string, patch: {
|
|
18
|
+
displayName?: string | null;
|
|
19
|
+
kind?: UserPersonKind | null;
|
|
20
|
+
hidden?: boolean;
|
|
21
|
+
}): UserPerson | null;
|
|
22
|
+
export declare function mergeUserRelationships(sourcePersonId: string, targetPersonId: string): UserPerson | null;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { listUnderstandings } from "../../storage/sqlite/user-context-repository.js";
|
|
2
|
+
import { getUserPerson, listUserPeople, mergeUserPeople, summarizeUserPeople, updateUserPerson } from "../../storage/sqlite/user-people-repository.js";
|
|
3
|
+
import "../../storage/sqlite/index.js";
|
|
4
|
+
import { ensureUserPeopleIndex } from "./indexer.js";
|
|
5
|
+
//#region src/user-context/relationships/service.ts
|
|
6
|
+
function withUnderstanding(person) {
|
|
7
|
+
const understanding = listUnderstandings().find((item) => item.kind === "relationship" && item.payload.personId === person.id && !["archived", "rejected"].includes(item.status));
|
|
8
|
+
return understanding ? {
|
|
9
|
+
...person,
|
|
10
|
+
relationshipUnderstanding: {
|
|
11
|
+
id: understanding.id,
|
|
12
|
+
statement: understanding.statement,
|
|
13
|
+
status: understanding.status
|
|
14
|
+
}
|
|
15
|
+
} : person;
|
|
16
|
+
}
|
|
17
|
+
function listUserRelationships(options = {}) {
|
|
18
|
+
ensureUserPeopleIndex();
|
|
19
|
+
const offset = options.cursor && /^\d+$/.test(options.cursor) ? Number(options.cursor) : 0;
|
|
20
|
+
const result = listUserPeople({
|
|
21
|
+
query: options.query,
|
|
22
|
+
kind: options.kind,
|
|
23
|
+
sourceInstanceId: options.sourceInstanceId,
|
|
24
|
+
includeHidden: options.includeHidden,
|
|
25
|
+
hiddenOnly: options.hiddenOnly,
|
|
26
|
+
offset,
|
|
27
|
+
limit: options.limit
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
items: result.items.map(withUnderstanding),
|
|
31
|
+
summary: summarizeUserPeople(),
|
|
32
|
+
total: result.total,
|
|
33
|
+
...result.nextOffset === void 0 ? {} : { nextCursor: String(result.nextOffset) }
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function getUserRelationship(personId) {
|
|
37
|
+
ensureUserPeopleIndex();
|
|
38
|
+
const person = getUserPerson(personId);
|
|
39
|
+
return person ? withUnderstanding(person) : null;
|
|
40
|
+
}
|
|
41
|
+
function patchUserRelationship(personId, patch) {
|
|
42
|
+
const person = updateUserPerson(personId, patch);
|
|
43
|
+
return person ? withUnderstanding(person) : null;
|
|
44
|
+
}
|
|
45
|
+
function mergeUserRelationships(sourcePersonId, targetPersonId) {
|
|
46
|
+
const person = mergeUserPeople(sourcePersonId, targetPersonId);
|
|
47
|
+
return person ? withUnderstanding(person) : null;
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { getUserRelationship, listUserRelationships, mergeUserRelationships, patchUserRelationship };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export declare const USER_PERSON_KINDS: readonly ["person", "bot", "service", "group", "unknown"];
|
|
2
|
+
export type UserPersonKind = typeof USER_PERSON_KINDS[number];
|
|
3
|
+
export type UserPersonHandle = {
|
|
4
|
+
id: string;
|
|
5
|
+
type: 'email' | 'provider_user' | 'username' | 'display_name';
|
|
6
|
+
value: string;
|
|
7
|
+
sourceInstanceId: string;
|
|
8
|
+
verification: 'observed' | 'inferred' | 'user_confirmed';
|
|
9
|
+
firstObservedAt: number;
|
|
10
|
+
lastObservedAt: number;
|
|
11
|
+
};
|
|
12
|
+
export type UserPersonSource = {
|
|
13
|
+
sourceInstanceId: string;
|
|
14
|
+
connectorId?: string;
|
|
15
|
+
toolkit?: string;
|
|
16
|
+
interactionCount: number;
|
|
17
|
+
firstObservedAt: number;
|
|
18
|
+
lastObservedAt: number;
|
|
19
|
+
};
|
|
20
|
+
export type UserPerson = {
|
|
21
|
+
id: string;
|
|
22
|
+
displayName: string;
|
|
23
|
+
kind: UserPersonKind;
|
|
24
|
+
hidden: boolean;
|
|
25
|
+
confidence: number;
|
|
26
|
+
primaryHandle?: string;
|
|
27
|
+
interactionCount: number;
|
|
28
|
+
firstObservedAt: number;
|
|
29
|
+
lastObservedAt: number;
|
|
30
|
+
handles: UserPersonHandle[];
|
|
31
|
+
sources: UserPersonSource[];
|
|
32
|
+
relationshipUnderstanding?: {
|
|
33
|
+
id: string;
|
|
34
|
+
statement: string;
|
|
35
|
+
status: 'candidate' | 'active' | 'needs_review' | 'stale' | 'archived' | 'rejected';
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
export type UserRelationshipSummary = {
|
|
39
|
+
people: number;
|
|
40
|
+
automatedAccounts: number;
|
|
41
|
+
needsReview: number;
|
|
42
|
+
hidden: number;
|
|
43
|
+
sources: number;
|
|
44
|
+
lastUpdatedAt?: number;
|
|
45
|
+
};
|
|
46
|
+
export type UserPersonIndexEntry = {
|
|
47
|
+
id: string;
|
|
48
|
+
displayName: string;
|
|
49
|
+
inferredKind: UserPersonKind;
|
|
50
|
+
confidence: number;
|
|
51
|
+
firstObservedAt: number;
|
|
52
|
+
lastObservedAt: number;
|
|
53
|
+
handles: UserPersonHandle[];
|
|
54
|
+
sources: UserPersonSource[];
|
|
55
|
+
};
|
|
@@ -55,6 +55,6 @@ export declare function updateUnderstandingSourceRun(id: string, patch: {
|
|
|
55
55
|
}): UnderstandingSourceRun | null;
|
|
56
56
|
export declare function upsertUserFocus(input: UserFocusInput): UserFocus;
|
|
57
57
|
export declare function listUserFocuses(statuses?: UserFocus['status'][]): UserFocus[];
|
|
58
|
-
export declare function updateUserFocus(id: string, patch: Partial<Pick<UserFocus, 'title' | 'summary' | 'status'>>, nowMs?: number): UserFocus | null;
|
|
58
|
+
export declare function updateUserFocus(id: string, patch: Partial<Pick<UserFocus, 'title' | 'summary' | 'status' | 'validFrom' | 'validTo' | 'reviewAt'>>, nowMs?: number): UserFocus | null;
|
|
59
59
|
export declare function deleteUserFocus(id: string): boolean;
|
|
60
60
|
export {};
|