@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
package/dist/src/pets/factory.js
CHANGED
|
@@ -75,23 +75,26 @@ function esc(value) {
|
|
|
75
75
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
76
76
|
}
|
|
77
77
|
function desktopPetActionFrameCount(action) {
|
|
78
|
-
if (action === "idle") return 6;
|
|
79
|
-
if (action === "success" || action === "
|
|
78
|
+
if (action === "idle" || action === "sleep") return 6;
|
|
79
|
+
if (action === "success" || action === "concern") return 8;
|
|
80
80
|
return 10;
|
|
81
81
|
}
|
|
82
82
|
function desktopPetActionFps(action) {
|
|
83
|
-
if (action === "idle") return 6;
|
|
84
|
-
if (action === "success" || action === "
|
|
83
|
+
if (action === "idle" || action === "sleep") return 6;
|
|
84
|
+
if (action === "success" || action === "concern") return 12;
|
|
85
85
|
return 10;
|
|
86
86
|
}
|
|
87
87
|
function desktopPetActionLoops(action) {
|
|
88
|
-
return action
|
|
88
|
+
return action === "idle" || action === "sleep" || action === "research" || action === "read" || action === "create" || action === "execute" || action === "wait";
|
|
89
89
|
}
|
|
90
90
|
function renderAccessory(action, frame, style) {
|
|
91
91
|
const t = frame / Math.max(1, desktopPetActionFrameCount(action) - 1);
|
|
92
92
|
const bob = Math.sin(t * Math.PI * 2) * 2;
|
|
93
93
|
const { palette } = style;
|
|
94
|
-
if (action === "
|
|
94
|
+
if (action === "sleep") return `<g fill="${palette.screen}" font-family="Arial, sans-serif" font-weight="800"><text x="70" y="28" font-size="13" opacity="${frame % 2 ? .45 : .9}">z</text><text x="80" y="17" font-size="10" opacity="${frame % 2 ? .9 : .45}">z</text></g>`;
|
|
95
|
+
if (action === "greet" || action === "wake" || action === "pet") return `<path d="M76 23c-7-9-19 1-7 13l7 7 7-7c12-12 0-22-7-13z" fill="${palette.cheek}" stroke="#101827" stroke-width="2"/>`;
|
|
96
|
+
if (action === "wait") return `<g fill="${palette.screen}" transform="translate(62 18)"><circle cx="0" cy="0" r="3" opacity="${frame % 3 === 0 ? 1 : .35}"/><circle cx="10" cy="0" r="3" opacity="${frame % 3 === 1 ? 1 : .35}"/><circle cx="20" cy="0" r="3" opacity="${frame % 3 === 2 ? 1 : .35}"/></g>`;
|
|
97
|
+
if (action === "create") {
|
|
95
98
|
const glow = frame % 2 === 0 ? 1 : .45;
|
|
96
99
|
return `
|
|
97
100
|
<g transform="translate(55 ${59 + bob})">
|
|
@@ -103,7 +106,7 @@ function renderAccessory(action, frame, style) {
|
|
|
103
106
|
<rect x="29" y="28" width="5" height="2" rx="1" fill="#e0f2fe" opacity="${glow}"/>
|
|
104
107
|
</g>`;
|
|
105
108
|
}
|
|
106
|
-
if (action === "
|
|
109
|
+
if (action === "prepare") {
|
|
107
110
|
const open = frame % 2 === 0 ? -11 : -3;
|
|
108
111
|
return `
|
|
109
112
|
<g transform="translate(6 ${61 + bob})">
|
|
@@ -114,38 +117,30 @@ function renderAccessory(action, frame, style) {
|
|
|
114
117
|
<rect x="29" y="${8 + open / 2}" width="5" height="17" rx="2" fill="#e5e7eb" stroke="#101827" stroke-width="2" transform="rotate(25 31 24)"/>
|
|
115
118
|
</g>`;
|
|
116
119
|
}
|
|
117
|
-
if (action === "
|
|
120
|
+
if (action === "research") return `
|
|
118
121
|
<g transform="translate(58 ${58 + bob}) rotate(${frame % 2 === 0 ? -7 : 4} 18 18)">
|
|
119
122
|
<circle cx="15" cy="14" r="11" fill="#e0f2fe" stroke="${palette.accent}" stroke-width="5"/>
|
|
120
123
|
<rect x="24" y="24" width="17" height="7" rx="3" fill="${palette.accent}" stroke="#101827" stroke-width="3" transform="rotate(43 24 24)"/>
|
|
121
124
|
</g>`;
|
|
122
|
-
if (action === "
|
|
125
|
+
if (action === "read") return `
|
|
123
126
|
<g transform="translate(59 ${55 + bob}) rotate(${frame % 2 === 0 ? -5 : 5} 16 20)">
|
|
124
127
|
<path d="M5 1h22l9 9v31H5z" fill="#eff6ff" stroke="#101827" stroke-width="3" stroke-linejoin="round"/>
|
|
125
128
|
<path d="M27 1v10h9" fill="${palette.accent}" stroke="#101827" stroke-width="3" stroke-linejoin="round"/>
|
|
126
129
|
<rect x="12" y="20" width="16" height="3" rx="1.5" fill="${palette.body}"/>
|
|
127
130
|
<rect x="12" y="28" width="12" height="3" rx="1.5" fill="${palette.body}"/>
|
|
128
131
|
</g>`;
|
|
129
|
-
if (action === "
|
|
132
|
+
if (action === "execute") return `
|
|
130
133
|
<g transform="translate(55 ${61 + bob})">
|
|
131
134
|
<rect x="0" y="0" width="39" height="29" rx="7" fill="#111827" stroke="#101827" stroke-width="3"/>
|
|
132
135
|
<path d="M10 10l6 5-6 5" fill="none" stroke="#86efac" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" opacity="${frame % 2 === 0 ? 1 : .4}"/>
|
|
133
136
|
<path d="M20 20h9" stroke="#86efac" stroke-width="3" stroke-linecap="round"/>
|
|
134
|
-
</g>`;
|
|
135
|
-
if (action === "browser") return `
|
|
136
|
-
<g transform="translate(55 ${58 + bob})">
|
|
137
|
-
<rect x="0" y="0" width="40" height="31" rx="7" fill="#e0f2fe" stroke="#101827" stroke-width="3"/>
|
|
138
|
-
<path d="M1 11h38" stroke="${palette.accent}" stroke-width="8"/>
|
|
139
|
-
<circle cx="10" cy="10" r="2" fill="#101827"/>
|
|
140
|
-
<circle cx="17" cy="10" r="2" fill="#101827"/>
|
|
141
|
-
<rect x="11" y="21" width="20" height="4" rx="2" fill="${palette.body}"/>
|
|
142
137
|
</g>`;
|
|
143
138
|
if (action === "success") return `
|
|
144
139
|
<g fill="#facc15" transform="translate(61 ${12 + bob})">
|
|
145
140
|
<path d="M12 0l3 8 8 3-8 3-3 8-3-8-8-3 8-3z"/>
|
|
146
141
|
<path d="M-12 18l2 5 5 2-5 2-2 5-2-5-5-2 5-2z" opacity="0.78"/>
|
|
147
142
|
</g>`;
|
|
148
|
-
if (action === "
|
|
143
|
+
if (action === "concern") return `
|
|
149
144
|
<g transform="translate(61 ${56 + bob})">
|
|
150
145
|
<path d="M18 0l18 32H0z" fill="#facc15" stroke="#101827" stroke-width="3" stroke-linejoin="round"/>
|
|
151
146
|
<text x="18" y="26" text-anchor="middle" font-family="Arial, sans-serif" font-size="22" font-weight="900" fill="#101827">!</text>
|
|
@@ -170,14 +165,14 @@ function renderHead(style) {
|
|
|
170
165
|
}
|
|
171
166
|
function renderFrame(style, action, frame) {
|
|
172
167
|
const t = frame / Math.max(1, desktopPetActionFrameCount(action) - 1);
|
|
173
|
-
const bob = Math.sin(t * Math.PI * 2) * (action === "idle" ? 2.5 : 1.5);
|
|
174
|
-
const tilt = action === "
|
|
168
|
+
const bob = action === "sleep" ? 7 : action === "pickedUp" ? -6 : action === "released" ? 4 : Math.sin(t * Math.PI * 2) * (action === "idle" ? 2.5 : 1.5);
|
|
169
|
+
const tilt = action === "prepare" ? Math.sin(t * Math.PI * 2) * 2.5 : action === "pet" ? Math.sin(t * Math.PI) * 5 : 0;
|
|
175
170
|
const { palette } = style;
|
|
176
|
-
const face = action === "
|
|
177
|
-
const eye = action === "
|
|
178
|
-
const mouth = action === "success" ? "M39 45q5 5 10 0" : "M42 46h9";
|
|
179
|
-
const leftArm = action === "
|
|
180
|
-
const rightArm = action === "
|
|
171
|
+
const face = action === "concern" ? "#3f0f18" : palette.face;
|
|
172
|
+
const eye = action === "concern" ? "#fca5a5" : palette.screen;
|
|
173
|
+
const mouth = action === "success" || action === "greet" || action === "pet" ? "M39 45q5 5 10 0" : "M42 46h9";
|
|
174
|
+
const leftArm = action === "create" || action === "execute" ? 23 + frame % 2 * 13 : action === "prepare" || action === "read" ? 39 + frame % 2 * 10 : action === "pickedUp" ? 58 : 9;
|
|
175
|
+
const rightArm = action === "create" || action === "execute" ? -23 - frame % 2 * 13 : action === "prepare" || action === "read" ? -39 - frame % 2 * 10 : action === "greet" || action === "wake" ? -48 - frame % 2 * 12 : action === "pickedUp" ? -58 : -9;
|
|
181
176
|
return `
|
|
182
177
|
<g transform="translate(0 ${bob}) rotate(${tilt} 48 76)">
|
|
183
178
|
${renderHead(style)}
|
|
@@ -287,6 +282,7 @@ async function createDesktopPetPackage(input) {
|
|
|
287
282
|
await mkdir(targetRoot, { recursive: true });
|
|
288
283
|
const { id, dir } = await resolvePackageDir(targetRoot, (input.id?.trim() ? normalizePackageId(input.id) : null) || slugify(input.name?.trim() || titleFromPrompt(prompt), `pet-${hashString(prompt).toString(36)}`), input.overwrite === true);
|
|
289
284
|
const existingManifest = input.overwrite === true ? await readExistingManifest(dir) : null;
|
|
285
|
+
if (existingManifest && existingManifest.schemaVersion !== 2) throw new Error(`Existing pet package must use schemaVersion 2`);
|
|
290
286
|
const existingName = typeof existingManifest?.name === "string" ? existingManifest.name.trim() : "";
|
|
291
287
|
const persona = normalizePersona(input.persona ?? existingManifest?.persona);
|
|
292
288
|
const baseName = clampText(input.name?.trim() || existingName || titleFromPrompt(prompt), 36);
|
|
@@ -296,6 +292,7 @@ async function createDesktopPetPackage(input) {
|
|
|
296
292
|
const thumbnailPath = join(tempDir, "thumbnail.svg");
|
|
297
293
|
const manifestPath = join(tempDir, "manifest.json");
|
|
298
294
|
const manifest = {
|
|
295
|
+
schemaVersion: 2,
|
|
299
296
|
id,
|
|
300
297
|
name: baseName,
|
|
301
298
|
description,
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const DESKTOP_PET_SCHEMA_VERSION: 2;
|
|
2
|
+
export declare const DESKTOP_PET_ACTIONS: readonly ["idle", "sleep", "wake", "greet", "prepare", "research", "read", "create", "execute", "wait", "success", "concern", "pet", "pickedUp", "released"];
|
|
2
3
|
export type DesktopPetPackageAction = (typeof DESKTOP_PET_ACTIONS)[number];
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
//#region src/pets/manifest.ts
|
|
2
|
+
const DESKTOP_PET_SCHEMA_VERSION = 2;
|
|
2
3
|
const DESKTOP_PET_ACTIONS = [
|
|
3
4
|
"idle",
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
5
|
+
"sleep",
|
|
6
|
+
"wake",
|
|
7
|
+
"greet",
|
|
8
|
+
"prepare",
|
|
9
|
+
"research",
|
|
10
|
+
"read",
|
|
11
|
+
"create",
|
|
12
|
+
"execute",
|
|
13
|
+
"wait",
|
|
10
14
|
"success",
|
|
11
|
-
"
|
|
15
|
+
"concern",
|
|
16
|
+
"pet",
|
|
17
|
+
"pickedUp",
|
|
18
|
+
"released"
|
|
12
19
|
];
|
|
13
20
|
//#endregion
|
|
14
|
-
export { DESKTOP_PET_ACTIONS };
|
|
21
|
+
export { DESKTOP_PET_ACTIONS, DESKTOP_PET_SCHEMA_VERSION };
|
|
@@ -135,6 +135,7 @@ async function validateDesktopPetPackage(dir) {
|
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
137
|
if (typeof manifest.id !== "string" || !manifest.id.trim()) details.push("id is required");
|
|
138
|
+
if (manifest.schemaVersion !== 2) details.push(`schemaVersion must be 2`);
|
|
138
139
|
if (typeof manifest.name !== "string" || !manifest.name.trim()) details.push("name is required");
|
|
139
140
|
if (typeof manifest.description !== "string" || !manifest.description.trim()) details.push("description is required");
|
|
140
141
|
if (positiveNumber(manifest.canvasWidth) === null || positiveNumber(manifest.canvasHeight) === null) details.push("canvasWidth and canvasHeight must be positive numbers");
|
|
@@ -143,9 +144,13 @@ async function validateDesktopPetPackage(dir) {
|
|
|
143
144
|
const rawAnimations = manifest.animations && typeof manifest.animations === "object" ? manifest.animations : null;
|
|
144
145
|
if (!rawAnimations) details.push("animations is required");
|
|
145
146
|
const animations = [];
|
|
146
|
-
if (rawAnimations)
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
147
|
+
if (rawAnimations) {
|
|
148
|
+
const supportedActions = new Set(DESKTOP_PET_ACTIONS);
|
|
149
|
+
for (const action of Object.keys(rawAnimations)) if (!supportedActions.has(action)) details.push(`${action}: unsupported animation action`);
|
|
150
|
+
for (const action of DESKTOP_PET_ACTIONS) {
|
|
151
|
+
const animation = parseAnimation(action, rawAnimations[action], details);
|
|
152
|
+
if (animation) animations.push(animation);
|
|
153
|
+
}
|
|
149
154
|
}
|
|
150
155
|
for (const animation of animations) {
|
|
151
156
|
const sourcePath = safeResolve(dir, animation.src);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { KnowledgeSourceItem } from '../knowledge/types.js';
|
|
2
|
+
import type { FocusCalendarSignal, FocusView } from './types.js';
|
|
3
|
+
export declare function buildFocusCalendarSignals(items: KnowledgeSourceItem[], focuses: FocusView[], nowMs?: number): FocusCalendarSignal[];
|
|
4
|
+
export declare function listFocusCalendarSignals(focuses: FocusView[], nowMs?: number): FocusCalendarSignal[];
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { listKnowledgeSourceItems } from "../storage/sqlite/knowledge-repository.js";
|
|
2
|
+
//#region src/proactive/calendar-signals.ts
|
|
3
|
+
const LOOKAHEAD_MS = 7 * 864e5;
|
|
4
|
+
const FIELD_LIMIT = 8;
|
|
5
|
+
const TITLE_KEYS = new Set([
|
|
6
|
+
"summary",
|
|
7
|
+
"title",
|
|
8
|
+
"name",
|
|
9
|
+
"subject"
|
|
10
|
+
]);
|
|
11
|
+
const DESCRIPTION_KEYS = new Set([
|
|
12
|
+
"description",
|
|
13
|
+
"notes",
|
|
14
|
+
"body",
|
|
15
|
+
"agenda"
|
|
16
|
+
]);
|
|
17
|
+
const START_KEYS = new Set([
|
|
18
|
+
"start",
|
|
19
|
+
"starttime",
|
|
20
|
+
"start_time",
|
|
21
|
+
"startat",
|
|
22
|
+
"start_at",
|
|
23
|
+
"datetime"
|
|
24
|
+
]);
|
|
25
|
+
const END_KEYS = new Set([
|
|
26
|
+
"end",
|
|
27
|
+
"endtime",
|
|
28
|
+
"end_time",
|
|
29
|
+
"endat",
|
|
30
|
+
"end_at"
|
|
31
|
+
]);
|
|
32
|
+
const STOP_WORDS = new Set([
|
|
33
|
+
"about",
|
|
34
|
+
"after",
|
|
35
|
+
"before",
|
|
36
|
+
"from",
|
|
37
|
+
"have",
|
|
38
|
+
"meeting",
|
|
39
|
+
"project",
|
|
40
|
+
"review",
|
|
41
|
+
"the",
|
|
42
|
+
"this",
|
|
43
|
+
"with"
|
|
44
|
+
]);
|
|
45
|
+
function parsePayload(item) {
|
|
46
|
+
if (!item.normalizedText) return null;
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(item.normalizedText);
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function findStrings(value, keys, depth = 0) {
|
|
54
|
+
if (depth > 6 || value == null) return [];
|
|
55
|
+
if (Array.isArray(value)) return value.flatMap((item) => findStrings(item, keys, depth + 1)).slice(0, FIELD_LIMIT);
|
|
56
|
+
if (typeof value !== "object") return [];
|
|
57
|
+
const record = value;
|
|
58
|
+
const direct = Object.entries(record).flatMap(([key, nested]) => {
|
|
59
|
+
if (!keys.has(key.toLowerCase().replaceAll("-", "_"))) return [];
|
|
60
|
+
if (typeof nested === "string") return [nested];
|
|
61
|
+
return findStrings(nested, new Set([
|
|
62
|
+
"datetime",
|
|
63
|
+
"date",
|
|
64
|
+
"value"
|
|
65
|
+
]), depth + 1);
|
|
66
|
+
});
|
|
67
|
+
if (direct.length > 0) return direct.slice(0, FIELD_LIMIT);
|
|
68
|
+
return Object.values(record).flatMap((nested) => findStrings(nested, keys, depth + 1)).slice(0, FIELD_LIMIT);
|
|
69
|
+
}
|
|
70
|
+
function firstDate(payload, keys) {
|
|
71
|
+
for (const value of findStrings(payload, keys)) {
|
|
72
|
+
const parsed = Date.parse(value);
|
|
73
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function terms(value) {
|
|
77
|
+
const normalized = value.toLocaleLowerCase();
|
|
78
|
+
const result = new Set(normalized.match(/[a-z0-9][a-z0-9_-]{2,}/g)?.filter((word) => !STOP_WORDS.has(word)) ?? []);
|
|
79
|
+
for (const sequence of normalized.match(/[\p{Script=Han}]{2,}/gu) ?? []) for (let index = 0; index < sequence.length - 1; index += 1) result.add(sequence.slice(index, index + 2));
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
function relatedFocus(focuses, text) {
|
|
83
|
+
const eventTerms = terms(text);
|
|
84
|
+
if (eventTerms.size === 0) return void 0;
|
|
85
|
+
return focuses.map((focus) => ({
|
|
86
|
+
focus,
|
|
87
|
+
score: [...terms(`${focus.title} ${focus.summary}`)].filter((term) => eventTerms.has(term)).length
|
|
88
|
+
})).filter((candidate) => candidate.score >= 2).toSorted((left, right) => right.score - left.score || right.focus.focusScore - left.focus.focusScore)[0]?.focus;
|
|
89
|
+
}
|
|
90
|
+
function buildFocusCalendarSignals(items, focuses, nowMs = Date.now()) {
|
|
91
|
+
const end = nowMs + LOOKAHEAD_MS;
|
|
92
|
+
const signals = items.flatMap((item) => {
|
|
93
|
+
if (item.itemType !== "calendar_event") return [];
|
|
94
|
+
const payload = parsePayload(item);
|
|
95
|
+
const title = findStrings(payload, TITLE_KEYS)[0]?.trim();
|
|
96
|
+
const startsAt = firstDate(payload, START_KEYS);
|
|
97
|
+
if (!title || startsAt == null || startsAt < nowMs || startsAt > end) return [];
|
|
98
|
+
const focus = relatedFocus(focuses, `${title} ${findStrings(payload, DESCRIPTION_KEYS).join(" ")}`);
|
|
99
|
+
if (!focus) return [];
|
|
100
|
+
const endsAt = firstDate(payload, END_KEYS);
|
|
101
|
+
return [{
|
|
102
|
+
id: item.id,
|
|
103
|
+
focusId: focus.id,
|
|
104
|
+
focusTitle: focus.title,
|
|
105
|
+
title,
|
|
106
|
+
startsAt,
|
|
107
|
+
...endsAt != null ? { endsAt } : {},
|
|
108
|
+
sourceInstanceId: item.sourceInstanceId
|
|
109
|
+
}];
|
|
110
|
+
}).toSorted((left, right) => left.startsAt - right.startsAt);
|
|
111
|
+
return signals.filter((signal, index) => signals.findIndex((candidate) => candidate.focusId === signal.focusId && candidate.startsAt === signal.startsAt && candidate.title.toLocaleLowerCase() === signal.title.toLocaleLowerCase()) === index).slice(0, 20);
|
|
112
|
+
}
|
|
113
|
+
function listFocusCalendarSignals(focuses, nowMs = Date.now()) {
|
|
114
|
+
return buildFocusCalendarSignals(listKnowledgeSourceItems({
|
|
115
|
+
itemType: "calendar_event",
|
|
116
|
+
limit: 500
|
|
117
|
+
}), focuses, nowMs);
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
export { buildFocusCalendarSignals, listFocusCalendarSignals };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Automation, AutomationRun } from '../automations/index.js';
|
|
2
|
+
import type { FocusView, FocusWatch, FocusWatchKind } from './types.js';
|
|
3
|
+
interface AutomationPort {
|
|
4
|
+
create(input: {
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
projectId?: string;
|
|
8
|
+
trigger: {
|
|
9
|
+
kind: 'schedule';
|
|
10
|
+
schedule: {
|
|
11
|
+
kind: 'interval';
|
|
12
|
+
everyMs: number;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
action: {
|
|
16
|
+
kind: 'agent';
|
|
17
|
+
instruction: string;
|
|
18
|
+
timeoutSeconds: number;
|
|
19
|
+
};
|
|
20
|
+
safety: {
|
|
21
|
+
mode: 'suggest_only';
|
|
22
|
+
};
|
|
23
|
+
afterRun: {
|
|
24
|
+
kind: 'none';
|
|
25
|
+
};
|
|
26
|
+
reliability: {
|
|
27
|
+
timeoutSeconds: number;
|
|
28
|
+
disableAfterConsecutiveFailures: number;
|
|
29
|
+
};
|
|
30
|
+
}): Promise<Automation>;
|
|
31
|
+
pause(id: string): Promise<Automation | null>;
|
|
32
|
+
resume(id: string): Promise<Automation | null>;
|
|
33
|
+
update(id: string, patch: {
|
|
34
|
+
action: {
|
|
35
|
+
kind: 'agent';
|
|
36
|
+
instruction: string;
|
|
37
|
+
timeoutSeconds: number;
|
|
38
|
+
};
|
|
39
|
+
}): Promise<Automation | null>;
|
|
40
|
+
runNow(id: string): Promise<AutomationRun>;
|
|
41
|
+
}
|
|
42
|
+
export declare class FocusService {
|
|
43
|
+
private readonly automations;
|
|
44
|
+
private readonly goals;
|
|
45
|
+
constructor(automations: AutomationPort);
|
|
46
|
+
list(options?: {
|
|
47
|
+
includeUnreviewed?: boolean;
|
|
48
|
+
}): FocusView[];
|
|
49
|
+
confirm(id: string): FocusView | null;
|
|
50
|
+
activateTrial(input: {
|
|
51
|
+
threadId: string;
|
|
52
|
+
kind?: FocusWatchKind;
|
|
53
|
+
eventContext?: string;
|
|
54
|
+
}): Promise<{
|
|
55
|
+
focus: FocusView;
|
|
56
|
+
watch: FocusWatch;
|
|
57
|
+
}>;
|
|
58
|
+
pauseWatch(id: string): Promise<FocusWatch | null>;
|
|
59
|
+
reconcileExpiredTrials(nowMs?: number): Promise<number>;
|
|
60
|
+
recordInsightFeedback(watchId: string, useful: boolean): Promise<FocusWatch | null>;
|
|
61
|
+
}
|
|
62
|
+
export {};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { GoalService } from "../goals/goal-service.js";
|
|
4
|
+
import "../goals/index.js";
|
|
5
|
+
import { addWorkUnderstandingThreadFeedback, getWorkUnderstandingThread, listWorkUnderstandingThreads } from "../work-discovery/thread-repository.js";
|
|
6
|
+
import { createFocusWatch, getFocusWatchByThreadAndKind, listFocusWatches, recordFocusWatchFeedback, restartFocusWatchTrial, setFocusWatchStatus } from "./watch-repository.js";
|
|
7
|
+
//#region src/proactive/focus-service.ts
|
|
8
|
+
init_logger();
|
|
9
|
+
const TRIAL_DURATION_MS = 7 * 864e5;
|
|
10
|
+
const DAILY_INTERVAL_MS = 1440 * 60 * 1e3;
|
|
11
|
+
const log = createLogger("FocusService");
|
|
12
|
+
function instructionForFocus(title, summary, kind, eventContext) {
|
|
13
|
+
const intent = kind === "progress" ? "Look for meaningful progress, blockers, and the most useful next step." : kind === "staleness" ? "Check whether this focus has stalled and identify the smallest credible unblock." : kind === "deadline" ? "Check upcoming deadlines or meetings and prepare only the material that is needed next." : "Search for external changes published since the previous daily check. Use primary sources when possible, include the publication date and canonical URL, reject generic background news, and explain the concrete impact on this focus.";
|
|
14
|
+
return [
|
|
15
|
+
"Maintain this user-confirmed focus in suggest-only mode.",
|
|
16
|
+
`Focus: ${title}`,
|
|
17
|
+
`Context: ${summary}`,
|
|
18
|
+
...eventContext ? [`Upcoming event selected by the user: ${eventContext}`] : [],
|
|
19
|
+
intent,
|
|
20
|
+
"Inspect only evidence that changed since the previous daily check; use timestamps, history, status, or source revisions to establish the delta.",
|
|
21
|
+
"Use evidence available to the agent. Do not modify files, send messages, publish content, or claim progress without evidence.",
|
|
22
|
+
"Return JSON only, with no markdown.",
|
|
23
|
+
"If nothing materially changed: {\"meaningful\":false}.",
|
|
24
|
+
"Otherwise return {\"meaningful\":true,\"title\":\"short title\",\"summary\":\"what changed\",\"whyItMatters\":\"concrete impact\",\"nextAction\":\"one next action\",\"evidence\":[{\"label\":\"specific observed evidence\",\"source\":\"file, event, or canonical URL\",\"publishedAt\":\"ISO date when this is external news\"}]}.",
|
|
25
|
+
"A meaningful result requires at least one specific evidence item. Never invent evidence. Keep the entire JSON response under 2,000 characters."
|
|
26
|
+
].join("\n");
|
|
27
|
+
}
|
|
28
|
+
var FocusService = class {
|
|
29
|
+
goals = new GoalService();
|
|
30
|
+
constructor(automations) {
|
|
31
|
+
this.automations = automations;
|
|
32
|
+
}
|
|
33
|
+
list(options = {}) {
|
|
34
|
+
const threads = listWorkUnderstandingThreads({ limit: 200 }).filter((thread) => options.includeUnreviewed || thread.userStatus === "confirmed" || thread.userStatus === "corrected");
|
|
35
|
+
const watches = listFocusWatches();
|
|
36
|
+
const goals = this.goals.list({
|
|
37
|
+
status: [
|
|
38
|
+
"active",
|
|
39
|
+
"blocked",
|
|
40
|
+
"needs_input",
|
|
41
|
+
"paused"
|
|
42
|
+
],
|
|
43
|
+
limit: 200
|
|
44
|
+
});
|
|
45
|
+
return threads.map((thread) => {
|
|
46
|
+
const goal = goals.find((candidate) => candidate.projectId && thread.projectIds.includes(candidate.projectId));
|
|
47
|
+
return {
|
|
48
|
+
id: thread.id,
|
|
49
|
+
title: thread.title,
|
|
50
|
+
summary: thread.summary,
|
|
51
|
+
status: thread.status,
|
|
52
|
+
horizon: thread.horizon,
|
|
53
|
+
confidence: thread.confidence,
|
|
54
|
+
focusScore: thread.focusScore,
|
|
55
|
+
userStatus: thread.userStatus,
|
|
56
|
+
projectIds: thread.projectIds,
|
|
57
|
+
...goal ? {
|
|
58
|
+
goalId: goal.id,
|
|
59
|
+
nextAction: goal.nextAction,
|
|
60
|
+
blockedReason: goal.blockedReason
|
|
61
|
+
} : {},
|
|
62
|
+
watches: watches.filter((watch) => watch.threadId === thread.id),
|
|
63
|
+
lastObservedAt: thread.lastObservedAt
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
confirm(id) {
|
|
68
|
+
const thread = addWorkUnderstandingThreadFeedback({
|
|
69
|
+
threadId: id,
|
|
70
|
+
decision: "confirmed"
|
|
71
|
+
});
|
|
72
|
+
return thread ? this.list({ includeUnreviewed: true }).find((focus) => focus.id === thread.id) ?? null : null;
|
|
73
|
+
}
|
|
74
|
+
async activateTrial(input) {
|
|
75
|
+
const kind = input.kind ?? "progress";
|
|
76
|
+
let thread = getWorkUnderstandingThread(input.threadId);
|
|
77
|
+
if (!thread || thread.userStatus === "rejected") throw new Error("Focus not found");
|
|
78
|
+
if (thread.userStatus === "unreviewed") thread = addWorkUnderstandingThreadFeedback({
|
|
79
|
+
threadId: thread.id,
|
|
80
|
+
decision: "confirmed"
|
|
81
|
+
});
|
|
82
|
+
if (!thread) throw new Error("Focus not found");
|
|
83
|
+
const existing = getFocusWatchByThreadAndKind(thread.id, kind);
|
|
84
|
+
if (existing) {
|
|
85
|
+
if (input.eventContext) await this.automations.update(existing.automationId, { action: {
|
|
86
|
+
kind: "agent",
|
|
87
|
+
instruction: instructionForFocus(thread.title, thread.summary, kind, input.eventContext),
|
|
88
|
+
timeoutSeconds: 300
|
|
89
|
+
} });
|
|
90
|
+
if (existing.status === "paused") {
|
|
91
|
+
await this.automations.resume(existing.automationId);
|
|
92
|
+
restartFocusWatchTrial(existing.id, Date.now() + TRIAL_DURATION_MS);
|
|
93
|
+
}
|
|
94
|
+
if (input.eventContext) this.automations.runNow(existing.automationId).catch((err) => {
|
|
95
|
+
log.warn({
|
|
96
|
+
err,
|
|
97
|
+
automationId: existing.automationId,
|
|
98
|
+
watchId: existing.id
|
|
99
|
+
}, "Focus event preparation run failed");
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
focus: this.list({ includeUnreviewed: true }).find((item) => item.id === thread.id),
|
|
103
|
+
watch: getFocusWatchByThreadAndKind(thread.id, kind)
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const automation = await this.automations.create({
|
|
107
|
+
name: `Keep moving: ${thread.title}`.slice(0, 200),
|
|
108
|
+
description: `Seven-day ${kind} watch for a user-confirmed focus.`,
|
|
109
|
+
...thread.projectIds[0] ? { projectId: thread.projectIds[0] } : {},
|
|
110
|
+
trigger: {
|
|
111
|
+
kind: "schedule",
|
|
112
|
+
schedule: {
|
|
113
|
+
kind: "interval",
|
|
114
|
+
everyMs: DAILY_INTERVAL_MS
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
action: {
|
|
118
|
+
kind: "agent",
|
|
119
|
+
instruction: instructionForFocus(thread.title, thread.summary, kind, input.eventContext),
|
|
120
|
+
timeoutSeconds: 300
|
|
121
|
+
},
|
|
122
|
+
safety: { mode: "suggest_only" },
|
|
123
|
+
afterRun: { kind: "none" },
|
|
124
|
+
reliability: {
|
|
125
|
+
timeoutSeconds: 300,
|
|
126
|
+
disableAfterConsecutiveFailures: 3
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
const watch = createFocusWatch({
|
|
130
|
+
threadId: thread.id,
|
|
131
|
+
automationId: automation.id,
|
|
132
|
+
kind,
|
|
133
|
+
config: {
|
|
134
|
+
cadence: "daily",
|
|
135
|
+
notifyOnlyWhenMeaningful: true
|
|
136
|
+
},
|
|
137
|
+
trialEndsAt: Date.now() + TRIAL_DURATION_MS
|
|
138
|
+
});
|
|
139
|
+
this.automations.runNow(automation.id).catch((err) => {
|
|
140
|
+
log.warn({
|
|
141
|
+
err,
|
|
142
|
+
automationId: automation.id,
|
|
143
|
+
watchId: watch.id
|
|
144
|
+
}, "Initial focus watch run failed");
|
|
145
|
+
});
|
|
146
|
+
return {
|
|
147
|
+
focus: this.list({ includeUnreviewed: true }).find((item) => item.id === thread.id),
|
|
148
|
+
watch
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
async pauseWatch(id) {
|
|
152
|
+
const watch = listFocusWatches().find((item) => item.id === id);
|
|
153
|
+
if (!watch) return null;
|
|
154
|
+
await this.automations.pause(watch.automationId);
|
|
155
|
+
return setFocusWatchStatus(id, "paused");
|
|
156
|
+
}
|
|
157
|
+
async reconcileExpiredTrials(nowMs = Date.now()) {
|
|
158
|
+
const expired = listFocusWatches({ status: "active" }).filter((watch) => watch.trialEndsAt != null && watch.trialEndsAt <= nowMs);
|
|
159
|
+
for (const watch of expired) {
|
|
160
|
+
await this.automations.pause(watch.automationId);
|
|
161
|
+
setFocusWatchStatus(watch.id, "paused", nowMs);
|
|
162
|
+
}
|
|
163
|
+
return expired.length;
|
|
164
|
+
}
|
|
165
|
+
async recordInsightFeedback(watchId, useful) {
|
|
166
|
+
const result = recordFocusWatchFeedback(watchId, useful);
|
|
167
|
+
if (!result) return null;
|
|
168
|
+
if (!useful && result.consecutiveDismissed >= 3) return this.pauseWatch(watchId);
|
|
169
|
+
return result.watch;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
//#endregion
|
|
173
|
+
export { FocusService };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export * from './calendar-signals.js';
|
|
3
|
+
export * from './focus-service.js';
|
|
4
|
+
export * from './insight-repository.js';
|
|
5
|
+
export * from './run-processor.js';
|
|
6
|
+
export * from './trial-expiry-reconciler.js';
|
|
7
|
+
export * from './watch-repository.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { buildFocusCalendarSignals, listFocusCalendarSignals } from "./calendar-signals.js";
|
|
2
|
+
import { createFocusWatch, getFocusWatchByAutomationId, getFocusWatchByThreadAndKind, listFocusWatches, recordFocusWatchFeedback, recordFocusWatchRun, restartFocusWatchTrial, setFocusWatchStatus } from "./watch-repository.js";
|
|
3
|
+
import { FocusService } from "./focus-service.js";
|
|
4
|
+
import { claimProactiveInsightApproval, createProactiveInsight, getProactiveInsight, listProactiveInsights, setProactiveInsightStatus } from "./insight-repository.js";
|
|
5
|
+
import { hasValidIntelligenceEvidence, parseFocusRunResult, processFocusAutomationRun } from "./run-processor.js";
|
|
6
|
+
import { startTrialExpiryReconciler } from "./trial-expiry-reconciler.js";
|
|
7
|
+
export { FocusService, buildFocusCalendarSignals, claimProactiveInsightApproval, createFocusWatch, createProactiveInsight, getFocusWatchByAutomationId, getFocusWatchByThreadAndKind, getProactiveInsight, hasValidIntelligenceEvidence, listFocusCalendarSignals, listFocusWatches, listProactiveInsights, parseFocusRunResult, processFocusAutomationRun, recordFocusWatchFeedback, recordFocusWatchRun, restartFocusWatchTrial, setFocusWatchStatus, setProactiveInsightStatus, startTrialExpiryReconciler };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { FocusWatchKind, ProactiveEvidence, ProactiveInsight, ProactiveInsightStatus } from './types.js';
|
|
2
|
+
export declare function getProactiveInsight(id: string): ProactiveInsight | null;
|
|
3
|
+
export declare function listProactiveInsights(options?: {
|
|
4
|
+
status?: ProactiveInsightStatus[];
|
|
5
|
+
limit?: number;
|
|
6
|
+
}): ProactiveInsight[];
|
|
7
|
+
export declare function createProactiveInsight(input: {
|
|
8
|
+
watchId: string;
|
|
9
|
+
runId: string;
|
|
10
|
+
kind: FocusWatchKind;
|
|
11
|
+
title: string;
|
|
12
|
+
summary: string;
|
|
13
|
+
whyItMatters: string;
|
|
14
|
+
nextAction: string;
|
|
15
|
+
evidence: ProactiveEvidence[];
|
|
16
|
+
nowMs?: number;
|
|
17
|
+
}): ProactiveInsight | null;
|
|
18
|
+
export declare function setProactiveInsightStatus(id: string, status: ProactiveInsightStatus, nowMs?: number, expectedStatus?: ProactiveInsightStatus): ProactiveInsight | null;
|
|
19
|
+
export declare function claimProactiveInsightApproval(id: string, nowMs?: number): ProactiveInsight | null;
|