@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
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { requireXopcDatabase } from "../storage/sqlite/connection.js";
|
|
2
|
+
import { runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
//#region src/proactive/watch-repository.ts
|
|
5
|
+
function watchFromRow(row) {
|
|
6
|
+
let config = {};
|
|
7
|
+
try {
|
|
8
|
+
const parsed = JSON.parse(row.config_json);
|
|
9
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) config = parsed;
|
|
10
|
+
} catch {
|
|
11
|
+
config = {};
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
id: row.watch_id,
|
|
15
|
+
threadId: row.thread_id,
|
|
16
|
+
...row.goal_id ? { goalId: row.goal_id } : {},
|
|
17
|
+
automationId: row.automation_id,
|
|
18
|
+
kind: row.kind,
|
|
19
|
+
status: row.status,
|
|
20
|
+
config,
|
|
21
|
+
...row.trial_ends_at != null ? { trialEndsAt: row.trial_ends_at } : {},
|
|
22
|
+
...row.last_cursor ? { lastCursor: row.last_cursor } : {},
|
|
23
|
+
...row.last_run_at != null ? { lastRunAt: row.last_run_at } : {},
|
|
24
|
+
...row.last_useful_result_at != null ? { lastUsefulResultAt: row.last_useful_result_at } : {},
|
|
25
|
+
consecutiveEmptyRuns: row.consecutive_empty_runs,
|
|
26
|
+
createdAt: row.created_at,
|
|
27
|
+
updatedAt: row.updated_at
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function listFocusWatches(options = {}) {
|
|
31
|
+
const clauses = [];
|
|
32
|
+
const values = [];
|
|
33
|
+
if (options.threadId) {
|
|
34
|
+
clauses.push("thread_id = ?");
|
|
35
|
+
values.push(options.threadId);
|
|
36
|
+
}
|
|
37
|
+
if (options.status) {
|
|
38
|
+
clauses.push("status = ?");
|
|
39
|
+
values.push(options.status);
|
|
40
|
+
}
|
|
41
|
+
const { db } = requireXopcDatabase();
|
|
42
|
+
return db.prepare(`SELECT * FROM focus_watches ${clauses.length ? `WHERE ${clauses.join(" AND ")}` : ""}
|
|
43
|
+
ORDER BY updated_at DESC`).all(...values).map(watchFromRow);
|
|
44
|
+
}
|
|
45
|
+
function getFocusWatchByThreadAndKind(threadId, kind) {
|
|
46
|
+
const { db } = requireXopcDatabase();
|
|
47
|
+
const row = db.prepare("SELECT * FROM focus_watches WHERE thread_id = ? AND kind = ?").get(threadId, kind);
|
|
48
|
+
return row ? watchFromRow(row) : null;
|
|
49
|
+
}
|
|
50
|
+
function getFocusWatchByAutomationId(automationId) {
|
|
51
|
+
const { db } = requireXopcDatabase();
|
|
52
|
+
const row = db.prepare("SELECT * FROM focus_watches WHERE automation_id = ?").get(automationId);
|
|
53
|
+
return row ? watchFromRow(row) : null;
|
|
54
|
+
}
|
|
55
|
+
function createFocusWatch(input) {
|
|
56
|
+
const now = input.nowMs ?? Date.now();
|
|
57
|
+
const id = randomUUID();
|
|
58
|
+
runSqliteWriteTransaction((db) => {
|
|
59
|
+
db.prepare(`INSERT INTO focus_watches (
|
|
60
|
+
watch_id, thread_id, goal_id, automation_id, kind, status, config_json,
|
|
61
|
+
trial_ends_at, consecutive_empty_runs, created_at, updated_at
|
|
62
|
+
) VALUES (?, ?, ?, ?, ?, 'active', ?, ?, 0, ?, ?)`).run(id, input.threadId, input.goalId ?? null, input.automationId, input.kind, JSON.stringify(input.config ?? {}), input.trialEndsAt ?? null, now, now);
|
|
63
|
+
});
|
|
64
|
+
return getFocusWatchByThreadAndKind(input.threadId, input.kind);
|
|
65
|
+
}
|
|
66
|
+
function setFocusWatchStatus(id, status, nowMs = Date.now()) {
|
|
67
|
+
let changed = false;
|
|
68
|
+
runSqliteWriteTransaction((db) => {
|
|
69
|
+
changed = db.prepare("UPDATE focus_watches SET status = ?, updated_at = ? WHERE watch_id = ?").run(status, nowMs, id).changes > 0;
|
|
70
|
+
});
|
|
71
|
+
if (!changed) return null;
|
|
72
|
+
const { db } = requireXopcDatabase();
|
|
73
|
+
return watchFromRow(db.prepare("SELECT * FROM focus_watches WHERE watch_id = ?").get(id));
|
|
74
|
+
}
|
|
75
|
+
function restartFocusWatchTrial(id, trialEndsAt, nowMs = Date.now()) {
|
|
76
|
+
let changed = false;
|
|
77
|
+
runSqliteWriteTransaction((db) => {
|
|
78
|
+
changed = db.prepare(`UPDATE focus_watches
|
|
79
|
+
SET status = 'active', trial_ends_at = ?, consecutive_empty_runs = 0, updated_at = ?
|
|
80
|
+
WHERE watch_id = ?`).run(trialEndsAt, nowMs, id).changes > 0;
|
|
81
|
+
});
|
|
82
|
+
if (!changed) return null;
|
|
83
|
+
const { db } = requireXopcDatabase();
|
|
84
|
+
return watchFromRow(db.prepare("SELECT * FROM focus_watches WHERE watch_id = ?").get(id));
|
|
85
|
+
}
|
|
86
|
+
function recordFocusWatchRun(input) {
|
|
87
|
+
const now = input.nowMs ?? Date.now();
|
|
88
|
+
let changed = false;
|
|
89
|
+
runSqliteWriteTransaction((db) => {
|
|
90
|
+
changed = db.prepare(`UPDATE focus_watches SET
|
|
91
|
+
last_cursor = ?,
|
|
92
|
+
last_run_at = ?,
|
|
93
|
+
last_useful_result_at = CASE WHEN ? = 'meaningful' THEN ? ELSE last_useful_result_at END,
|
|
94
|
+
consecutive_empty_runs = CASE
|
|
95
|
+
WHEN ? = 'meaningful' THEN 0
|
|
96
|
+
WHEN ? = 'empty' THEN consecutive_empty_runs + 1
|
|
97
|
+
ELSE consecutive_empty_runs
|
|
98
|
+
END,
|
|
99
|
+
updated_at = ?
|
|
100
|
+
WHERE watch_id = ? AND (last_cursor IS NULL OR last_cursor <> ?)`).run(input.runId, now, input.outcome, now, input.outcome, input.outcome, now, input.id, input.runId).changes > 0;
|
|
101
|
+
});
|
|
102
|
+
if (!changed) return null;
|
|
103
|
+
return listFocusWatches().find((watch) => watch.id === input.id) ?? null;
|
|
104
|
+
}
|
|
105
|
+
function recordFocusWatchFeedback(id, useful, nowMs = Date.now()) {
|
|
106
|
+
const watch = listFocusWatches().find((item) => item.id === id);
|
|
107
|
+
if (!watch) return null;
|
|
108
|
+
const previous = watch.config.feedback && typeof watch.config.feedback === "object" && !Array.isArray(watch.config.feedback) ? watch.config.feedback : {};
|
|
109
|
+
const approved = typeof previous.approved === "number" ? previous.approved : 0;
|
|
110
|
+
const dismissed = typeof previous.dismissed === "number" ? previous.dismissed : 0;
|
|
111
|
+
const consecutiveDismissed = useful ? 0 : (typeof previous.consecutiveDismissed === "number" ? previous.consecutiveDismissed : 0) + 1;
|
|
112
|
+
const config = {
|
|
113
|
+
...watch.config,
|
|
114
|
+
feedback: {
|
|
115
|
+
approved: approved + (useful ? 1 : 0),
|
|
116
|
+
dismissed: dismissed + (useful ? 0 : 1),
|
|
117
|
+
consecutiveDismissed
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
runSqliteWriteTransaction((db) => {
|
|
121
|
+
db.prepare("UPDATE focus_watches SET config_json = ?, updated_at = ? WHERE watch_id = ?").run(JSON.stringify(config), nowMs, id);
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
watch: listFocusWatches().find((item) => item.id === id),
|
|
125
|
+
consecutiveDismissed
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
export { createFocusWatch, getFocusWatchByAutomationId, getFocusWatchByThreadAndKind, listFocusWatches, recordFocusWatchFeedback, recordFocusWatchRun, restartFocusWatchTrial, setFocusWatchStatus };
|
|
@@ -39,6 +39,7 @@ export declare function getKnowledgeConsumerWatermark(consumerId: string, source
|
|
|
39
39
|
export declare function setKnowledgeConsumerWatermark(consumerId: string, sourceInstanceId: string, sequence: number, nowMs?: number): void;
|
|
40
40
|
export declare function listKnowledgeSourceItems(options?: {
|
|
41
41
|
sourceInstanceId?: string;
|
|
42
|
+
itemType?: string;
|
|
42
43
|
synthesisStatus?: KnowledgeSynthesisStatus;
|
|
43
44
|
includeDeleted?: boolean;
|
|
44
45
|
limit?: number;
|
|
@@ -238,6 +238,10 @@ function listKnowledgeSourceItems(options = {}) {
|
|
|
238
238
|
where.push("source_instance_id = ?");
|
|
239
239
|
params.push(options.sourceInstanceId);
|
|
240
240
|
}
|
|
241
|
+
if (options.itemType) {
|
|
242
|
+
where.push("item_type = ?");
|
|
243
|
+
params.push(options.itemType);
|
|
244
|
+
}
|
|
241
245
|
if (options.synthesisStatus) {
|
|
242
246
|
where.push("synthesis_status = ?");
|
|
243
247
|
params.push(options.synthesisStatus);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS work_discovery_sources (
|
|
2
|
+
source_id TEXT PRIMARY KEY,
|
|
3
|
+
kind TEXT NOT NULL CHECK (kind = 'directory'),
|
|
4
|
+
root_path TEXT,
|
|
5
|
+
display_name TEXT NOT NULL,
|
|
6
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'revoked')),
|
|
7
|
+
scope_json TEXT NOT NULL DEFAULT '{}',
|
|
8
|
+
fingerprint_json TEXT,
|
|
9
|
+
last_scanned_at INTEGER,
|
|
10
|
+
created_at INTEGER NOT NULL,
|
|
11
|
+
updated_at INTEGER NOT NULL,
|
|
12
|
+
UNIQUE(kind, root_path)
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
CREATE INDEX idx_work_discovery_sources_kind_status
|
|
16
|
+
ON work_discovery_sources(kind, status, updated_at DESC);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS work_understanding_investigations (
|
|
2
|
+
investigation_id TEXT PRIMARY KEY,
|
|
3
|
+
discovery_run_id TEXT NOT NULL UNIQUE,
|
|
4
|
+
status TEXT NOT NULL CHECK (status IN (
|
|
5
|
+
'planning', 'investigating', 'synthesizing', 'completed', 'failed', 'canceled'
|
|
6
|
+
)),
|
|
7
|
+
plan_json TEXT NOT NULL DEFAULT '{}',
|
|
8
|
+
budget_json TEXT NOT NULL,
|
|
9
|
+
tool_call_count INTEGER NOT NULL DEFAULT 0,
|
|
10
|
+
content_chars_read INTEGER NOT NULL DEFAULT 0,
|
|
11
|
+
error_message TEXT,
|
|
12
|
+
started_at INTEGER NOT NULL,
|
|
13
|
+
completed_at INTEGER,
|
|
14
|
+
FOREIGN KEY (discovery_run_id) REFERENCES work_discovery_runs(id) ON DELETE CASCADE
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
CREATE TABLE IF NOT EXISTS work_understanding_evidence (
|
|
18
|
+
evidence_id TEXT PRIMARY KEY,
|
|
19
|
+
investigation_id TEXT NOT NULL,
|
|
20
|
+
source_grant_id TEXT,
|
|
21
|
+
project_id TEXT,
|
|
22
|
+
source_type TEXT NOT NULL CHECK (source_type IN (
|
|
23
|
+
'file', 'git', 'project_metadata', 'personal_context', 'session', 'user_statement'
|
|
24
|
+
)),
|
|
25
|
+
source_ref TEXT NOT NULL,
|
|
26
|
+
observation TEXT NOT NULL,
|
|
27
|
+
content_hash TEXT,
|
|
28
|
+
observed_at INTEGER,
|
|
29
|
+
collected_at INTEGER NOT NULL,
|
|
30
|
+
sensitivity TEXT NOT NULL DEFAULT 'normal' CHECK (sensitivity IN ('normal', 'restricted')),
|
|
31
|
+
FOREIGN KEY (investigation_id) REFERENCES work_understanding_investigations(investigation_id) ON DELETE CASCADE,
|
|
32
|
+
FOREIGN KEY (source_grant_id) REFERENCES work_discovery_sources(source_id) ON DELETE SET NULL,
|
|
33
|
+
FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE SET NULL
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
CREATE INDEX idx_work_understanding_evidence_investigation
|
|
37
|
+
ON work_understanding_evidence(investigation_id, collected_at ASC);
|
|
38
|
+
|
|
39
|
+
CREATE INDEX idx_work_understanding_evidence_source
|
|
40
|
+
ON work_understanding_evidence(source_grant_id, source_type, collected_at DESC);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS work_understanding_threads (
|
|
2
|
+
thread_id TEXT PRIMARY KEY,
|
|
3
|
+
canonical_key TEXT NOT NULL UNIQUE,
|
|
4
|
+
title TEXT NOT NULL,
|
|
5
|
+
summary TEXT NOT NULL,
|
|
6
|
+
status TEXT NOT NULL CHECK (status IN ('active', 'paused', 'blocked', 'completed', 'uncertain')),
|
|
7
|
+
horizon TEXT NOT NULL CHECK (horizon IN ('current', 'ongoing', 'long_term')),
|
|
8
|
+
focus_score REAL NOT NULL,
|
|
9
|
+
confidence REAL NOT NULL,
|
|
10
|
+
user_status TEXT NOT NULL DEFAULT 'unreviewed' CHECK (user_status IN ('unreviewed', 'confirmed', 'corrected', 'rejected')),
|
|
11
|
+
parent_thread_id TEXT,
|
|
12
|
+
first_observed_at INTEGER NOT NULL,
|
|
13
|
+
last_observed_at INTEGER NOT NULL,
|
|
14
|
+
created_at INTEGER NOT NULL,
|
|
15
|
+
updated_at INTEGER NOT NULL,
|
|
16
|
+
FOREIGN KEY (parent_thread_id) REFERENCES work_understanding_threads(thread_id) ON DELETE SET NULL
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
CREATE TABLE IF NOT EXISTS work_understanding_thread_projects (
|
|
20
|
+
thread_id TEXT NOT NULL,
|
|
21
|
+
project_id TEXT NOT NULL,
|
|
22
|
+
created_at INTEGER NOT NULL,
|
|
23
|
+
PRIMARY KEY (thread_id, project_id),
|
|
24
|
+
FOREIGN KEY (thread_id) REFERENCES work_understanding_threads(thread_id) ON DELETE CASCADE,
|
|
25
|
+
FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
CREATE TABLE IF NOT EXISTS work_understanding_thread_evidence (
|
|
29
|
+
thread_id TEXT NOT NULL,
|
|
30
|
+
evidence_id TEXT NOT NULL,
|
|
31
|
+
relation TEXT NOT NULL DEFAULT 'supports' CHECK (relation IN ('supports', 'contradicts', 'context')),
|
|
32
|
+
created_at INTEGER NOT NULL,
|
|
33
|
+
PRIMARY KEY (thread_id, evidence_id),
|
|
34
|
+
FOREIGN KEY (thread_id) REFERENCES work_understanding_threads(thread_id) ON DELETE CASCADE,
|
|
35
|
+
FOREIGN KEY (evidence_id) REFERENCES work_understanding_evidence(evidence_id) ON DELETE CASCADE
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
CREATE TABLE IF NOT EXISTS work_understanding_thread_feedback (
|
|
39
|
+
feedback_id TEXT PRIMARY KEY,
|
|
40
|
+
thread_id TEXT NOT NULL,
|
|
41
|
+
decision TEXT NOT NULL CHECK (decision IN ('confirmed', 'corrected', 'rejected', 'paused', 'completed')),
|
|
42
|
+
corrected_title TEXT,
|
|
43
|
+
corrected_summary TEXT,
|
|
44
|
+
created_at INTEGER NOT NULL,
|
|
45
|
+
FOREIGN KEY (thread_id) REFERENCES work_understanding_threads(thread_id) ON DELETE CASCADE
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
CREATE INDEX idx_work_understanding_threads_focus
|
|
49
|
+
ON work_understanding_threads(user_status, status, focus_score DESC, last_observed_at DESC);
|
|
50
|
+
|
|
51
|
+
CREATE INDEX idx_work_understanding_thread_projects_project
|
|
52
|
+
ON work_understanding_thread_projects(project_id, created_at DESC);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS work_discovery_source_refreshes (
|
|
2
|
+
refresh_id TEXT PRIMARY KEY,
|
|
3
|
+
source_id TEXT NOT NULL,
|
|
4
|
+
discovery_run_id TEXT,
|
|
5
|
+
changed INTEGER NOT NULL CHECK (changed IN (0, 1)),
|
|
6
|
+
previous_fingerprint_json TEXT,
|
|
7
|
+
current_fingerprint_json TEXT NOT NULL,
|
|
8
|
+
status TEXT NOT NULL CHECK (status IN ('checked', 'queued', 'completed', 'failed')),
|
|
9
|
+
checked_at INTEGER NOT NULL,
|
|
10
|
+
FOREIGN KEY (source_id) REFERENCES work_discovery_sources(source_id) ON DELETE CASCADE,
|
|
11
|
+
FOREIGN KEY (discovery_run_id) REFERENCES work_discovery_runs(id) ON DELETE SET NULL
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
CREATE INDEX idx_work_discovery_source_refreshes_source
|
|
15
|
+
ON work_discovery_source_refreshes(source_id, checked_at DESC);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
CREATE TABLE focus_watches (
|
|
2
|
+
watch_id TEXT PRIMARY KEY,
|
|
3
|
+
thread_id TEXT NOT NULL,
|
|
4
|
+
goal_id TEXT,
|
|
5
|
+
automation_id TEXT NOT NULL,
|
|
6
|
+
kind TEXT NOT NULL CHECK (kind IN ('progress', 'staleness', 'deadline', 'intelligence')),
|
|
7
|
+
status TEXT NOT NULL CHECK (status IN ('active', 'paused')),
|
|
8
|
+
config_json TEXT NOT NULL DEFAULT '{}',
|
|
9
|
+
trial_ends_at INTEGER,
|
|
10
|
+
last_cursor TEXT,
|
|
11
|
+
last_run_at INTEGER,
|
|
12
|
+
last_useful_result_at INTEGER,
|
|
13
|
+
consecutive_empty_runs INTEGER NOT NULL DEFAULT 0,
|
|
14
|
+
created_at INTEGER NOT NULL,
|
|
15
|
+
updated_at INTEGER NOT NULL,
|
|
16
|
+
UNIQUE (thread_id, kind),
|
|
17
|
+
FOREIGN KEY (thread_id) REFERENCES work_understanding_threads(thread_id) ON DELETE CASCADE,
|
|
18
|
+
FOREIGN KEY (goal_id) REFERENCES goals(goal_id) ON DELETE SET NULL,
|
|
19
|
+
FOREIGN KEY (automation_id) REFERENCES automations(automation_id) ON DELETE CASCADE
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
CREATE INDEX idx_focus_watches_thread
|
|
23
|
+
ON focus_watches(thread_id, status, updated_at DESC);
|
|
24
|
+
|
|
25
|
+
CREATE INDEX idx_focus_watches_automation
|
|
26
|
+
ON focus_watches(automation_id);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
CREATE TABLE proactive_insights (
|
|
2
|
+
insight_id TEXT PRIMARY KEY,
|
|
3
|
+
watch_id TEXT NOT NULL,
|
|
4
|
+
run_id TEXT NOT NULL UNIQUE,
|
|
5
|
+
kind TEXT NOT NULL CHECK (kind IN ('progress', 'staleness', 'deadline', 'intelligence')),
|
|
6
|
+
title TEXT NOT NULL,
|
|
7
|
+
summary TEXT NOT NULL,
|
|
8
|
+
why_it_matters TEXT NOT NULL,
|
|
9
|
+
next_action TEXT NOT NULL,
|
|
10
|
+
evidence_json TEXT NOT NULL,
|
|
11
|
+
content_hash TEXT NOT NULL,
|
|
12
|
+
status TEXT NOT NULL CHECK (status IN ('unread', 'read', 'approved', 'dismissed')),
|
|
13
|
+
created_at INTEGER NOT NULL,
|
|
14
|
+
updated_at INTEGER NOT NULL,
|
|
15
|
+
FOREIGN KEY (watch_id) REFERENCES focus_watches(watch_id) ON DELETE CASCADE
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
CREATE INDEX idx_proactive_insights_status
|
|
19
|
+
ON proactive_insights(status, created_at DESC);
|
|
20
|
+
|
|
21
|
+
CREATE INDEX idx_proactive_insights_watch
|
|
22
|
+
ON proactive_insights(watch_id, created_at DESC);
|
|
23
|
+
|
|
24
|
+
CREATE UNIQUE INDEX idx_proactive_insights_dedupe
|
|
25
|
+
ON proactive_insights(watch_id, content_hash);
|
|
@@ -3,7 +3,7 @@ import type { ApplyMigrationsOptions } from './types.js';
|
|
|
3
3
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
4
4
|
export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
5
5
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
6
|
-
export declare const XOPC_DB_SCHEMA_VERSION =
|
|
6
|
+
export declare const XOPC_DB_SCHEMA_VERSION = 52;
|
|
7
7
|
export declare function resolveMigrationsDir(override?: string): string;
|
|
8
8
|
/**
|
|
9
9
|
* Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
|
|
@@ -12,7 +12,7 @@ const log = createLogger("Sqlite:Migrations");
|
|
|
12
12
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
13
13
|
const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
14
14
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
15
|
-
const XOPC_DB_SCHEMA_VERSION =
|
|
15
|
+
const XOPC_DB_SCHEMA_VERSION = 52;
|
|
16
16
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
17
17
|
function resolveMigrationsDir(override) {
|
|
18
18
|
if (override) return override;
|
|
@@ -46,7 +46,7 @@ function applySingleMigration(db, migration) {
|
|
|
46
46
|
* Each step runs in its own transaction and updates schema_meta on success.
|
|
47
47
|
*/
|
|
48
48
|
function applyPendingMigrations(db, options = {}) {
|
|
49
|
-
const targetVersion = options.targetVersion ??
|
|
49
|
+
const targetVersion = options.targetVersion ?? 52;
|
|
50
50
|
let currentVersion = readSchemaVersion(db);
|
|
51
51
|
if (currentVersion > targetVersion) throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
|
|
52
52
|
if (currentVersion >= targetVersion) return currentVersion;
|
|
@@ -62,7 +62,7 @@ function applyPendingMigrations(db, options = {}) {
|
|
|
62
62
|
}
|
|
63
63
|
/** Inspect schema version without mutating the database. */
|
|
64
64
|
function inspectSchemaMigrationStatus(db, options = {}) {
|
|
65
|
-
const appVersion = options.targetVersion ??
|
|
65
|
+
const appVersion = options.targetVersion ?? 52;
|
|
66
66
|
const dbVersion = readSchemaVersion(db);
|
|
67
67
|
if (dbVersion > appVersion) return {
|
|
68
68
|
dbVersion,
|
|
@@ -19,7 +19,7 @@ const log = createLogger('Sqlite:Migrations');
|
|
|
19
19
|
export const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
20
20
|
|
|
21
21
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
22
|
-
export const XOPC_DB_SCHEMA_VERSION =
|
|
22
|
+
export const XOPC_DB_SCHEMA_VERSION = 52;
|
|
23
23
|
|
|
24
24
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
25
25
|
|
|
@@ -31,7 +31,7 @@ function ensureXopcDatabaseSchema(db) {
|
|
|
31
31
|
bootstrapFreshDatabase(db);
|
|
32
32
|
currentVersion = 11;
|
|
33
33
|
}
|
|
34
|
-
applyPendingMigrations(db, { targetVersion:
|
|
34
|
+
applyPendingMigrations(db, { targetVersion: 52 });
|
|
35
35
|
}
|
|
36
36
|
//#endregion
|
|
37
37
|
export { SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_SCHEMA_VERSION, ensureSchemaMetaTable, ensureXopcDatabaseSchema, readSchemaVersion, readSchemaVersionForTest, setSchemaVersion };
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import type { Config } from '../config/schema.js';
|
|
2
|
-
import type { WorkContextSnapshot, WorkDiscoveryResult } from './types.js';
|
|
2
|
+
import type { WorkContextSnapshot, WorkDiscoveryCandidate, WorkDiscoveryPersonalContextItem, WorkDiscoveryProfileCandidate, WorkDiscoveryResult, WorkUnderstandingThreadCandidate } from './types.js';
|
|
3
3
|
export declare function analyzeWorkContext(input: {
|
|
4
4
|
config: Config;
|
|
5
5
|
snapshot: WorkContextSnapshot;
|
|
6
|
+
candidateContext?: WorkDiscoveryCandidate[];
|
|
6
7
|
signal?: AbortSignal;
|
|
7
8
|
}): Promise<{
|
|
8
9
|
modelRef: string;
|
|
9
10
|
result: WorkDiscoveryResult;
|
|
10
11
|
}>;
|
|
12
|
+
export declare function analyzePersonalContext(input: {
|
|
13
|
+
config: Config;
|
|
14
|
+
items: WorkDiscoveryPersonalContextItem[];
|
|
15
|
+
workContext?: Pick<WorkDiscoveryResult, 'projectSummary' | 'currentState' | 'uncertainties' | 'workThreads'>;
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
}): Promise<{
|
|
18
|
+
modelRef: string;
|
|
19
|
+
profileCandidates: WorkDiscoveryProfileCandidate[];
|
|
20
|
+
workThreadCandidates: WorkUnderstandingThreadCandidate[];
|
|
21
|
+
}>;
|
|
11
22
|
export declare function workDiscoveryResultMarkdown(result: WorkDiscoveryResult): string;
|
|
@@ -6,6 +6,7 @@ import { randomUUID } from "node:crypto";
|
|
|
6
6
|
init_schema();
|
|
7
7
|
init_providers();
|
|
8
8
|
const MAX_MODEL_CONTEXT_CHARS = 12e4;
|
|
9
|
+
const MAX_PERSONAL_CONTEXT_CHARS = 1e5;
|
|
9
10
|
function extractText(content) {
|
|
10
11
|
if (typeof content === "string") return content;
|
|
11
12
|
if (!Array.isArray(content)) return "";
|
|
@@ -66,6 +67,46 @@ function validateSuggestion(value, allowedPaths) {
|
|
|
66
67
|
verification: strings(item.verification, 4).map((entry) => entry.slice(0, 300))
|
|
67
68
|
};
|
|
68
69
|
}
|
|
70
|
+
function validateProfileCandidate(value) {
|
|
71
|
+
if (!value || typeof value !== "object") return null;
|
|
72
|
+
const item = value;
|
|
73
|
+
const category = item.category === "role" || item.category === "focus" || item.category === "technology" || item.category === "workflow" || item.category === "preference" ? item.category : void 0;
|
|
74
|
+
if (!category || typeof item.statement !== "string") return null;
|
|
75
|
+
const statement = item.statement.trim().slice(0, 500);
|
|
76
|
+
if (statement.length < 4) return null;
|
|
77
|
+
const confidence = item.confidence === "high" || item.confidence === "low" ? item.confidence : "medium";
|
|
78
|
+
return {
|
|
79
|
+
id: randomUUID(),
|
|
80
|
+
category,
|
|
81
|
+
statement,
|
|
82
|
+
confidence,
|
|
83
|
+
evidence: strings(item.evidence, 4).map((entry) => entry.slice(0, 300)),
|
|
84
|
+
status: "pending"
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function validateWorkThreadCandidate(value, allowedRefs) {
|
|
88
|
+
if (!value || typeof value !== "object") return null;
|
|
89
|
+
const item = value;
|
|
90
|
+
if (typeof item.title !== "string" || typeof item.summary !== "string") return null;
|
|
91
|
+
const title = item.title.trim().slice(0, 200);
|
|
92
|
+
const summary = item.summary.trim().slice(0, 2e3);
|
|
93
|
+
if (!title || !summary) return null;
|
|
94
|
+
const horizon = item.horizon === "current" || item.horizon === "ongoing" || item.horizon === "long_term" ? item.horizon : "current";
|
|
95
|
+
const status = item.status === "active" || item.status === "paused" || item.status === "blocked" || item.status === "completed" || item.status === "uncertain" ? item.status : "uncertain";
|
|
96
|
+
const confidence = item.confidence === "high" || item.confidence === "low" ? item.confidence : "medium";
|
|
97
|
+
const topicKey = (typeof item.topicKey === "string" ? item.topicKey.toLowerCase() : title.toLowerCase()).replace(/[^a-z0-9\p{L}]+/gu, "-").replace(/^-+|-+$/g, "").slice(0, 100) || `thread-${randomUUID().slice(0, 8)}`;
|
|
98
|
+
const evidenceRefs = strings(item.evidenceRefs, 12).filter((ref) => allowedRefs.has(ref));
|
|
99
|
+
if (!evidenceRefs.length) return null;
|
|
100
|
+
return {
|
|
101
|
+
topicKey,
|
|
102
|
+
title,
|
|
103
|
+
summary,
|
|
104
|
+
horizon,
|
|
105
|
+
status,
|
|
106
|
+
confidence,
|
|
107
|
+
evidenceRefs
|
|
108
|
+
};
|
|
109
|
+
}
|
|
69
110
|
function suggestionScore(suggestion) {
|
|
70
111
|
const confidence = suggestion.confidence === "high" ? 3 : suggestion.confidence === "medium" ? 2 : 1;
|
|
71
112
|
const safety = suggestion.risk === "analysis" ? 3 : suggestion.risk === "command" ? 2 : 1;
|
|
@@ -116,7 +157,14 @@ async function analyzeWorkContext(input) {
|
|
|
116
157
|
content: [
|
|
117
158
|
"You help a user resume real work in one explicitly selected local folder.",
|
|
118
159
|
"Analyze only the supplied bounded snapshot. Never claim that you ran commands, tests, or inspected anything absent from it.",
|
|
119
|
-
"Return only one JSON object with projectSummary, currentState, uncertainties, suggestions, lowConfidence, and contextQuestion.",
|
|
160
|
+
"Return only one JSON object with projectSummary, currentState, uncertainties, suggestions, profileCandidates, workThreads, lowConfidence, and contextQuestion.",
|
|
161
|
+
"profileCandidates contains up to 6 stable, useful facts about the user inferred from the supplied project evidence.",
|
|
162
|
+
"Each profile candidate has category (role, focus, technology, workflow, or preference), statement, confidence, and evidence.",
|
|
163
|
+
"Do not infer sensitive traits, identity, health, finances, political views, or anything not directly supported by the work evidence.",
|
|
164
|
+
"workThreads contains up to 3 evidence-backed work streams with topicKey, title, summary, horizon, status, confidence, and evidenceRefs.",
|
|
165
|
+
"horizon is current, ongoing, or long_term. status is active, paused, blocked, completed, or uncertain.",
|
|
166
|
+
"topicKey is a short stable topic identifier. evidenceRefs must use exact relative paths from the snapshot or git://recent-state.",
|
|
167
|
+
"Distinguish one-off recent edits from work sustained across multiple days. Prefer one current thread and only add ongoing or long_term threads when evidence supports them.",
|
|
120
168
|
"For a normal result, suggestions must contain exactly 3 materially different next steps.",
|
|
121
169
|
"Each suggestion must include actionType, title, rationale, evidence, actionPrompt, confidence, expectedOutcome, estimatedMinutes, risk, and verification.",
|
|
122
170
|
"actionType must be summarize_recent_work, inspect_related_tests, or plan_next_step.",
|
|
@@ -127,6 +175,15 @@ async function analyzeWorkContext(input) {
|
|
|
127
175
|
"If the current objective is unclear or fewer than three credible suggestions exist, set lowConfidence=true, return suggestions=[], and ask one concise contextQuestion.",
|
|
128
176
|
"Use Simplified Chinese for every user-facing string when the visible snapshot documents are mainly Chinese; otherwise use English.",
|
|
129
177
|
"",
|
|
178
|
+
input.candidateContext?.length ? `Other ranked local work projects (metadata only):\n${JSON.stringify(input.candidateContext.slice(0, 8).map((candidate) => ({
|
|
179
|
+
displayName: candidate.displayName,
|
|
180
|
+
projectKind: candidate.projectKind,
|
|
181
|
+
score: candidate.score,
|
|
182
|
+
lastActiveAt: candidate.lastActiveAt,
|
|
183
|
+
branch: candidate.branch,
|
|
184
|
+
changedFileCount: candidate.changedFileCount,
|
|
185
|
+
evidence: candidate.evidence
|
|
186
|
+
})))}` : "",
|
|
130
187
|
JSON.stringify(compactSnapshot)
|
|
131
188
|
].join("\n"),
|
|
132
189
|
timestamp: Date.now()
|
|
@@ -140,17 +197,16 @@ async function analyzeWorkContext(input) {
|
|
|
140
197
|
modelRef,
|
|
141
198
|
result: lowConfidenceResult(input.snapshot, typeof parsed.contextQuestion === "string" ? parsed.contextQuestion.trim().slice(0, 500) : void 0)
|
|
142
199
|
};
|
|
143
|
-
const allowedPaths = new Set([
|
|
144
|
-
|
|
145
|
-
...input.snapshot.documents.map((document) => document.relativePath),
|
|
146
|
-
...input.snapshot.git?.changedPaths ?? []
|
|
147
|
-
]);
|
|
200
|
+
const allowedPaths = new Set([...input.snapshot.documents.map((document) => document.relativePath), ...input.snapshot.git?.changedPaths ?? []]);
|
|
201
|
+
const allowedThreadRefs = new Set([...allowedPaths, "git://recent-state"]);
|
|
148
202
|
const suggestions = Array.isArray(parsed.suggestions) ? parsed.suggestions.map((value) => validateSuggestion(value, allowedPaths)).filter((value) => Boolean(value)) : [];
|
|
149
203
|
if (suggestions.length !== 3) return {
|
|
150
204
|
modelRef,
|
|
151
205
|
result: lowConfidenceResult(input.snapshot)
|
|
152
206
|
};
|
|
153
207
|
const primarySuggestion = [...suggestions].sort((a, b) => suggestionScore(b) - suggestionScore(a))[0];
|
|
208
|
+
const profileCandidates = input.candidateContext?.length && Array.isArray(parsed.profileCandidates) ? parsed.profileCandidates.map(validateProfileCandidate).filter((value) => Boolean(value)).slice(0, 6) : [];
|
|
209
|
+
const workThreadCandidates = Array.isArray(parsed.workThreads) ? parsed.workThreads.map((value) => validateWorkThreadCandidate(value, allowedThreadRefs)).filter((value) => Boolean(value)).slice(0, 3) : [];
|
|
154
210
|
const projectSummary = typeof parsed.projectSummary === "string" ? parsed.projectSummary.trim() : "";
|
|
155
211
|
const currentState = typeof parsed.currentState === "string" ? parsed.currentState.trim() : "";
|
|
156
212
|
if (!projectSummary || !currentState) throw new Error("Analysis result is missing its summary");
|
|
@@ -161,10 +217,73 @@ async function analyzeWorkContext(input) {
|
|
|
161
217
|
currentState: currentState.slice(0, 1200),
|
|
162
218
|
uncertainties: strings(parsed.uncertainties, 6).map((value) => value.slice(0, 500)),
|
|
163
219
|
suggestions,
|
|
220
|
+
...profileCandidates.length > 0 ? { profileCandidates } : {},
|
|
221
|
+
...workThreadCandidates.length > 0 ? { workThreadCandidates } : {},
|
|
164
222
|
...primarySuggestion ? { primarySuggestionId: primarySuggestion.id } : {}
|
|
165
223
|
}
|
|
166
224
|
};
|
|
167
225
|
}
|
|
226
|
+
async function analyzePersonalContext(input) {
|
|
227
|
+
const modelRef = getAgentDefaultModelRef(input.config);
|
|
228
|
+
if (!modelRef) throw new Error("No default model configured");
|
|
229
|
+
let remaining = MAX_PERSONAL_CONTEXT_CHARS;
|
|
230
|
+
const items = input.items.slice(0, 150).flatMap((item, index) => {
|
|
231
|
+
if (remaining <= 0) return [];
|
|
232
|
+
const content = item.content.slice(0, remaining);
|
|
233
|
+
remaining -= content.length;
|
|
234
|
+
return [{
|
|
235
|
+
ref: `${item.source}:${index + 1}`,
|
|
236
|
+
source: item.source,
|
|
237
|
+
title: item.title,
|
|
238
|
+
group: item.group,
|
|
239
|
+
createdAt: item.createdAt,
|
|
240
|
+
modifiedAt: item.modifiedAt,
|
|
241
|
+
startsAt: item.startsAt,
|
|
242
|
+
endsAt: item.endsAt,
|
|
243
|
+
content
|
|
244
|
+
}];
|
|
245
|
+
});
|
|
246
|
+
if (!items.some((item) => item.title.trim() || item.content.trim())) return {
|
|
247
|
+
modelRef,
|
|
248
|
+
profileCandidates: [],
|
|
249
|
+
workThreadCandidates: []
|
|
250
|
+
};
|
|
251
|
+
const prompt = [
|
|
252
|
+
"Analyze the bounded personal work context that the user explicitly chose to connect from Apple Notes, Calendar, and Reminders.",
|
|
253
|
+
"Return only one JSON object with profileCandidates and workThreads.",
|
|
254
|
+
"profileCandidates contains at most 6 stable, useful work-related facts.",
|
|
255
|
+
"Each item has category (role, focus, technology, workflow, or preference), statement, confidence, and evidence.",
|
|
256
|
+
"workThreads contains at most 3 evidence-backed current, ongoing, or long-term work streams.",
|
|
257
|
+
"Each work thread has topicKey, title, summary, status, horizon, confidence, and evidenceRefs.",
|
|
258
|
+
"evidenceRefs must contain only the supplied source:index refs. Do not create a thread without direct support.",
|
|
259
|
+
"Prefer recurring work themes and durable preferences. Do not turn one-off errands or stale notes into user facts.",
|
|
260
|
+
"Do not infer sensitive identity, health, finances, political views, relationships, passwords, credentials, or private contact details.",
|
|
261
|
+
"Evidence must be a short paraphrase and must not quote private note content verbatim.",
|
|
262
|
+
"Calendar and reminder titles may establish commitments and timing, but must not be treated as stable preferences without repeated evidence.",
|
|
263
|
+
"When a work-directory summary is supplied, reconcile it with the personal sources: identify corroboration, conflicts, and whether an apparent focus is current or merely stale.",
|
|
264
|
+
"Do not repeat the directory summary as a personal fact unless at least one supplied personal source supports it.",
|
|
265
|
+
"Use Simplified Chinese when the supplied items are mainly Chinese; otherwise use English.",
|
|
266
|
+
input.workContext ? `Work-directory understanding from the same one-shot investigation:\n${JSON.stringify(input.workContext)}` : "",
|
|
267
|
+
JSON.stringify(items)
|
|
268
|
+
].join("\n");
|
|
269
|
+
const parsed = parseJson(extractText((await completeWithResolvedCredentials(resolveModel(modelRef), { messages: [{
|
|
270
|
+
role: "user",
|
|
271
|
+
content: prompt,
|
|
272
|
+
timestamp: Date.now()
|
|
273
|
+
}] }, {
|
|
274
|
+
maxTokens: 1500,
|
|
275
|
+
temperature: .1,
|
|
276
|
+
signal: input.signal
|
|
277
|
+
})).content));
|
|
278
|
+
if (!parsed) throw new Error("Personal context analysis did not return valid JSON");
|
|
279
|
+
const profileCandidates = Array.isArray(parsed.profileCandidates) ? parsed.profileCandidates.map(validateProfileCandidate).filter((value) => Boolean(value)).slice(0, 6) : [];
|
|
280
|
+
const allowedRefs = new Set(items.map((item) => item.ref));
|
|
281
|
+
return {
|
|
282
|
+
modelRef,
|
|
283
|
+
profileCandidates,
|
|
284
|
+
workThreadCandidates: Array.isArray(parsed.workThreads) ? parsed.workThreads.map((value) => validateWorkThreadCandidate(value, allowedRefs)).filter((value) => Boolean(value)).slice(0, 3) : []
|
|
285
|
+
};
|
|
286
|
+
}
|
|
168
287
|
function workDiscoveryResultMarkdown(result) {
|
|
169
288
|
const lines = [
|
|
170
289
|
`## What I found`,
|
|
@@ -185,4 +304,4 @@ function workDiscoveryResultMarkdown(result) {
|
|
|
185
304
|
return lines.join("\n");
|
|
186
305
|
}
|
|
187
306
|
//#endregion
|
|
188
|
-
export { analyzeWorkContext, workDiscoveryResultMarkdown };
|
|
307
|
+
export { analyzePersonalContext, analyzeWorkContext, workDiscoveryResultMarkdown };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { WorkDiscoveryCandidate } from './types.js';
|
|
2
|
+
export interface DiscoverWorkCandidatesOptions {
|
|
3
|
+
existingProjects?: Array<{
|
|
4
|
+
id: string;
|
|
5
|
+
workspaceRoot?: string;
|
|
6
|
+
}>;
|
|
7
|
+
approvedDirectories?: Array<{
|
|
8
|
+
id: string;
|
|
9
|
+
rootPath: string;
|
|
10
|
+
}>;
|
|
11
|
+
homeDirectory?: string;
|
|
12
|
+
nowMs?: number;
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
platform?: NodeJS.Platform;
|
|
15
|
+
environment?: NodeJS.ProcessEnv;
|
|
16
|
+
}
|
|
17
|
+
export declare function discoverWorkCandidates(options?: DiscoverWorkCandidatesOptions): Promise<WorkDiscoveryCandidate[]>;
|