@xopcai/xopc 0.0.159 → 0.0.161
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/{action-boundary-settings-panel-CaRK2vIy.js → action-boundary-settings-panel-BIVBUiNT.js} +1 -1
- package/dist/gateway/static/root/assets/activity-store-o7GRljfD.js +1 -0
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BeVf03hx.js → agent-avatar-dicebear-9R3szLMc.js} +1 -1
- package/dist/gateway/static/root/assets/agents-_qbUaYbs.js +2 -0
- package/dist/gateway/static/root/assets/{api-BjcBIV2O.js → api-jSTqFMq9.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-BG2ulMYw.js → app-management-settings-panel-D81cyE76.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-0Kjenr2T.js → appearance-settings-Ct6bZQCV.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-0sPqD0FK.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-ChiNnH1e.js → archive-plugin-CfD7AZKn.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-pbBlQR-g.js → attachment-load-CP_Gaxt3.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-CTZDOyDy.js → automation-suggestion-card-BGKM_NDZ.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-BxWBuBsE.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-BeZGzpGF.js → binary-plugins-IPr2EVnS.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-BnDmhN5Z.js → block-editor-B6aHEGA6.js} +3 -3
- package/dist/gateway/static/root/assets/browser-settings-page-C499b9AH.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-Ii36j_7v.js → capability-presets-api-CCX9-LjU.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-settings-panel-Cl3dw4yh.js → capability-presets-settings-panel-Dva9SiPK.js} +2 -2
- package/dist/gateway/static/root/assets/{channel-recipient-api-CFqrSi5K.js → channel-recipient-api-MmLMLDWf.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-DNAU7u9o.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-CIOH7tgn.js +1 -0
- package/dist/gateway/static/root/assets/check-DUBLcKHA.js +1 -0
- package/dist/gateway/static/root/assets/circle-alert-BEjgurr2.js +1 -0
- package/dist/gateway/static/root/assets/cn-JdwAhgNB.js +1 -0
- package/dist/gateway/static/root/assets/{connector-logo-CiLxuZ8S.js → connector-logo-BbjzIs87.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-TwiBp8vY.js +2 -0
- package/dist/gateway/static/root/assets/createLucideIcon-DPHK1VkS.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-CYH5ZSXy.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-settings-DQLqGLc0.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-sprite-DTqXSpqf.js +1 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-CC2VL5Mc.js → directory-picker-path-field-ClsiXhmz.js} +1 -1
- package/dist/gateway/static/root/assets/{dreaming-settings-KY7ZfVsy.js → dreaming-settings-CjMPPdzg.js} +3 -3
- package/dist/gateway/static/root/assets/{extension-debug-page-C8KBeucs.js → extension-debug-page-57eizjhB.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CV-5Kcyk.js → extension-page-CsbSb3u1.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DvUtDrAu.js → extension-settings-page-DKA9-lyP.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-DXfVeJLc.js → gateway-config-api-DIPJuzBZ.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-CDLqAQY7.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-cxQ94qMm.js → gateway-startup-retry-DrfSJtvT.js} +1 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-Q3B0E-0s.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-C5VAK04G.js +4 -0
- package/dist/gateway/static/root/assets/{goals-config-api-P8VhLqYF.js → goals-config-api-lGC10UkU.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-BJb-5IYw.js +2 -0
- package/dist/gateway/static/root/assets/{goals-settings-DHXyaH_O.js → goals-settings-BWq0pdrA.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-CNckP_Dv.js → heartbeat-settings-D4o8BJVv.js} +1 -1
- package/dist/gateway/static/root/assets/index-CkUHH170.css +1 -0
- package/dist/gateway/static/root/assets/index-vq_cJZJC.js +199 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-jXmv1RVX.js → keyboard-shortcuts-settings-6T2QYehS.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-euH17Kvb.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-Cs5LKjtc.js → local-apps-page-DCjg2K9f.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-C_M71MK9.js → locale-store-Dar-xqQr.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-CyYCBqym.js +7 -0
- package/dist/gateway/static/root/assets/{markdown-split-BFqeSNte.js → markdown-split-rt1XO43g.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-Cc8379gq.js → media-plugins-Bwo8CJuh.js} +1 -1
- package/dist/gateway/static/root/assets/messages-CxySDn9d.js +5 -0
- package/dist/gateway/static/root/assets/models-hub-panel-DObxxjc9.js +2 -0
- package/dist/gateway/static/root/assets/{notes-page-DlGU4ipo.js → notes-page-Dp-pkX7d.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-CCb4RXcZ.js +4 -0
- package/dist/gateway/static/root/assets/{page-tabs-kFOswGlG.js → page-tabs-CzZ4iKOb.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-CvjuvABg.js → preview-open-alternatives-3E_Qa-58.js} +1 -1
- package/dist/gateway/static/root/assets/product-delivery-BTRT9kCS.js +1 -0
- package/dist/gateway/static/root/assets/product-open-page-B2T48Sbt.js +1 -0
- package/dist/gateway/static/root/assets/project-detail-page-BvEcriRC.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-B_e79MRJ.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-DGcqnYfR.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-DwrD7ODx.js → save-bar-controls-CkeKasGm.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-Ba4WzTmE.js → schedule-field-TkqUL3wm.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-DvydL8eF.js → schema-form-DC-iU5IG.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DQZhrDJ8.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BkdeN99Y.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-LaQqGGej.js → settings-collapsible-section-Dpqth3oA.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-DruI__qO.js → settings-form-section-CNqF6IwY.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-UvA-eDhF.js → settings-loading-skeleton-DkvwitK_.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-DG-XMIPu.js → settings-page-layout-DWvNgISx.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-5oCGSebm.js → settings-page-qPG7I2zd.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-lwRAh9aw.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-FLVeTrRt.js → share-preview-page-DGpSPW-n.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-BGj9IVUr.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-B2UBAs5U.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-DdjzFZ8I.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-CeK93xVi.js → system-settings-panel-8TxxSbUw.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-C8ohIHhw.js → theme-store-B5FzMWOx.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-D1Lr2gG0.js → typed-models-lib-DEGtSA5h.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-BWzou5ar.js → use-channel-catalog-BDU6FneG.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-BTTZpAGV.js +1 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-DuDQd-WU.js +1 -0
- package/dist/gateway/static/root/assets/work-discovery-page-BUVkVCsW.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-DeEPPu4n.js +1 -0
- package/dist/gateway/static/root/assets/work-page-DQNUyTUq.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Bynqe4kV.js → workflow-run-setup-panel-Cwi8Pz7n.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-BIy4wrzO.js +11 -0
- package/dist/gateway/static/root/index.html +12 -10
- package/dist/package.js +1 -1
- package/dist/src/automations/service/action-executor.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/chat-stream/mapper.d.ts +1 -0
- package/dist/src/gateway/chat-stream/mapper.js +43 -7
- package/dist/src/gateway/hono/routes/home.js +15 -2
- package/dist/src/gateway/hono/routes/work-discovery.js +451 -0
- package/dist/src/gateway/hono/sse.d.ts +2 -0
- package/dist/src/gateway/hono/sse.js +10 -3
- package/dist/src/gateway/run-loop.js +17 -7
- package/dist/src/gateway/server.d.ts +1 -0
- package/dist/src/gateway/server.js +9 -0
- package/dist/src/gateway/service.d.ts +2 -0
- package/dist/src/gateway/service.js +27 -2
- package/dist/src/local-apps/preview-runtime-bridge.d.ts +1 -1
- package/dist/src/local-apps/preview-runtime-bridge.js +14 -11
- package/dist/src/mobile/notification-service.js +1 -0
- package/dist/src/pets/factory.js +23 -26
- package/dist/src/pets/manifest.d.ts +2 -1
- package/dist/src/pets/manifest.js +15 -8
- package/dist/src/pets/validator.js +8 -3
- package/dist/src/proactive/calendar-signals.d.ts +4 -0
- package/dist/src/proactive/calendar-signals.js +120 -0
- package/dist/src/proactive/focus-service.d.ts +62 -0
- package/dist/src/proactive/focus-service.js +173 -0
- package/dist/src/proactive/index.d.ts +7 -0
- package/dist/src/proactive/index.js +7 -0
- package/dist/src/proactive/insight-repository.d.ts +19 -0
- package/dist/src/proactive/insight-repository.js +106 -0
- package/dist/src/proactive/run-processor.d.ts +19 -0
- package/dist/src/proactive/run-processor.js +125 -0
- package/dist/src/proactive/trial-expiry-reconciler.d.ts +5 -0
- package/dist/src/proactive/trial-expiry-reconciler.js +30 -0
- package/dist/src/proactive/types.d.ts +64 -0
- package/dist/src/proactive/types.js +1 -0
- package/dist/src/proactive/watch-repository.d.ts +28 -0
- package/dist/src/proactive/watch-repository.js +129 -0
- package/dist/src/storage/sqlite/knowledge-repository.d.ts +1 -0
- package/dist/src/storage/sqlite/knowledge-repository.js +4 -0
- package/dist/src/storage/sqlite/migrations/047_work_discovery_sources.sql +16 -0
- package/dist/src/storage/sqlite/migrations/048_work_understanding_investigations.sql +40 -0
- package/dist/src/storage/sqlite/migrations/049_work_understanding_threads.sql +52 -0
- package/dist/src/storage/sqlite/migrations/050_work_discovery_source_refreshes.sql +15 -0
- package/dist/src/storage/sqlite/migrations/051_focus_watches.sql +26 -0
- package/dist/src/storage/sqlite/migrations/052_proactive_insights.sql +25 -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/work-discovery/analyzer.d.ts +12 -1
- package/dist/src/work-discovery/analyzer.js +126 -7
- package/dist/src/work-discovery/candidate-discovery.d.ts +17 -0
- package/dist/src/work-discovery/candidate-discovery.js +279 -0
- package/dist/src/work-discovery/evaluation.d.ts +11 -0
- package/dist/src/work-discovery/evaluation.js +17 -0
- package/dist/src/work-discovery/governance.d.ts +25 -0
- package/dist/src/work-discovery/governance.js +96 -0
- package/dist/src/work-discovery/incremental-repository.d.ts +13 -0
- package/dist/src/work-discovery/incremental-repository.js +55 -0
- package/dist/src/work-discovery/incremental.d.ts +2 -0
- package/dist/src/work-discovery/incremental.js +7 -0
- package/dist/src/work-discovery/index.d.ts +10 -0
- package/dist/src/work-discovery/index.js +13 -3
- package/dist/src/work-discovery/investigation-repository.d.ts +16 -0
- package/dist/src/work-discovery/investigation-repository.js +122 -0
- package/dist/src/work-discovery/investigator.d.ts +55 -0
- package/dist/src/work-discovery/investigator.js +358 -0
- package/dist/src/work-discovery/probe.d.ts +22 -0
- package/dist/src/work-discovery/probe.js +88 -1
- package/dist/src/work-discovery/service.d.ts +110 -1
- package/dist/src/work-discovery/service.js +578 -8
- package/dist/src/work-discovery/source-repository.d.ts +13 -0
- package/dist/src/work-discovery/source-repository.js +72 -0
- package/dist/src/work-discovery/thread-repository.d.ts +34 -0
- package/dist/src/work-discovery/thread-repository.js +112 -0
- package/dist/src/work-discovery/thread-service.d.ts +8 -0
- package/dist/src/work-discovery/thread-service.js +104 -0
- package/dist/src/work-discovery/types.d.ts +145 -0
- package/package.json +1 -1
- package/skills/creative/hatch-pet/SKILL.md +2 -2
- package/dist/gateway/static/root/assets/activity-store-C-57rvkX.js +0 -1
- package/dist/gateway/static/root/assets/agents-P05C4A-3.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-DANGrBpN.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-BXfGL_cm.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-534luV0B.js +0 -8
- package/dist/gateway/static/root/assets/channels-settings-M91zFJVi.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-vooEF4lG.js +0 -1
- package/dist/gateway/static/root/assets/check-BODqo66o.js +0 -1
- package/dist/gateway/static/root/assets/circle-alert-RzVebuX2.js +0 -1
- package/dist/gateway/static/root/assets/cn-BQ_8x0bT.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-CV5UulW6.js +0 -2
- package/dist/gateway/static/root/assets/desktop-pet-Cil2eSpW.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-settings-DwsC6l5F.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-sprite-DqZ6fbIj.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-BpJ0v56l.js +0 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-lMHD-2CX.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-yPmF-G7b.js +0 -4
- package/dist/gateway/static/root/assets/goals-page-BHTCKobG.js +0 -2
- package/dist/gateway/static/root/assets/index-BgZhEOkN.css +0 -1
- package/dist/gateway/static/root/assets/index-BuqGh2SW.js +0 -193
- package/dist/gateway/static/root/assets/local-app-workbench-page-zpp_wciE.js +0 -2
- package/dist/gateway/static/root/assets/logs-page-B4CRYqYR.js +0 -7
- package/dist/gateway/static/root/assets/messages-BuT0SR1R.js +0 -5
- package/dist/gateway/static/root/assets/models-hub-panel-1hdoAoB_.js +0 -2
- package/dist/gateway/static/root/assets/notes-workbench-DQEAZTA5.js +0 -4
- package/dist/gateway/static/root/assets/project-detail-page-Bp9bC7A6.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-RNorFXTw.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BqRTcM_6.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-B2EfW1cm.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-CklL85T4.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-D3my1HgB.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-DVxgjrsP.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-Bcu58ZaF.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-CDd-84FE.js +0 -2
- package/dist/gateway/static/root/assets/sliding-segmented-uWmrxE3P.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-1OyJnTyR.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-B2ncRwHS.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-page-BZlY4PZL.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-DPXMrneY.js +0 -1
- package/dist/gateway/static/root/assets/work-page-CTERoqyv.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-CLgsnqFQ.js +0 -11
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import { createLogger } from "../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { getMemoryRecord, upsertMemoryRecord } from "../storage/sqlite/memory-records-repository.js";
|
|
4
|
+
import { upsertKnowledgeSourceItems } from "../storage/sqlite/knowledge-repository.js";
|
|
5
|
+
import "../storage/sqlite/index.js";
|
|
3
6
|
import { ActivityService } from "../activity/service.js";
|
|
4
7
|
import { getAgentDefaultModelRef, init_schema } from "../config/schema.js";
|
|
5
8
|
import { buildSessionKey, init_session_key } from "../routing/session-key.js";
|
|
6
9
|
import { getDefaultAgentId, init_resolve_route } from "../routing/resolve-route.js";
|
|
10
|
+
import { nextMemoryReviewAt } from "../agent/memory/lifecycle.js";
|
|
11
|
+
import { addWorkUnderstandingThreadFeedback, attachWorkUnderstandingThreadEvidence, getWorkUnderstandingThread, listWorkUnderstandingThreads } from "./thread-repository.js";
|
|
7
12
|
import { previewWorkDiscoveryRoot, probeWorkDiscoveryRoot, summarizeWorkContextSnapshot } from "./probe.js";
|
|
8
|
-
import { analyzeWorkContext, workDiscoveryResultMarkdown } from "./analyzer.js";
|
|
13
|
+
import { analyzePersonalContext, analyzeWorkContext, workDiscoveryResultMarkdown } from "./analyzer.js";
|
|
14
|
+
import { discoverWorkCandidates } from "./candidate-discovery.js";
|
|
15
|
+
import { deleteWorkUnderstandingDerivedData, getWorkUnderstandingMetrics, getWorkUnderstandingSourceLineage } from "./governance.js";
|
|
16
|
+
import { workDiscoveryFingerprintsEqual } from "./incremental.js";
|
|
17
|
+
import { findActiveWorkDiscoverySourceRefresh, recordWorkDiscoverySourceRefresh, updateWorkDiscoverySourceRefreshForRun } from "./incremental-repository.js";
|
|
18
|
+
import { appendWorkUnderstandingEvidence, getWorkUnderstandingInvestigationForRun, listWorkUnderstandingEvidence } from "./investigation-repository.js";
|
|
19
|
+
import { investigateWorkContext } from "./investigator.js";
|
|
20
|
+
import { persistWorkThreadsFromDiscovery } from "./thread-service.js";
|
|
9
21
|
import { createWorkDiscoveryRun, getWorkDiscoveryOnboardingState, getWorkDiscoveryRun, getWorkDiscoveryRunByIdempotencyKey, setWorkDiscoveryFeedback, setWorkDiscoveryOnboardingState, updateWorkDiscoveryRun } from "./repository.js";
|
|
10
|
-
import {
|
|
22
|
+
import { getWorkDiscoveryDirectorySource, listWorkDiscoveryDirectorySources, revokeWorkDiscoveryDirectorySource, upsertWorkDiscoveryDirectorySource } from "./source-repository.js";
|
|
23
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
11
24
|
//#region src/work-discovery/service.ts
|
|
12
25
|
init_schema();
|
|
13
26
|
init_session_key();
|
|
@@ -47,8 +60,19 @@ function errorCode(error) {
|
|
|
47
60
|
if (message.includes("timeout")) return "analysis_timeout";
|
|
48
61
|
return "internal_error";
|
|
49
62
|
}
|
|
63
|
+
function hasLongVerbatimOverlap(value, sources, minimumLength = 32) {
|
|
64
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
65
|
+
if (normalized.length < minimumLength) return false;
|
|
66
|
+
const sourceText = sources.map((source) => source.replace(/\s+/g, " "));
|
|
67
|
+
for (let index = 0; index <= normalized.length - minimumLength; index += Math.max(1, Math.floor(minimumLength / 2))) {
|
|
68
|
+
const fragment = normalized.slice(index, index + minimumLength);
|
|
69
|
+
if (sourceText.some((source) => source.includes(fragment))) return true;
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
50
73
|
var WorkDiscoveryService = class {
|
|
51
74
|
abortControllers = /* @__PURE__ */ new Map();
|
|
75
|
+
candidateContexts = /* @__PURE__ */ new Map();
|
|
52
76
|
activity = new ActivityService();
|
|
53
77
|
constructor(options) {
|
|
54
78
|
this.options = options;
|
|
@@ -68,6 +92,339 @@ var WorkDiscoveryService = class {
|
|
|
68
92
|
getRun(id) {
|
|
69
93
|
return getWorkDiscoveryRun(id);
|
|
70
94
|
}
|
|
95
|
+
getInvestigation(runId) {
|
|
96
|
+
const investigation = getWorkUnderstandingInvestigationForRun(runId);
|
|
97
|
+
return investigation ? {
|
|
98
|
+
investigation,
|
|
99
|
+
evidence: listWorkUnderstandingEvidence(investigation.id)
|
|
100
|
+
} : null;
|
|
101
|
+
}
|
|
102
|
+
listWorkThreads(options = {}) {
|
|
103
|
+
return listWorkUnderstandingThreads(options);
|
|
104
|
+
}
|
|
105
|
+
getWorkThread(id) {
|
|
106
|
+
return getWorkUnderstandingThread(id);
|
|
107
|
+
}
|
|
108
|
+
getSourceLineage(sourceId) {
|
|
109
|
+
return getWorkUnderstandingSourceLineage(sourceId);
|
|
110
|
+
}
|
|
111
|
+
deleteSourceDerivedData(sourceId) {
|
|
112
|
+
return deleteWorkUnderstandingDerivedData(sourceId);
|
|
113
|
+
}
|
|
114
|
+
getUnderstandingMetrics() {
|
|
115
|
+
return getWorkUnderstandingMetrics();
|
|
116
|
+
}
|
|
117
|
+
updateWorkThread(input) {
|
|
118
|
+
return addWorkUnderstandingThreadFeedback({
|
|
119
|
+
threadId: input.id,
|
|
120
|
+
decision: input.decision,
|
|
121
|
+
correctedTitle: input.correctedTitle,
|
|
122
|
+
correctedSummary: input.correctedSummary
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async discoverCandidates(signal) {
|
|
126
|
+
return discoverWorkCandidates({
|
|
127
|
+
existingProjects: this.options.projects.list({
|
|
128
|
+
status: "active",
|
|
129
|
+
limit: 500
|
|
130
|
+
}).items.map((project) => ({
|
|
131
|
+
id: project.id,
|
|
132
|
+
workspaceRoot: project.workspaceRoot
|
|
133
|
+
})),
|
|
134
|
+
approvedDirectories: listWorkDiscoveryDirectorySources().map((source) => ({
|
|
135
|
+
id: source.id,
|
|
136
|
+
rootPath: source.rootPath
|
|
137
|
+
})),
|
|
138
|
+
signal
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
listDirectorySources() {
|
|
142
|
+
return listWorkDiscoveryDirectorySources();
|
|
143
|
+
}
|
|
144
|
+
async grantDirectorySource(rootPath) {
|
|
145
|
+
const preview = await previewWorkDiscoveryRoot(rootPath);
|
|
146
|
+
return upsertWorkDiscoveryDirectorySource({
|
|
147
|
+
rootPath: preview.canonicalRootPath,
|
|
148
|
+
displayName: preview.displayName,
|
|
149
|
+
fingerprint: preview.fingerprint
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
async checkDirectorySources() {
|
|
153
|
+
const checks = [];
|
|
154
|
+
for (const source of listWorkDiscoveryDirectorySources()) try {
|
|
155
|
+
const preview = await previewWorkDiscoveryRoot(source.rootPath);
|
|
156
|
+
checks.push(recordWorkDiscoverySourceRefresh({
|
|
157
|
+
sourceId: source.id,
|
|
158
|
+
changed: !workDiscoveryFingerprintsEqual(source.fingerprint, preview.fingerprint),
|
|
159
|
+
previousFingerprint: source.fingerprint,
|
|
160
|
+
currentFingerprint: preview.fingerprint
|
|
161
|
+
}));
|
|
162
|
+
} catch (error) {
|
|
163
|
+
log.warn({
|
|
164
|
+
err: error,
|
|
165
|
+
sourceId: source.id,
|
|
166
|
+
path: source.rootPath
|
|
167
|
+
}, "Work source change check failed");
|
|
168
|
+
}
|
|
169
|
+
return checks;
|
|
170
|
+
}
|
|
171
|
+
async refreshDirectorySourceIfChanged(input) {
|
|
172
|
+
const source = getWorkDiscoveryDirectorySource(input.id);
|
|
173
|
+
if (!source || source.status !== "active") throw new Error("Approved work folder not found");
|
|
174
|
+
const preview = await previewWorkDiscoveryRoot(source.rootPath);
|
|
175
|
+
if (!!workDiscoveryFingerprintsEqual(source.fingerprint, preview.fingerprint)) return {
|
|
176
|
+
changed: false,
|
|
177
|
+
refresh: recordWorkDiscoverySourceRefresh({
|
|
178
|
+
sourceId: source.id,
|
|
179
|
+
changed: false,
|
|
180
|
+
previousFingerprint: source.fingerprint,
|
|
181
|
+
currentFingerprint: preview.fingerprint
|
|
182
|
+
})
|
|
183
|
+
};
|
|
184
|
+
const activeRefresh = findActiveWorkDiscoverySourceRefresh(source.id, preview.fingerprint);
|
|
185
|
+
const activeRun = activeRefresh?.discoveryRunId ? getWorkDiscoveryRun(activeRefresh.discoveryRunId) : null;
|
|
186
|
+
if (activeRefresh && activeRun) return {
|
|
187
|
+
changed: true,
|
|
188
|
+
reused: true,
|
|
189
|
+
refresh: activeRefresh,
|
|
190
|
+
run: activeRun
|
|
191
|
+
};
|
|
192
|
+
const run = await this.startRun({
|
|
193
|
+
rootPath: preview.canonicalRootPath,
|
|
194
|
+
source: "manual_selected_directory",
|
|
195
|
+
idempotencyKey: input.idempotencyKey
|
|
196
|
+
});
|
|
197
|
+
return {
|
|
198
|
+
changed: true,
|
|
199
|
+
run,
|
|
200
|
+
refresh: recordWorkDiscoverySourceRefresh({
|
|
201
|
+
sourceId: source.id,
|
|
202
|
+
changed: true,
|
|
203
|
+
previousFingerprint: source.fingerprint,
|
|
204
|
+
currentFingerprint: preview.fingerprint,
|
|
205
|
+
status: "queued",
|
|
206
|
+
discoveryRunId: run.id
|
|
207
|
+
})
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
revokeDirectorySource(id) {
|
|
211
|
+
return revokeWorkDiscoveryDirectorySource(id);
|
|
212
|
+
}
|
|
213
|
+
async importPersonalContext(rawItems, signal, runId) {
|
|
214
|
+
let remaining = 3e5;
|
|
215
|
+
const allowedSources = new Set([
|
|
216
|
+
"apple_notes",
|
|
217
|
+
"calendar",
|
|
218
|
+
"reminders"
|
|
219
|
+
]);
|
|
220
|
+
const items = rawItems.flatMap((value) => {
|
|
221
|
+
if (!value || typeof value !== "object" || remaining <= 0) return [];
|
|
222
|
+
const raw = value;
|
|
223
|
+
const id = typeof raw.id === "string" ? raw.id.trim().slice(0, 500) : "";
|
|
224
|
+
const title = typeof raw.title === "string" ? raw.title.trim().slice(0, 300) : "";
|
|
225
|
+
const source = typeof raw.source === "string" && allowedSources.has(raw.source) ? raw.source : null;
|
|
226
|
+
if (!id || !title || !source) return [];
|
|
227
|
+
const content = typeof raw.content === "string" ? raw.content.trim().slice(0, Math.min(12e3, remaining)) : "";
|
|
228
|
+
remaining -= content.length;
|
|
229
|
+
const timestamp = (key) => {
|
|
230
|
+
const value = raw[key];
|
|
231
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
232
|
+
return Number.isFinite(new Date(value).getTime()) ? value : void 0;
|
|
233
|
+
};
|
|
234
|
+
return [{
|
|
235
|
+
id,
|
|
236
|
+
source,
|
|
237
|
+
title,
|
|
238
|
+
...typeof raw.group === "string" && raw.group.trim() ? { group: raw.group.trim().slice(0, 200) } : {},
|
|
239
|
+
content,
|
|
240
|
+
...timestamp("createdAt") ? { createdAt: timestamp("createdAt") } : {},
|
|
241
|
+
...timestamp("modifiedAt") ? { modifiedAt: timestamp("modifiedAt") } : {},
|
|
242
|
+
...timestamp("startsAt") ? { startsAt: timestamp("startsAt") } : {},
|
|
243
|
+
...timestamp("endsAt") ? { endsAt: timestamp("endsAt") } : {}
|
|
244
|
+
}];
|
|
245
|
+
}).slice(0, 150);
|
|
246
|
+
if (!items.length) throw new Error("No readable personal context was provided");
|
|
247
|
+
const linkedRun = runId ? getWorkDiscoveryRun(runId) : null;
|
|
248
|
+
const analysis = await analyzePersonalContext({
|
|
249
|
+
config: this.options.getConfig(),
|
|
250
|
+
items,
|
|
251
|
+
...linkedRun?.result ? { workContext: {
|
|
252
|
+
projectSummary: linkedRun.result.projectSummary,
|
|
253
|
+
currentState: linkedRun.result.currentState,
|
|
254
|
+
uncertainties: linkedRun.result.uncertainties,
|
|
255
|
+
workThreads: linkedRun.result.workThreads
|
|
256
|
+
} } : {},
|
|
257
|
+
signal
|
|
258
|
+
});
|
|
259
|
+
const calendarItems = items.filter((item) => item.source === "calendar" && item.startsAt != null);
|
|
260
|
+
if (calendarItems.length > 0) upsertKnowledgeSourceItems(calendarItems.map((item) => {
|
|
261
|
+
const normalizedText = JSON.stringify({
|
|
262
|
+
title: item.title,
|
|
263
|
+
description: item.content,
|
|
264
|
+
start: new Date(item.startsAt).toISOString(),
|
|
265
|
+
...item.endsAt != null ? { end: new Date(item.endsAt).toISOString() } : {},
|
|
266
|
+
...item.group ? { calendar: item.group } : {}
|
|
267
|
+
});
|
|
268
|
+
return {
|
|
269
|
+
sourceInstanceId: "desktop:calendar",
|
|
270
|
+
externalId: item.id,
|
|
271
|
+
itemType: "calendar_event",
|
|
272
|
+
authorRole: "unknown",
|
|
273
|
+
occurredAt: new Date(item.startsAt).toISOString(),
|
|
274
|
+
...item.modifiedAt != null ? { sourceUpdatedAt: new Date(item.modifiedAt).toISOString() } : {},
|
|
275
|
+
contentHash: createHash("sha256").update(normalizedText).digest("hex"),
|
|
276
|
+
normalizedText,
|
|
277
|
+
metadata: {
|
|
278
|
+
provider: "desktop-calendar",
|
|
279
|
+
calendar: item.group ?? ""
|
|
280
|
+
},
|
|
281
|
+
sensitivity: "personal",
|
|
282
|
+
retentionClass: "bounded",
|
|
283
|
+
synthesisPipeline: "user_understanding",
|
|
284
|
+
synthesisStatus: "ignored"
|
|
285
|
+
};
|
|
286
|
+
}));
|
|
287
|
+
const rawContents = items.flatMap((item) => item.content ? [item.content] : []);
|
|
288
|
+
const safeProfileCandidates = analysis.profileCandidates.filter((candidate) => !hasLongVerbatimOverlap(candidate.statement, rawContents));
|
|
289
|
+
const safeWorkThreadCandidates = analysis.workThreadCandidates.filter((candidate) => !hasLongVerbatimOverlap(candidate.title, rawContents) && !hasLongVerbatimOverlap(candidate.summary, rawContents));
|
|
290
|
+
const agentId = getDefaultAgentId(this.options.getConfig());
|
|
291
|
+
const profileCandidates = safeProfileCandidates.map((candidate) => {
|
|
292
|
+
const autoRemember = candidate.confidence === "high" && candidate.category !== "focus" && candidate.evidence.length >= 2;
|
|
293
|
+
const record = upsertMemoryRecord({
|
|
294
|
+
providerId: "local",
|
|
295
|
+
kind: candidate.category === "preference" ? "preference" : candidate.category === "workflow" ? "routine" : "derived_insight",
|
|
296
|
+
sourceAgentId: agentId,
|
|
297
|
+
content: candidate.statement,
|
|
298
|
+
source: {
|
|
299
|
+
provider: "personal-context",
|
|
300
|
+
path: "personal-context://onboarding"
|
|
301
|
+
},
|
|
302
|
+
confidence: candidate.confidence === "high" ? .9 : candidate.confidence === "medium" ? .72 : .58,
|
|
303
|
+
tags: [
|
|
304
|
+
"user-understanding",
|
|
305
|
+
"personal-context",
|
|
306
|
+
`work-discovery:${candidate.category}`
|
|
307
|
+
],
|
|
308
|
+
status: autoRemember ? "active" : "candidate",
|
|
309
|
+
sensitivity: "normal",
|
|
310
|
+
explicitness: "inferred",
|
|
311
|
+
durability: candidate.category === "focus" ? "ephemeral" : candidate.category === "workflow" ? "recurring" : "durable",
|
|
312
|
+
importance: candidate.category === "focus" ? .75 : .65,
|
|
313
|
+
disclosurePolicy: "referenceable"
|
|
314
|
+
});
|
|
315
|
+
return {
|
|
316
|
+
...candidate,
|
|
317
|
+
status: autoRemember ? "accepted" : "pending",
|
|
318
|
+
memoryRecordId: record.id
|
|
319
|
+
};
|
|
320
|
+
});
|
|
321
|
+
const investigation = linkedRun ? getWorkUnderstandingInvestigationForRun(linkedRun.id) : null;
|
|
322
|
+
const contextEvidence = investigation ? safeWorkThreadCandidates.map((candidate) => {
|
|
323
|
+
const topicHash = createHash("sha256").update(candidate.topicKey).digest("hex").slice(0, 16);
|
|
324
|
+
return appendWorkUnderstandingEvidence({
|
|
325
|
+
investigationId: investigation.id,
|
|
326
|
+
projectId: linkedRun.projectId,
|
|
327
|
+
sourceType: "personal_context",
|
|
328
|
+
sourceRef: `personal-context://thread/${topicHash}`,
|
|
329
|
+
observation: candidate.summary,
|
|
330
|
+
contentHash: createHash("sha256").update(JSON.stringify(candidate.evidenceRefs)).digest("hex"),
|
|
331
|
+
sensitivity: "normal"
|
|
332
|
+
});
|
|
333
|
+
}) : [];
|
|
334
|
+
const workThreads = linkedRun && contextEvidence.length ? persistWorkThreadsFromDiscovery({
|
|
335
|
+
projectId: linkedRun.projectId,
|
|
336
|
+
result: {
|
|
337
|
+
projectSummary: "Personal work context",
|
|
338
|
+
currentState: "Work streams inferred from explicitly connected personal sources.",
|
|
339
|
+
uncertainties: [],
|
|
340
|
+
suggestions: [],
|
|
341
|
+
workThreadCandidates: safeWorkThreadCandidates.map((candidate, index) => ({
|
|
342
|
+
...candidate,
|
|
343
|
+
evidenceRefs: contextEvidence[index] ? [contextEvidence[index].sourceRef] : []
|
|
344
|
+
}))
|
|
345
|
+
},
|
|
346
|
+
snapshot: {
|
|
347
|
+
root: {
|
|
348
|
+
displayName: "Personal Context",
|
|
349
|
+
projectKind: "general",
|
|
350
|
+
markerReasons: ["explicitly_connected_personal_context"]
|
|
351
|
+
},
|
|
352
|
+
structure: {
|
|
353
|
+
sampledPaths: [],
|
|
354
|
+
metadataOnlyFiles: [],
|
|
355
|
+
omittedPathCount: 0
|
|
356
|
+
},
|
|
357
|
+
documents: [],
|
|
358
|
+
limits: {
|
|
359
|
+
policyVersion: 1,
|
|
360
|
+
fileCount: 0,
|
|
361
|
+
contentBytes: 0,
|
|
362
|
+
truncated: false
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
evidence: contextEvidence
|
|
366
|
+
}) : [];
|
|
367
|
+
log.info({
|
|
368
|
+
runId,
|
|
369
|
+
itemCount: items.length,
|
|
370
|
+
candidateCount: profileCandidates.length,
|
|
371
|
+
workThreadCount: workThreads.length
|
|
372
|
+
}, "Personal context analyzed");
|
|
373
|
+
return {
|
|
374
|
+
profileCandidates,
|
|
375
|
+
workThreads,
|
|
376
|
+
autoRemembered: profileCandidates.filter((candidate) => candidate.status === "accepted")
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
updatePersonalContextProfile(input) {
|
|
380
|
+
return input.decisions.flatMap((decision) => {
|
|
381
|
+
const record = getMemoryRecord(decision.memoryRecordId);
|
|
382
|
+
if (!record || record.source.provider !== "personal-context") return [];
|
|
383
|
+
const statement = decision.status === "edited" ? decision.statement?.trim().slice(0, 500) : void 0;
|
|
384
|
+
if (decision.status === "edited" && !statement) return [];
|
|
385
|
+
const updated = this.preserveMemoryRecord(record, {
|
|
386
|
+
providerId: "local",
|
|
387
|
+
status: decision.status === "rejected" ? "rejected" : "active",
|
|
388
|
+
...statement ? {
|
|
389
|
+
content: statement,
|
|
390
|
+
explicitness: "explicit",
|
|
391
|
+
confidence: 1
|
|
392
|
+
} : {},
|
|
393
|
+
...decision.status !== "rejected" ? { reviewAfter: nextMemoryReviewAt(record) } : {}
|
|
394
|
+
});
|
|
395
|
+
return [{
|
|
396
|
+
memoryRecordId: updated.id,
|
|
397
|
+
status: decision.status,
|
|
398
|
+
statement: updated.content
|
|
399
|
+
}];
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
async rescanDirectorySource(input) {
|
|
403
|
+
const source = getWorkDiscoveryDirectorySource(input.id);
|
|
404
|
+
if (!source || source.status !== "active") throw new Error("Approved work folder not found");
|
|
405
|
+
const preview = await previewWorkDiscoveryRoot(source.rootPath);
|
|
406
|
+
upsertWorkDiscoveryDirectorySource({
|
|
407
|
+
rootPath: preview.canonicalRootPath,
|
|
408
|
+
displayName: preview.displayName,
|
|
409
|
+
fingerprint: preview.fingerprint
|
|
410
|
+
});
|
|
411
|
+
return this.startRun({
|
|
412
|
+
rootPath: preview.canonicalRootPath,
|
|
413
|
+
source: "manual_selected_directory",
|
|
414
|
+
idempotencyKey: input.idempotencyKey
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
async startQuickRun(input) {
|
|
418
|
+
const candidates = await this.discoverCandidates();
|
|
419
|
+
const primary = candidates[0];
|
|
420
|
+
if (!primary) throw new Error("No accessible work projects were found");
|
|
421
|
+
return this.startRun({
|
|
422
|
+
rootPath: primary.rootPath,
|
|
423
|
+
source: "onboarding_selected_directory",
|
|
424
|
+
idempotencyKey: input.idempotencyKey,
|
|
425
|
+
candidateContext: candidates
|
|
426
|
+
});
|
|
427
|
+
}
|
|
71
428
|
async startRun(input) {
|
|
72
429
|
const existing = getWorkDiscoveryRunByIdempotencyKey(input.idempotencyKey);
|
|
73
430
|
if (existing) return existing;
|
|
@@ -154,6 +511,7 @@ var WorkDiscoveryService = class {
|
|
|
154
511
|
});
|
|
155
512
|
const controller = new AbortController();
|
|
156
513
|
this.abortControllers.set(run.id, controller);
|
|
514
|
+
if (input.candidateContext?.length) this.candidateContexts.set(run.id, input.candidateContext.slice(0, 8));
|
|
157
515
|
this.execute(run, controller.signal);
|
|
158
516
|
return run;
|
|
159
517
|
}
|
|
@@ -185,11 +543,81 @@ var WorkDiscoveryService = class {
|
|
|
185
543
|
snapshot: summarizeWorkContextSnapshot(snapshot)
|
|
186
544
|
});
|
|
187
545
|
this.publish(current);
|
|
188
|
-
const
|
|
546
|
+
const approvedSource = listWorkDiscoveryDirectorySources().find((source) => source.rootPath === run.rootPath);
|
|
547
|
+
const investigation = await investigateWorkContext({
|
|
189
548
|
config: this.options.getConfig(),
|
|
190
549
|
snapshot,
|
|
550
|
+
rootPath: run.rootPath,
|
|
551
|
+
discoveryRunId: run.id,
|
|
552
|
+
projectId: run.projectId,
|
|
553
|
+
...approvedSource ? { sourceGrantId: approvedSource.id } : {},
|
|
191
554
|
signal
|
|
192
555
|
});
|
|
556
|
+
const investigatedSnapshot = {
|
|
557
|
+
...snapshot,
|
|
558
|
+
documents: investigation.documents,
|
|
559
|
+
limits: {
|
|
560
|
+
...snapshot.limits,
|
|
561
|
+
contentBytes: investigation.documents.reduce((sum, document) => sum + document.excerpt.length, 0),
|
|
562
|
+
truncated: snapshot.limits.truncated || investigation.degraded
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
const candidateContext = this.candidateContexts.get(run.id) ?? (run.source === "onboarding_selected_directory" ? await this.discoverCandidates(signal) : void 0);
|
|
566
|
+
const unifiedEvidence = [...investigation.evidence];
|
|
567
|
+
for (const candidate of candidateContext ?? []) {
|
|
568
|
+
if (candidate.rootPath === run.rootPath) continue;
|
|
569
|
+
const sourceRef = candidate.projectId ? `project://${candidate.projectId}` : `project-candidate://${createHash("sha256").update(candidate.rootPath).digest("hex").slice(0, 16)}`;
|
|
570
|
+
unifiedEvidence.push(appendWorkUnderstandingEvidence({
|
|
571
|
+
investigationId: investigation.investigation.id,
|
|
572
|
+
...candidate.projectId ? { projectId: candidate.projectId } : {},
|
|
573
|
+
sourceType: "project_metadata",
|
|
574
|
+
sourceRef,
|
|
575
|
+
observation: `${candidate.displayName} is a related work area with ${candidate.changedFileCount} changed files and activity score ${candidate.score}.`,
|
|
576
|
+
contentHash: createHash("sha256").update(JSON.stringify({
|
|
577
|
+
score: candidate.score,
|
|
578
|
+
branch: candidate.branch,
|
|
579
|
+
changedFileCount: candidate.changedFileCount
|
|
580
|
+
})).digest("hex"),
|
|
581
|
+
...candidate.lastActiveAt ? { observedAt: candidate.lastActiveAt } : {},
|
|
582
|
+
sensitivity: "normal"
|
|
583
|
+
}));
|
|
584
|
+
}
|
|
585
|
+
const analysis = await analyzeWorkContext({
|
|
586
|
+
config: this.options.getConfig(),
|
|
587
|
+
snapshot: investigatedSnapshot,
|
|
588
|
+
...candidateContext?.length ? { candidateContext } : {},
|
|
589
|
+
signal
|
|
590
|
+
});
|
|
591
|
+
const discoveredProjects = candidateContext?.map((candidate) => ({
|
|
592
|
+
rootPath: candidate.rootPath,
|
|
593
|
+
displayName: candidate.displayName,
|
|
594
|
+
score: candidate.score,
|
|
595
|
+
projectKind: candidate.projectKind,
|
|
596
|
+
...candidate.lastActiveAt ? { lastActiveAt: candidate.lastActiveAt } : {},
|
|
597
|
+
evidence: candidate.evidence
|
|
598
|
+
}));
|
|
599
|
+
const baseResult = {
|
|
600
|
+
...analysis.result,
|
|
601
|
+
investigation: {
|
|
602
|
+
id: investigation.investigation.id,
|
|
603
|
+
hypotheses: investigation.investigation.plan.hypotheses,
|
|
604
|
+
questions: investigation.investigation.plan.questions,
|
|
605
|
+
toolCallCount: investigation.investigation.toolCallCount,
|
|
606
|
+
contentCharsRead: investigation.investigation.contentCharsRead,
|
|
607
|
+
degraded: investigation.degraded
|
|
608
|
+
},
|
|
609
|
+
...discoveredProjects?.length ? { discoveredProjects } : {}
|
|
610
|
+
};
|
|
611
|
+
const workThreads = persistWorkThreadsFromDiscovery({
|
|
612
|
+
projectId: run.projectId,
|
|
613
|
+
result: baseResult,
|
|
614
|
+
snapshot: investigatedSnapshot,
|
|
615
|
+
evidence: unifiedEvidence
|
|
616
|
+
});
|
|
617
|
+
const result = this.persistProfileCandidates(run, {
|
|
618
|
+
...baseResult,
|
|
619
|
+
...workThreads.length ? { workThreads } : {}
|
|
620
|
+
});
|
|
193
621
|
current = updateWorkDiscoveryRun(run.id, {
|
|
194
622
|
status: "analyzing",
|
|
195
623
|
stage: "next_steps"
|
|
@@ -197,11 +625,11 @@ var WorkDiscoveryService = class {
|
|
|
197
625
|
this.publish(current);
|
|
198
626
|
await this.options.sessions.appendTranscriptCustomMessageEntry(run.sessionKey, {
|
|
199
627
|
customType: "work-discovery-result",
|
|
200
|
-
content: workDiscoveryResultMarkdown(
|
|
628
|
+
content: workDiscoveryResultMarkdown(result),
|
|
201
629
|
display: true,
|
|
202
630
|
details: {
|
|
203
631
|
runId: run.id,
|
|
204
|
-
result
|
|
632
|
+
result
|
|
205
633
|
}
|
|
206
634
|
});
|
|
207
635
|
const project = this.options.projects.get(run.projectId);
|
|
@@ -209,12 +637,28 @@ var WorkDiscoveryService = class {
|
|
|
209
637
|
name: project ? `Continue work on ${project.name}` : "Continue recent work",
|
|
210
638
|
hiddenFromSessionList: false
|
|
211
639
|
});
|
|
640
|
+
if (approvedSource) try {
|
|
641
|
+
const preview = await previewWorkDiscoveryRoot(run.rootPath);
|
|
642
|
+
upsertWorkDiscoveryDirectorySource({
|
|
643
|
+
rootPath: preview.canonicalRootPath,
|
|
644
|
+
displayName: preview.displayName,
|
|
645
|
+
fingerprint: preview.fingerprint,
|
|
646
|
+
scanned: true
|
|
647
|
+
});
|
|
648
|
+
} catch (error) {
|
|
649
|
+
log.debug({
|
|
650
|
+
err: error,
|
|
651
|
+
runId: run.id,
|
|
652
|
+
phase: "source_fingerprint"
|
|
653
|
+
}, "Work source fingerprint update failed");
|
|
654
|
+
}
|
|
212
655
|
current = updateWorkDiscoveryRun(run.id, {
|
|
213
656
|
status: "completed",
|
|
214
657
|
stage: "next_steps",
|
|
215
|
-
result
|
|
658
|
+
result,
|
|
216
659
|
completedAt: Date.now()
|
|
217
660
|
});
|
|
661
|
+
updateWorkDiscoverySourceRefreshForRun(run.id, "completed");
|
|
218
662
|
this.activity.record({
|
|
219
663
|
type: "work_discovery.completed",
|
|
220
664
|
primaryObject: {
|
|
@@ -237,8 +681,8 @@ var WorkDiscoveryService = class {
|
|
|
237
681
|
},
|
|
238
682
|
payload: {
|
|
239
683
|
policyVersion: run.scanPolicyVersion,
|
|
240
|
-
suggestionCount:
|
|
241
|
-
lowConfidence:
|
|
684
|
+
suggestionCount: result.suggestions.length,
|
|
685
|
+
lowConfidence: result.lowConfidence === true
|
|
242
686
|
},
|
|
243
687
|
scopes: [{
|
|
244
688
|
scopeKind: "project",
|
|
@@ -268,6 +712,7 @@ var WorkDiscoveryService = class {
|
|
|
268
712
|
errorMessage: message,
|
|
269
713
|
...canceled ? { canceledAt: Date.now() } : { completedAt: Date.now() }
|
|
270
714
|
});
|
|
715
|
+
updateWorkDiscoverySourceRefreshForRun(run.id, "failed");
|
|
271
716
|
if (current) this.publish(current);
|
|
272
717
|
this.activity.record({
|
|
273
718
|
type: canceled ? "work_discovery.canceled" : "work_discovery.failed",
|
|
@@ -305,8 +750,110 @@ var WorkDiscoveryService = class {
|
|
|
305
750
|
}, `Work discovery failed: ${message}`);
|
|
306
751
|
} finally {
|
|
307
752
|
this.abortControllers.delete(run.id);
|
|
753
|
+
this.candidateContexts.delete(run.id);
|
|
308
754
|
}
|
|
309
755
|
}
|
|
756
|
+
persistProfileCandidates(run, result) {
|
|
757
|
+
if (!result.profileCandidates?.length) return result;
|
|
758
|
+
const profileCandidates = result.profileCandidates.map((candidate) => {
|
|
759
|
+
const kind = candidate.category === "preference" ? "preference" : candidate.category === "workflow" ? "routine" : "derived_insight";
|
|
760
|
+
const confidence = candidate.confidence === "high" ? .9 : candidate.confidence === "medium" ? .72 : .58;
|
|
761
|
+
const durability = candidate.category === "focus" ? "ephemeral" : candidate.category === "workflow" ? "recurring" : "durable";
|
|
762
|
+
const record = upsertMemoryRecord({
|
|
763
|
+
providerId: "local",
|
|
764
|
+
kind,
|
|
765
|
+
sourceAgentId: run.agentId,
|
|
766
|
+
sessionKey: run.sessionKey,
|
|
767
|
+
projectId: run.projectId,
|
|
768
|
+
content: candidate.statement,
|
|
769
|
+
source: {
|
|
770
|
+
provider: "work-discovery",
|
|
771
|
+
path: `work-discovery://${run.id}`
|
|
772
|
+
},
|
|
773
|
+
confidence,
|
|
774
|
+
tags: [
|
|
775
|
+
"user-understanding",
|
|
776
|
+
"work-discovery",
|
|
777
|
+
`work-discovery:${candidate.category}`
|
|
778
|
+
],
|
|
779
|
+
status: "candidate",
|
|
780
|
+
sensitivity: "normal",
|
|
781
|
+
explicitness: "inferred",
|
|
782
|
+
durability,
|
|
783
|
+
importance: candidate.category === "focus" ? .75 : .65,
|
|
784
|
+
disclosurePolicy: "referenceable"
|
|
785
|
+
});
|
|
786
|
+
return {
|
|
787
|
+
...candidate,
|
|
788
|
+
memoryRecordId: record.id
|
|
789
|
+
};
|
|
790
|
+
});
|
|
791
|
+
return {
|
|
792
|
+
...result,
|
|
793
|
+
profileCandidates
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
updateProfileCandidates(input) {
|
|
797
|
+
const run = getWorkDiscoveryRun(input.runId);
|
|
798
|
+
if (!run?.result?.profileCandidates?.length) return null;
|
|
799
|
+
const decisions = new Map(input.decisions.map((decision) => [decision.id, decision]));
|
|
800
|
+
const profileCandidates = run.result.profileCandidates.map((candidate) => {
|
|
801
|
+
const decision = decisions.get(candidate.id);
|
|
802
|
+
if (!decision || !candidate.memoryRecordId) return candidate;
|
|
803
|
+
const record = getMemoryRecord(candidate.memoryRecordId);
|
|
804
|
+
if (!record) return candidate;
|
|
805
|
+
const statement = decision.status === "edited" ? decision.statement?.trim().slice(0, 500) : void 0;
|
|
806
|
+
if (decision.status === "edited" && !statement) return candidate;
|
|
807
|
+
this.preserveMemoryRecord(record, {
|
|
808
|
+
status: decision.status === "rejected" ? "rejected" : "active",
|
|
809
|
+
...statement ? {
|
|
810
|
+
content: statement,
|
|
811
|
+
explicitness: "explicit",
|
|
812
|
+
confidence: 1
|
|
813
|
+
} : {},
|
|
814
|
+
...decision.status !== "rejected" ? { reviewAfter: nextMemoryReviewAt(record) } : {}
|
|
815
|
+
});
|
|
816
|
+
return {
|
|
817
|
+
...candidate,
|
|
818
|
+
...statement ? { statement } : {},
|
|
819
|
+
status: decision.status
|
|
820
|
+
};
|
|
821
|
+
});
|
|
822
|
+
return updateWorkDiscoveryRun(run.id, { result: {
|
|
823
|
+
...run.result,
|
|
824
|
+
profileCandidates
|
|
825
|
+
} });
|
|
826
|
+
}
|
|
827
|
+
preserveMemoryRecord(record, patch) {
|
|
828
|
+
return upsertMemoryRecord({
|
|
829
|
+
id: record.id,
|
|
830
|
+
providerId: "local",
|
|
831
|
+
kind: record.kind,
|
|
832
|
+
sourceAgentId: record.provenance.sourceAgentId,
|
|
833
|
+
workspaceId: record.scope.workspaceId,
|
|
834
|
+
sessionKey: record.scope.sessionKey,
|
|
835
|
+
projectId: record.scope.projectId,
|
|
836
|
+
content: record.content,
|
|
837
|
+
source: record.source,
|
|
838
|
+
confidence: record.confidence,
|
|
839
|
+
tags: record.tags,
|
|
840
|
+
status: record.status,
|
|
841
|
+
sensitivity: record.sensitivity,
|
|
842
|
+
canonicalKey: record.canonicalKey,
|
|
843
|
+
explicitness: record.explicitness,
|
|
844
|
+
durability: record.durability,
|
|
845
|
+
importance: record.importance,
|
|
846
|
+
disclosurePolicy: record.disclosurePolicy,
|
|
847
|
+
evidence: record.evidence,
|
|
848
|
+
reviewAfter: record.reviewAfter,
|
|
849
|
+
expiresAt: record.expiresAt,
|
|
850
|
+
validFrom: record.validFrom,
|
|
851
|
+
validTo: record.validTo,
|
|
852
|
+
supersedesRecordId: record.supersedesRecordId,
|
|
853
|
+
conflictGroupId: record.conflictGroupId,
|
|
854
|
+
...patch
|
|
855
|
+
});
|
|
856
|
+
}
|
|
310
857
|
cancelRun(id) {
|
|
311
858
|
const run = getWorkDiscoveryRun(id);
|
|
312
859
|
if (!run) return null;
|
|
@@ -352,6 +899,29 @@ var WorkDiscoveryService = class {
|
|
|
352
899
|
recognitionDecision: input.decision,
|
|
353
900
|
...correctedIntent ? { correctedIntent } : {}
|
|
354
901
|
});
|
|
902
|
+
const investigation = getWorkUnderstandingInvestigationForRun(run.id);
|
|
903
|
+
if (investigation && input.decision !== "dismissed") {
|
|
904
|
+
const observation = correctedIntent ? `The user explicitly stated their current intent: ${correctedIntent}` : "The user explicitly confirmed the inferred current work understanding.";
|
|
905
|
+
const evidence = appendWorkUnderstandingEvidence({
|
|
906
|
+
investigationId: investigation.id,
|
|
907
|
+
projectId: run.projectId,
|
|
908
|
+
sourceType: "user_statement",
|
|
909
|
+
sourceRef: `session://${createHash("sha256").update(run.sessionKey).digest("hex").slice(0, 16)}/recognition`,
|
|
910
|
+
observation,
|
|
911
|
+
contentHash: createHash("sha256").update(observation).digest("hex"),
|
|
912
|
+
observedAt: Date.now(),
|
|
913
|
+
sensitivity: "normal"
|
|
914
|
+
});
|
|
915
|
+
const currentThreads = listWorkUnderstandingThreads({
|
|
916
|
+
projectId: run.projectId,
|
|
917
|
+
limit: 50
|
|
918
|
+
}).filter((thread) => thread.horizon === "current").slice(0, 3);
|
|
919
|
+
for (const thread of currentThreads) attachWorkUnderstandingThreadEvidence({
|
|
920
|
+
threadId: thread.id,
|
|
921
|
+
evidenceId: evidence.id,
|
|
922
|
+
projectId: run.projectId
|
|
923
|
+
});
|
|
924
|
+
}
|
|
355
925
|
if (run.source === "onboarding_selected_directory") setWorkDiscoveryOnboardingState({
|
|
356
926
|
status: input.decision === "dismissed" ? "dismissed" : "completed",
|
|
357
927
|
activeRunId: input.decision === "dismissed" ? null : run.id
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { WorkDiscoveryDirectorySource, WorkDiscoveryPreview } from './types.js';
|
|
2
|
+
export declare function listWorkDiscoveryDirectorySources(options?: {
|
|
3
|
+
includeRevoked?: boolean;
|
|
4
|
+
}): WorkDiscoveryDirectorySource[];
|
|
5
|
+
export declare function getWorkDiscoveryDirectorySource(id: string): WorkDiscoveryDirectorySource | null;
|
|
6
|
+
export declare function upsertWorkDiscoveryDirectorySource(input: {
|
|
7
|
+
rootPath: string;
|
|
8
|
+
displayName: string;
|
|
9
|
+
fingerprint?: WorkDiscoveryPreview['fingerprint'];
|
|
10
|
+
scanned?: boolean;
|
|
11
|
+
nowMs?: number;
|
|
12
|
+
}): WorkDiscoveryDirectorySource;
|
|
13
|
+
export declare function revokeWorkDiscoveryDirectorySource(id: string, nowMs?: number): WorkDiscoveryDirectorySource | null;
|