@xopcai/xopc 0.0.159 → 0.0.160
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-CY3sizqC.js} +1 -1
- package/dist/gateway/static/root/assets/{activity-store-C-57rvkX.js → activity-store-3Cso_2qm.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BeVf03hx.js → agent-avatar-dicebear-B_27aaZQ.js} +1 -1
- package/dist/gateway/static/root/assets/agents-B1l3iii6.js +2 -0
- package/dist/gateway/static/root/assets/{api-BjcBIV2O.js → api-BbzB57CJ.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-BG2ulMYw.js → app-management-settings-panel-DfmyWcyh.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-0Kjenr2T.js → appearance-settings-B2v0c-K-.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-DaNFLsp0.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-ChiNnH1e.js → archive-plugin-DL5Lnx0C.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-pbBlQR-g.js → attachment-load-InuP-ZjQ.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-CTZDOyDy.js → automation-suggestion-card-CahGuh7B.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-Cd0gMx6p.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-BeZGzpGF.js → binary-plugins-CdOTGYVu.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-BnDmhN5Z.js → block-editor-D6ShK1S-.js} +3 -3
- package/dist/gateway/static/root/assets/browser-settings-page-BmUYDL2Z.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-Ii36j_7v.js → capability-presets-api-BzoLm52Z.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-settings-panel-Cl3dw4yh.js → capability-presets-settings-panel-CE7mrWtS.js} +2 -2
- package/dist/gateway/static/root/assets/{channel-recipient-api-CFqrSi5K.js → channel-recipient-api-DbgydMjl.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-D1_gcAHK.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-Cqfqv59m.js +1 -0
- package/dist/gateway/static/root/assets/{connector-logo-CiLxuZ8S.js → connector-logo-C3bJC9nt.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-CaLBi6Na.js +2 -0
- package/dist/gateway/static/root/assets/desktop-pet-settings-BrHVvbBw.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-sprite-CFd1jGLJ.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-wacrnJJ1.js +1 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-CC2VL5Mc.js → directory-picker-path-field-BPI969w-.js} +1 -1
- package/dist/gateway/static/root/assets/{dreaming-settings-KY7ZfVsy.js → dreaming-settings-iw6QXD45.js} +3 -3
- package/dist/gateway/static/root/assets/{extension-debug-page-C8KBeucs.js → extension-debug-page-rCHyhEr6.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CV-5Kcyk.js → extension-page-C4gQsAPt.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DvUtDrAu.js → extension-settings-page-DB_3eik7.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-DXfVeJLc.js → gateway-config-api-Cdt38iF2.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-DM4BgyGw.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-cxQ94qMm.js → gateway-startup-retry-ChRFNjj_.js} +1 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-BQamcEmF.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-DZUQ0Lf1.js +4 -0
- package/dist/gateway/static/root/assets/{goals-config-api-P8VhLqYF.js → goals-config-api-RvljeEm6.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-BtxVD27C.js +2 -0
- package/dist/gateway/static/root/assets/{goals-settings-DHXyaH_O.js → goals-settings-DMGV4S4L.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-CNckP_Dv.js → heartbeat-settings-B8r5EXc5.js} +1 -1
- package/dist/gateway/static/root/assets/index-CHKe7nwC.js +197 -0
- package/dist/gateway/static/root/assets/index-CXhfUIjh.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-jXmv1RVX.js → keyboard-shortcuts-settings-1-LYibQ4.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-CTTfyFe3.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-Cs5LKjtc.js → local-apps-page-CC4vtUUI.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-C_M71MK9.js → locale-store-CPbGB_6l.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-Bn_taxBV.js +7 -0
- package/dist/gateway/static/root/assets/{markdown-split-BFqeSNte.js → markdown-split-CprEIf8A.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-Cc8379gq.js → media-plugins-BXt_Sj2O.js} +1 -1
- package/dist/gateway/static/root/assets/{messages-BuT0SR1R.js → messages-DDsiqdJS.js} +4 -4
- package/dist/gateway/static/root/assets/models-hub-panel-DJReXRP9.js +2 -0
- package/dist/gateway/static/root/assets/{notes-page-DlGU4ipo.js → notes-page-Wf5-eyyM.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-BbdjzYzs.js +4 -0
- package/dist/gateway/static/root/assets/{page-tabs-kFOswGlG.js → page-tabs-dtpK_4Fs.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-CvjuvABg.js → preview-open-alternatives-DijtEVxG.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-BwG8SOzI.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-nUlyUtxh.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-rf7bjije.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-DwrD7ODx.js → save-bar-controls-qhDisdqY.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-Ba4WzTmE.js → schedule-field-AhJ50NoT.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-DvydL8eF.js → schema-form-AuJg2CXU.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-CSoHUe38.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-Db976IEa.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-LaQqGGej.js → settings-collapsible-section-D5ganr_X.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-DruI__qO.js → settings-form-section-Db3cKRsg.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-UvA-eDhF.js → settings-loading-skeleton-D05YKT2x.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-5oCGSebm.js → settings-page-DYfXmfTx.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-DG-XMIPu.js → settings-page-layout-Cv_Cu4I5.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-wBWOFpvw.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-FLVeTrRt.js → share-preview-page-CYkZVuse.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-BAMma1-P.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-TWxFFWxM.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-Ca2RA29R.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-CeK93xVi.js → system-settings-panel-C1Ng5bXh.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-C8ohIHhw.js → theme-store-LMHIDR9n.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-D1Lr2gG0.js → typed-models-lib-Ch2Bdm_5.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-BWzou5ar.js → use-channel-catalog-CZnaMOeL.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-CTumZa0y.js +1 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-DdlMxeqO.js +1 -0
- package/dist/gateway/static/root/assets/work-discovery-page-CcfZhdBi.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page--wxLw1p_.js +1 -0
- package/dist/gateway/static/root/assets/work-page-DyS0XBpV.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Bynqe4kV.js → workflow-run-setup-panel-BFk8TvTw.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-CVVJd4gy.js +11 -0
- package/dist/gateway/static/root/index.html +7 -7
- 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/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/service.d.ts +2 -0
- package/dist/src/gateway/service.js +27 -2
- 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/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/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/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,106 @@
|
|
|
1
|
+
import { requireXopcDatabase } from "../storage/sqlite/connection.js";
|
|
2
|
+
import { runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
|
|
3
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
+
//#region src/proactive/insight-repository.ts
|
|
5
|
+
function insightFromRow(row) {
|
|
6
|
+
return {
|
|
7
|
+
id: row.insight_id,
|
|
8
|
+
watchId: row.watch_id,
|
|
9
|
+
runId: row.run_id,
|
|
10
|
+
kind: row.kind,
|
|
11
|
+
title: row.title,
|
|
12
|
+
summary: row.summary,
|
|
13
|
+
whyItMatters: row.why_it_matters,
|
|
14
|
+
nextAction: row.next_action,
|
|
15
|
+
evidence: JSON.parse(row.evidence_json),
|
|
16
|
+
status: row.status,
|
|
17
|
+
createdAt: row.created_at,
|
|
18
|
+
updatedAt: row.updated_at
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function getProactiveInsight(id) {
|
|
22
|
+
const { db } = requireXopcDatabase();
|
|
23
|
+
const row = db.prepare("SELECT * FROM proactive_insights WHERE insight_id = ?").get(id);
|
|
24
|
+
return row ? insightFromRow(row) : null;
|
|
25
|
+
}
|
|
26
|
+
function listProactiveInsights(options = {}) {
|
|
27
|
+
const statuses = options.status ?? ["unread", "read"];
|
|
28
|
+
if (statuses.length === 0) return [];
|
|
29
|
+
const limit = Math.max(1, Math.min(100, options.limit ?? 20));
|
|
30
|
+
const { db } = requireXopcDatabase();
|
|
31
|
+
return db.prepare(`SELECT * FROM proactive_insights
|
|
32
|
+
WHERE status IN (${statuses.map(() => "?").join(", ")})
|
|
33
|
+
ORDER BY created_at DESC LIMIT ?`).all(...statuses, limit).map(insightFromRow);
|
|
34
|
+
}
|
|
35
|
+
function createProactiveInsight(input) {
|
|
36
|
+
const id = randomUUID();
|
|
37
|
+
const now = input.nowMs ?? Date.now();
|
|
38
|
+
const intelligenceUpdates = input.evidence.map((item) => [
|
|
39
|
+
canonicalSource(item.source),
|
|
40
|
+
canonicalPublishedAt(item.publishedAt),
|
|
41
|
+
normalizeIdentityText(item.label)
|
|
42
|
+
]).filter(([source]) => Boolean(source)).toSorted((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
|
|
43
|
+
const fingerprint = input.kind === "intelligence" && intelligenceUpdates.length > 0 ? JSON.stringify({
|
|
44
|
+
kind: input.kind,
|
|
45
|
+
updates: intelligenceUpdates
|
|
46
|
+
}) : JSON.stringify({
|
|
47
|
+
kind: input.kind,
|
|
48
|
+
title: input.title.toLocaleLowerCase().trim(),
|
|
49
|
+
summary: input.summary.toLocaleLowerCase().trim(),
|
|
50
|
+
evidence: input.evidence.map((item) => [
|
|
51
|
+
item.label.toLocaleLowerCase().trim(),
|
|
52
|
+
canonicalSource(item.source),
|
|
53
|
+
item.publishedAt ?? ""
|
|
54
|
+
])
|
|
55
|
+
});
|
|
56
|
+
const contentHash = createHash("sha256").update(fingerprint).digest("hex");
|
|
57
|
+
let created = false;
|
|
58
|
+
runSqliteWriteTransaction((db) => {
|
|
59
|
+
created = db.prepare(`INSERT OR IGNORE INTO proactive_insights (
|
|
60
|
+
insight_id, watch_id, run_id, kind, title, summary, why_it_matters,
|
|
61
|
+
next_action, evidence_json, content_hash, status, created_at, updated_at
|
|
62
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'unread', ?, ?)`).run(id, input.watchId, input.runId, input.kind, input.title, input.summary, input.whyItMatters, input.nextAction, JSON.stringify(input.evidence), contentHash, now, now).changes > 0;
|
|
63
|
+
});
|
|
64
|
+
return created ? listProactiveInsights({ limit: 100 }).find((item) => item.id === id) ?? null : null;
|
|
65
|
+
}
|
|
66
|
+
function canonicalPublishedAt(value) {
|
|
67
|
+
if (!value) return "";
|
|
68
|
+
const timestamp = Date.parse(value);
|
|
69
|
+
return Number.isFinite(timestamp) ? new Date(timestamp).toISOString() : value.trim();
|
|
70
|
+
}
|
|
71
|
+
function normalizeIdentityText(value) {
|
|
72
|
+
return value.trim().toLocaleLowerCase().replace(/\s+/g, " ");
|
|
73
|
+
}
|
|
74
|
+
function canonicalSource(source) {
|
|
75
|
+
const value = source?.trim();
|
|
76
|
+
if (!value) return "";
|
|
77
|
+
try {
|
|
78
|
+
const url = new URL(value);
|
|
79
|
+
url.hash = "";
|
|
80
|
+
for (const key of [...url.searchParams.keys()]) if (key.toLowerCase().startsWith("utm_") || key === "ref" || key === "source") url.searchParams.delete(key);
|
|
81
|
+
url.pathname = url.pathname.replace(/\/$/, "");
|
|
82
|
+
return url.toString().toLocaleLowerCase();
|
|
83
|
+
} catch {
|
|
84
|
+
return value.toLocaleLowerCase();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function setProactiveInsightStatus(id, status, nowMs = Date.now(), expectedStatus) {
|
|
88
|
+
let changed = false;
|
|
89
|
+
runSqliteWriteTransaction((db) => {
|
|
90
|
+
changed = db.prepare(`UPDATE proactive_insights SET status = ?, updated_at = ? WHERE insight_id = ?
|
|
91
|
+
${expectedStatus ? "AND status = ?" : ""}`).run(status, nowMs, id, ...expectedStatus ? [expectedStatus] : []).changes > 0;
|
|
92
|
+
});
|
|
93
|
+
if (!changed) return null;
|
|
94
|
+
const { db } = requireXopcDatabase();
|
|
95
|
+
return insightFromRow(db.prepare("SELECT * FROM proactive_insights WHERE insight_id = ?").get(id));
|
|
96
|
+
}
|
|
97
|
+
function claimProactiveInsightApproval(id, nowMs = Date.now()) {
|
|
98
|
+
let claimed = false;
|
|
99
|
+
runSqliteWriteTransaction((db) => {
|
|
100
|
+
claimed = db.prepare(`UPDATE proactive_insights SET status = 'approved', updated_at = ?
|
|
101
|
+
WHERE insight_id = ? AND status = 'unread'`).run(nowMs, id).changes > 0;
|
|
102
|
+
});
|
|
103
|
+
return claimed ? getProactiveInsight(id) : null;
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
export { claimProactiveInsightApproval, createProactiveInsight, getProactiveInsight, listProactiveInsights, setProactiveInsightStatus };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AutomationRun } from '../automations/index.js';
|
|
2
|
+
import type { ProactiveEvidence, ProactiveInsight } from './types.js';
|
|
3
|
+
interface MeaningfulResult {
|
|
4
|
+
meaningful: true;
|
|
5
|
+
title: string;
|
|
6
|
+
summary: string;
|
|
7
|
+
whyItMatters: string;
|
|
8
|
+
nextAction: string;
|
|
9
|
+
evidence: ProactiveEvidence[];
|
|
10
|
+
}
|
|
11
|
+
export declare function parseFocusRunResult(summary: string): MeaningfulResult | {
|
|
12
|
+
meaningful: false;
|
|
13
|
+
} | null;
|
|
14
|
+
export declare function hasValidIntelligenceEvidence(evidence: ProactiveEvidence[]): boolean;
|
|
15
|
+
export declare function processFocusAutomationRun(run: AutomationRun): {
|
|
16
|
+
handled: boolean;
|
|
17
|
+
insight?: ProactiveInsight;
|
|
18
|
+
};
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { getFocusWatchByAutomationId, recordFocusWatchRun } from "./watch-repository.js";
|
|
4
|
+
import { createProactiveInsight } from "./insight-repository.js";
|
|
5
|
+
//#region src/proactive/run-processor.ts
|
|
6
|
+
init_logger();
|
|
7
|
+
const log = createLogger("ProactiveRunProcessor");
|
|
8
|
+
function nonEmptyString(value) {
|
|
9
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
10
|
+
}
|
|
11
|
+
function parseFocusRunResult(summary) {
|
|
12
|
+
const start = summary.indexOf("{");
|
|
13
|
+
const end = summary.lastIndexOf("}");
|
|
14
|
+
if (start < 0 || end <= start) return null;
|
|
15
|
+
let value;
|
|
16
|
+
try {
|
|
17
|
+
value = JSON.parse(summary.slice(start, end + 1));
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
22
|
+
const result = value;
|
|
23
|
+
if (result.meaningful === false) return { meaningful: false };
|
|
24
|
+
if (result.meaningful !== true) return null;
|
|
25
|
+
const title = nonEmptyString(result.title);
|
|
26
|
+
const resultSummary = nonEmptyString(result.summary);
|
|
27
|
+
const whyItMatters = nonEmptyString(result.whyItMatters);
|
|
28
|
+
const nextAction = nonEmptyString(result.nextAction);
|
|
29
|
+
const evidence = Array.isArray(result.evidence) ? result.evidence.flatMap((item) => {
|
|
30
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return [];
|
|
31
|
+
const label = nonEmptyString(item.label);
|
|
32
|
+
const source = nonEmptyString(item.source);
|
|
33
|
+
const publishedAtValue = nonEmptyString(item.publishedAt);
|
|
34
|
+
const publishedAt = publishedAtValue && Number.isFinite(Date.parse(publishedAtValue)) ? new Date(publishedAtValue).toISOString() : null;
|
|
35
|
+
return label ? [{
|
|
36
|
+
label,
|
|
37
|
+
...source ? { source } : {},
|
|
38
|
+
...publishedAt ? { publishedAt } : {}
|
|
39
|
+
}] : [];
|
|
40
|
+
}).slice(0, 8) : [];
|
|
41
|
+
if (!title || !resultSummary || !whyItMatters || !nextAction || evidence.length === 0) return null;
|
|
42
|
+
return {
|
|
43
|
+
meaningful: true,
|
|
44
|
+
title,
|
|
45
|
+
summary: resultSummary,
|
|
46
|
+
whyItMatters,
|
|
47
|
+
nextAction,
|
|
48
|
+
evidence
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function hasValidIntelligenceEvidence(evidence) {
|
|
52
|
+
return evidence.some((item) => {
|
|
53
|
+
if (!item.source || !item.publishedAt) return false;
|
|
54
|
+
try {
|
|
55
|
+
const url = new URL(item.source);
|
|
56
|
+
return (url.protocol === "https:" || url.protocol === "http:") && Number.isFinite(Date.parse(item.publishedAt));
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function processFocusAutomationRun(run) {
|
|
63
|
+
const watch = getFocusWatchByAutomationId(run.automationId);
|
|
64
|
+
if (!watch) return { handled: false };
|
|
65
|
+
if (run.status !== "succeeded" || !run.summary) {
|
|
66
|
+
recordFocusWatchRun({
|
|
67
|
+
id: watch.id,
|
|
68
|
+
runId: run.id,
|
|
69
|
+
outcome: "failed",
|
|
70
|
+
nowMs: run.endedAtMs
|
|
71
|
+
});
|
|
72
|
+
return { handled: true };
|
|
73
|
+
}
|
|
74
|
+
const result = parseFocusRunResult(run.summary);
|
|
75
|
+
if (!result) {
|
|
76
|
+
recordFocusWatchRun({
|
|
77
|
+
id: watch.id,
|
|
78
|
+
runId: run.id,
|
|
79
|
+
outcome: "failed",
|
|
80
|
+
nowMs: run.endedAtMs
|
|
81
|
+
});
|
|
82
|
+
log.warn({
|
|
83
|
+
automationId: run.automationId,
|
|
84
|
+
runId: run.id
|
|
85
|
+
}, "Focus watch returned an invalid result");
|
|
86
|
+
return { handled: true };
|
|
87
|
+
}
|
|
88
|
+
if (result.meaningful && watch.kind === "intelligence" && !hasValidIntelligenceEvidence(result.evidence)) {
|
|
89
|
+
recordFocusWatchRun({
|
|
90
|
+
id: watch.id,
|
|
91
|
+
runId: run.id,
|
|
92
|
+
outcome: "failed",
|
|
93
|
+
nowMs: run.endedAtMs
|
|
94
|
+
});
|
|
95
|
+
log.warn({
|
|
96
|
+
automationId: run.automationId,
|
|
97
|
+
runId: run.id
|
|
98
|
+
}, "Intelligence watch result lacked dated web evidence");
|
|
99
|
+
return { handled: true };
|
|
100
|
+
}
|
|
101
|
+
if (!recordFocusWatchRun({
|
|
102
|
+
id: watch.id,
|
|
103
|
+
runId: run.id,
|
|
104
|
+
outcome: result.meaningful ? "meaningful" : "empty",
|
|
105
|
+
nowMs: run.endedAtMs
|
|
106
|
+
})) return { handled: true };
|
|
107
|
+
if (!result.meaningful) return { handled: true };
|
|
108
|
+
const insight = createProactiveInsight({
|
|
109
|
+
watchId: watch.id,
|
|
110
|
+
runId: run.id,
|
|
111
|
+
kind: watch.kind,
|
|
112
|
+
title: result.title,
|
|
113
|
+
summary: result.summary,
|
|
114
|
+
whyItMatters: result.whyItMatters,
|
|
115
|
+
nextAction: result.nextAction,
|
|
116
|
+
evidence: result.evidence,
|
|
117
|
+
nowMs: run.endedAtMs
|
|
118
|
+
});
|
|
119
|
+
return insight ? {
|
|
120
|
+
handled: true,
|
|
121
|
+
insight
|
|
122
|
+
} : { handled: true };
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
export { hasValidIntelligenceEvidence, parseFocusRunResult, processFocusAutomationRun };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
//#region src/proactive/trial-expiry-reconciler.ts
|
|
4
|
+
init_logger();
|
|
5
|
+
const DEFAULT_RECONCILE_INTERVAL_MS = 6e4;
|
|
6
|
+
const log = createLogger("FocusTrialExpiry");
|
|
7
|
+
function startTrialExpiryReconciler(service, intervalMs = DEFAULT_RECONCILE_INTERVAL_MS) {
|
|
8
|
+
let stopped = false;
|
|
9
|
+
let pending = null;
|
|
10
|
+
const reconcile = () => {
|
|
11
|
+
if (stopped || pending) return;
|
|
12
|
+
pending = service.reconcileExpiredTrials().then((pausedCount) => {
|
|
13
|
+
if (pausedCount > 0) log.info({ pausedCount }, "Expired focus watch trials paused");
|
|
14
|
+
}).catch((err) => {
|
|
15
|
+
log.warn({ err }, "Focus watch trial expiry reconciliation failed");
|
|
16
|
+
}).finally(() => {
|
|
17
|
+
pending = null;
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
reconcile();
|
|
21
|
+
const timer = setInterval(reconcile, intervalMs);
|
|
22
|
+
timer.unref();
|
|
23
|
+
return async () => {
|
|
24
|
+
stopped = true;
|
|
25
|
+
clearInterval(timer);
|
|
26
|
+
await pending;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { startTrialExpiryReconciler };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { WorkUnderstandingThread } from '../work-discovery/types.js';
|
|
2
|
+
export type FocusWatchKind = 'progress' | 'staleness' | 'deadline' | 'intelligence';
|
|
3
|
+
export type FocusWatchStatus = 'active' | 'paused';
|
|
4
|
+
export interface FocusWatch {
|
|
5
|
+
id: string;
|
|
6
|
+
threadId: string;
|
|
7
|
+
goalId?: string;
|
|
8
|
+
automationId: string;
|
|
9
|
+
kind: FocusWatchKind;
|
|
10
|
+
status: FocusWatchStatus;
|
|
11
|
+
config: Record<string, unknown>;
|
|
12
|
+
trialEndsAt?: number;
|
|
13
|
+
lastCursor?: string;
|
|
14
|
+
lastRunAt?: number;
|
|
15
|
+
lastUsefulResultAt?: number;
|
|
16
|
+
consecutiveEmptyRuns: number;
|
|
17
|
+
createdAt: number;
|
|
18
|
+
updatedAt: number;
|
|
19
|
+
}
|
|
20
|
+
export interface FocusView {
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
summary: string;
|
|
24
|
+
status: WorkUnderstandingThread['status'];
|
|
25
|
+
horizon: WorkUnderstandingThread['horizon'];
|
|
26
|
+
confidence: number;
|
|
27
|
+
focusScore: number;
|
|
28
|
+
userStatus: WorkUnderstandingThread['userStatus'];
|
|
29
|
+
projectIds: string[];
|
|
30
|
+
goalId?: string;
|
|
31
|
+
nextAction?: string;
|
|
32
|
+
blockedReason?: string;
|
|
33
|
+
watches: FocusWatch[];
|
|
34
|
+
lastObservedAt: number;
|
|
35
|
+
}
|
|
36
|
+
export type ProactiveInsightStatus = 'unread' | 'read' | 'approved' | 'dismissed';
|
|
37
|
+
export interface ProactiveEvidence {
|
|
38
|
+
label: string;
|
|
39
|
+
source?: string;
|
|
40
|
+
publishedAt?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface ProactiveInsight {
|
|
43
|
+
id: string;
|
|
44
|
+
watchId: string;
|
|
45
|
+
runId: string;
|
|
46
|
+
kind: FocusWatchKind;
|
|
47
|
+
title: string;
|
|
48
|
+
summary: string;
|
|
49
|
+
whyItMatters: string;
|
|
50
|
+
nextAction: string;
|
|
51
|
+
evidence: ProactiveEvidence[];
|
|
52
|
+
status: ProactiveInsightStatus;
|
|
53
|
+
createdAt: number;
|
|
54
|
+
updatedAt: number;
|
|
55
|
+
}
|
|
56
|
+
export interface FocusCalendarSignal {
|
|
57
|
+
id: string;
|
|
58
|
+
focusId: string;
|
|
59
|
+
focusTitle: string;
|
|
60
|
+
title: string;
|
|
61
|
+
startsAt: number;
|
|
62
|
+
endsAt?: number;
|
|
63
|
+
sourceInstanceId: string;
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FocusWatch, FocusWatchKind, FocusWatchStatus } from './types.js';
|
|
2
|
+
export declare function listFocusWatches(options?: {
|
|
3
|
+
threadId?: string;
|
|
4
|
+
status?: FocusWatchStatus;
|
|
5
|
+
}): FocusWatch[];
|
|
6
|
+
export declare function getFocusWatchByThreadAndKind(threadId: string, kind: FocusWatchKind): FocusWatch | null;
|
|
7
|
+
export declare function getFocusWatchByAutomationId(automationId: string): FocusWatch | null;
|
|
8
|
+
export declare function createFocusWatch(input: {
|
|
9
|
+
threadId: string;
|
|
10
|
+
goalId?: string;
|
|
11
|
+
automationId: string;
|
|
12
|
+
kind: FocusWatchKind;
|
|
13
|
+
config?: Record<string, unknown>;
|
|
14
|
+
trialEndsAt?: number;
|
|
15
|
+
nowMs?: number;
|
|
16
|
+
}): FocusWatch;
|
|
17
|
+
export declare function setFocusWatchStatus(id: string, status: FocusWatchStatus, nowMs?: number): FocusWatch | null;
|
|
18
|
+
export declare function restartFocusWatchTrial(id: string, trialEndsAt: number, nowMs?: number): FocusWatch | null;
|
|
19
|
+
export declare function recordFocusWatchRun(input: {
|
|
20
|
+
id: string;
|
|
21
|
+
runId: string;
|
|
22
|
+
outcome: 'meaningful' | 'empty' | 'failed';
|
|
23
|
+
nowMs?: number;
|
|
24
|
+
}): FocusWatch | null;
|
|
25
|
+
export declare function recordFocusWatchFeedback(id: string, useful: boolean, nowMs?: number): {
|
|
26
|
+
watch: FocusWatch;
|
|
27
|
+
consecutiveDismissed: number;
|
|
28
|
+
} | null;
|
|
@@ -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);
|