@xopcai/xopc 0.0.197 → 0.0.199
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/{activity-store-BwmdJ5pv.js → activity-store-DYVN1hRp.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-C5st7lt5.js → agent-avatar-dicebear-Bt70bZjv.js} +1 -1
- package/dist/gateway/static/root/assets/agents-D19uNmJf.js +68 -0
- package/dist/gateway/static/root/assets/{api-BM1hNzjw.js → api-B8mzbAle.js} +1 -1
- package/dist/gateway/static/root/assets/app-management-settings-panel-B4x7JfL8.js +1 -0
- package/dist/gateway/static/root/assets/appearance-settings-Dwdagf-Q.js +1 -0
- package/dist/gateway/static/root/assets/apps-page-DkTiesxe.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-qL07mwnY.js → archive-plugin-Cv0VXKLm.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-CqSobpdi.js → attachment-load-CgidKrXn.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-BR6qMOVr.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-DBRPyLYE.js → binary-plugins-DN3CQgIq.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-6wG1LU7t.js → block-editor-BwkSl1F_.js} +5 -5
- package/dist/gateway/static/root/assets/browser-settings-page-CDFQWAxI.js +8 -0
- package/dist/gateway/static/root/assets/browser-workflow-inputs-_ndaecsf.js +1 -0
- package/dist/gateway/static/root/assets/{browser-workflows-page-BEu3E0et.js → browser-workflows-page-C_I31wLm.js} +2 -2
- package/dist/gateway/static/root/assets/button-CSEbl4zT.js +1 -0
- package/dist/gateway/static/root/assets/capabilities-settings-panel-D41mssOq.js +7 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-BoOzFotX.js → capability-presets-api-Dy1jZJKs.js} +1 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-BXgCyuu2.js +3 -0
- package/dist/gateway/static/root/assets/{channel-recipient-api-_8IiTxRB.js → channel-recipient-api-D-9qiuio.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-DV5BhAeu.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-B4Rbh3bM.js +1 -0
- package/dist/gateway/static/root/assets/connectors-api-5FEBYguL.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-C_qkEN0T.js +2 -0
- package/dist/gateway/static/root/assets/copy-Dv6d4Dvw.js +1 -0
- package/dist/gateway/static/root/assets/cron-expression-SEFx5rnr.js +2 -0
- package/dist/gateway/static/root/assets/date-picker-D4zCUQJL.js +1 -0
- package/dist/gateway/static/root/assets/dependency-picker-DpQAxS2k.js +1 -0
- package/dist/gateway/static/root/assets/{desktop-pet-CsLR4pWB.js → desktop-pet-B_5G3lQl.js} +1 -1
- package/dist/gateway/static/root/assets/desktop-pet-settings-ClEyoUJe.js +1 -0
- package/dist/gateway/static/root/assets/directory-picker-path-field-AsFWmOjG.js +1 -0
- package/dist/gateway/static/root/assets/dist-BPoKqzeH.js +1 -0
- package/dist/gateway/static/root/assets/dist-DY4Af0fY.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-CD73euAM.js → extension-debug-page-C22Y2o5l.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DAKjpNwd.js → extension-page-C61Ikawk.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-WlWWcDFr.js +1 -0
- package/dist/gateway/static/root/assets/external-link-DjqjBmc3.js +1 -0
- package/dist/gateway/static/root/assets/form-field-width-DJw_jULe.js +1 -0
- package/dist/gateway/static/root/assets/gateway-config-api-ByIOMQdc.js +1 -0
- package/dist/gateway/static/root/assets/gateway-config-swr-jB0hAhWO.js +1 -0
- package/dist/gateway/static/root/assets/gateway-settings-CzzS9A17.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry--2njcDpC.js → gateway-startup-retry-C-fRlHCL.js} +1 -1
- package/dist/gateway/static/root/assets/globe-DSSGFWFn.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-B4A_CXUL.js +1 -0
- package/dist/gateway/static/root/assets/home-page-FVGYYPWo.js +3 -0
- package/dist/gateway/static/root/assets/index-CGbKTCST.js +152 -0
- package/dist/gateway/static/root/assets/index-CR233S0Q.css +1 -0
- package/dist/gateway/static/root/assets/interaction-BmQogeqz.js +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-HMhxgOuD.js → keyboard-shortcuts-settings-BD_TFz0_.js} +1 -1
- package/dist/gateway/static/root/assets/loader-circle-CifKLtgG.js +1 -0
- package/dist/gateway/static/root/assets/local-app-workbench-page-BNpkigNb.js +2 -0
- package/dist/gateway/static/root/assets/local-apps-page-BwbUeLfm.js +1 -0
- package/dist/gateway/static/root/assets/{locale-store-Bjvw7jtg.js → locale-store-xULEcFVs.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-DXP9OQQt.js +2 -0
- package/dist/gateway/static/root/assets/management-settings-CW2VpqtT.js +1 -0
- package/dist/gateway/static/root/assets/{markdown-split-DvVBewS4.js → markdown-split-B1M9KO16.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-YVj7NVzD.js → media-plugins-B_QlNdqZ.js} +1 -1
- package/dist/gateway/static/root/assets/messages-3qRsLZuX.js +3 -0
- package/dist/gateway/static/root/assets/{notes-page-BDzuL2I9.js → notes-page-BTbD4GmH.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-JRiq7QDW.js +4 -0
- package/dist/gateway/static/root/assets/page-tabs-BaEQMYm_.js +1 -0
- package/dist/gateway/static/root/assets/popover-select-BzdnTy3h.js +1 -0
- package/dist/gateway/static/root/assets/preview-open-alternatives-CeqtArlZ.js +1 -0
- package/dist/gateway/static/root/assets/{product-open-page-Bwx_CQht.js → product-open-page-DnalBZZs.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-CjWA13aE.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-jsCkScbG.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-CGPJTEkM.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-Fe05Nus6.js → runtime-tools-settings-panel-cGkBEfg4.js} +4 -4
- package/dist/gateway/static/root/assets/schema-form-CmsrLlxu.js +1 -0
- package/dist/gateway/static/root/assets/sessions-page-BBHzziMd.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-2cGg8-9h.js +1 -0
- package/dist/gateway/static/root/assets/settings-form-draft-Dy10ldo8.js +1 -0
- package/dist/gateway/static/root/assets/settings-form-section-DUWCbaMJ.js +1 -0
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-BRE_-7Da.js → settings-loading-skeleton-Bvy5rdMY.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-C1zxCpqB.js → settings-page-cZSAwsRR.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-layout-BRQNel6r.js +1 -0
- package/dist/gateway/static/root/assets/settings-shell-dialog-layer-BU3UuiJr.js +1 -0
- package/dist/gateway/static/root/assets/setup-status-panel-Cf06b66-.js +1 -0
- package/dist/gateway/static/root/assets/share-preview-page-BzA2NCAH.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-xlNKydYY.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-C5Xu93c_.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-Bvde7_i5.js +2 -0
- package/dist/gateway/static/root/assets/{skills-page.utils-CYwFWTso.js → skills-page.utils-vXSqHR7g.js} +1 -1
- package/dist/gateway/static/root/assets/system-settings-panel-9cQGOGCK.js +1 -0
- package/dist/gateway/static/root/assets/task-detail-page-Bkno4tTe.js +1 -0
- package/dist/gateway/static/root/assets/{theme-store-DtxpMC5S.js → theme-store-CHQRiuCF.js} +1 -1
- package/dist/gateway/static/root/assets/tool-activity-R8V9l6nk.js +1 -0
- package/dist/gateway/static/root/assets/typed-models-lib-q5-Vfqd8.js +1 -0
- package/dist/gateway/static/root/assets/use-autosave-7XfKULPI.js +1 -0
- package/dist/gateway/static/root/assets/user-context-page-CZ9m9aC_.js +1 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-DXr21v-3.js +1 -0
- package/dist/gateway/static/root/assets/work-discovery-overlay-CeG7g_DZ.js +1 -0
- package/dist/gateway/static/root/assets/work-discovery-page-CQWZD03N.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-DqIb1oBB.js → workflow-run-setup-panel-DSQQZP9T.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-Cs-MXFJ1.js +5 -0
- package/dist/gateway/static/root/assets/working-directory-picker-modal-D-IgcHPp.js +1 -0
- package/dist/gateway/static/root/index.html +23 -8
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/tasks.js +54 -2
- package/dist/src/agent/agent-manager.d.ts +1 -0
- package/dist/src/agent/agent-manager.js +1 -0
- package/dist/src/agent/service.js +1 -0
- package/dist/src/agent/service.types.d.ts +1 -0
- package/dist/src/agent/tools/factory.d.ts +2 -0
- package/dist/src/agent/tools/factory.js +2 -1
- package/dist/src/agent/tools/xopc-use-tool.d.ts +1 -0
- package/dist/src/agent/tools/xopc-use-tool.js +56 -6
- package/dist/src/automations/domain/validation.d.ts +40 -0
- package/dist/src/automations/draft/automation-draft-validator.d.ts +4 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/config/rules.js +2 -2
- package/dist/src/gateway/hono/app.d.ts +2 -0
- package/dist/src/gateway/hono/app.js +10 -3
- package/dist/src/gateway/hono/middleware/strict-rate-limit.d.ts +4 -0
- package/dist/src/gateway/hono/middleware/strict-rate-limit.js +8 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +2 -1
- package/dist/src/gateway/hono/routes/config.js +9 -0
- package/dist/src/gateway/hono/routes/deps.d.ts +1 -0
- package/dist/src/gateway/hono/routes/projects.js +55 -0
- package/dist/src/gateway/hono/routes/tasks.js +112 -19
- package/dist/src/gateway/host.d.ts +9 -1
- package/dist/src/gateway/host.js +22 -5
- package/dist/src/gateway/rate-limit/buckets.d.ts +3 -0
- package/dist/src/gateway/rate-limit/buckets.js +12 -0
- package/dist/src/gateway/runtime-config.js +6 -5
- package/dist/src/gateway/security/audit.js +0 -7
- package/dist/src/gateway/server.js +4 -1
- package/dist/src/gateway/service.d.ts +1 -0
- package/dist/src/gateway/service.js +5 -1
- package/dist/src/proactive/actions/service.d.ts +2 -0
- package/dist/src/proactive/actions/service.js +80 -0
- package/dist/src/proactive/execution/insight.js +20 -0
- package/dist/src/proactive/execution/repository.js +8 -2
- package/dist/src/proactive/execution/types.d.ts +14 -0
- package/dist/src/proactive/inbox/repository.js +59 -9
- package/dist/src/proactive/inbox/service.d.ts +2 -2
- package/dist/src/proactive/inbox/service.js +7 -2
- package/dist/src/proactive/inbox/types.d.ts +14 -0
- package/dist/src/proactive/inbox/worker.js +2 -0
- package/dist/src/proactive/scenarios/prompt-composer.js +2 -2
- package/dist/src/storage/sqlite/migrations/118_task_board_rank.sql +6 -0
- package/dist/src/storage/sqlite/migrations/119_proactive_disposition.sql +13 -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/tasks/home-query-service.d.ts +5 -0
- package/dist/src/tasks/home-query-service.js +7 -2
- package/dist/src/tasks/index.d.ts +1 -0
- package/dist/src/tasks/index.js +4 -3
- package/dist/src/tasks/project-monitoring-service.d.ts +9 -0
- package/dist/src/tasks/project-monitoring-service.js +41 -3
- package/dist/src/tasks/project-operating-view-service.d.ts +1 -2
- package/dist/src/tasks/project-operating-view-service.js +26 -23
- package/dist/src/tasks/task-application-service.js +99 -6
- package/dist/src/tasks/task-change-events.d.ts +13 -0
- package/dist/src/tasks/task-change-events.js +29 -0
- package/dist/src/tasks/task-read-model-projector.js +2 -1
- package/dist/src/tasks/task-repository.d.ts +7 -0
- package/dist/src/tasks/task-repository.js +29 -6
- package/dist/src/tunnel/enable-lan-pairing.js +15 -26
- package/dist/src/tunnel/pair-context.js +1 -1
- package/dist/src/tunnel/pair-url.js +1 -1
- package/dist/src/tunnel/tunnel-qr.d.ts +0 -7
- package/dist/src/tunnel/tunnel-qr.js +2 -19
- package/package.json +3 -1
- package/dist/gateway/static/root/assets/agents-Baxfj44y.js +0 -68
- package/dist/gateway/static/root/assets/app-management-settings-panel-S8LhoD6G.js +0 -1
- package/dist/gateway/static/root/assets/appearance-settings-D8Cd5vDP.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-0uCxKyE5.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-XyDat125.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-CrYNMR4w.js +0 -8
- package/dist/gateway/static/root/assets/browser-workflow-inputs-Abil4puY.js +0 -1
- package/dist/gateway/static/root/assets/capabilities-settings-panel-CvMcDhGf.js +0 -7
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-CHJDY-Ih.js +0 -3
- package/dist/gateway/static/root/assets/channels-settings-Ds9BJbcY.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-DPaJSYX_.js +0 -1
- package/dist/gateway/static/root/assets/connectors-api-CgWjgPSI.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-NUs-Zf7F.js +0 -2
- package/dist/gateway/static/root/assets/cron-expression-ZdDBAoqA.js +0 -2
- package/dist/gateway/static/root/assets/dependency-picker-BGeB1jpn.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-settings-CCjT1bdo.js +0 -1
- package/dist/gateway/static/root/assets/directory-picker-path-field-C-Te1QN3.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-BLSNdlgd.js +0 -1
- package/dist/gateway/static/root/assets/gateway-config-api-CQrf0k3D.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-CvgH8IJI.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-SPpnqbjl.js +0 -1
- package/dist/gateway/static/root/assets/home-page-BFoD6047.js +0 -3
- package/dist/gateway/static/root/assets/index-84ys9u-K.js +0 -152
- package/dist/gateway/static/root/assets/index-D-srnucP.css +0 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-D5uL2_Fd.js +0 -2
- package/dist/gateway/static/root/assets/local-apps-page-DIWkFa3g.js +0 -1
- package/dist/gateway/static/root/assets/logs-page--6v8qq6y.js +0 -2
- package/dist/gateway/static/root/assets/management-settings-DtEuS-kR.js +0 -1
- package/dist/gateway/static/root/assets/messages-DQ5X6cXp.js +0 -3
- package/dist/gateway/static/root/assets/notes-workbench-IdjuNXvH.js +0 -4
- package/dist/gateway/static/root/assets/page-tabs-7bSEWfSX.js +0 -1
- package/dist/gateway/static/root/assets/preview-open-alternatives-tgLbHgcV.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-DnJXSuM4.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-DckZKlBI.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CfbUHBUH.js +0 -1
- package/dist/gateway/static/root/assets/schema-form-CFfyC4CB.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-BCfCh4My.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-Cbi4FvzO.js +0 -1
- package/dist/gateway/static/root/assets/settings-form-section-gEnEYT_3.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-layout-CRpXF_05.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-BcHdpmFf.js +0 -1
- package/dist/gateway/static/root/assets/share-preview-page-DU8L68A5.js +0 -2
- package/dist/gateway/static/root/assets/shares-settings-kjTqH9yY.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-QAGpwzIM.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-lE1-YRdK.js +0 -2
- package/dist/gateway/static/root/assets/system-settings-panel-CVDsiPKn.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-DsM13-cy.js +0 -1
- package/dist/gateway/static/root/assets/typed-models-lib-DelyYgoc.js +0 -1
- package/dist/gateway/static/root/assets/use-autosave-C50Mq5yQ.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-D-VEOt2A.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-DYfgTe3Y.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-overlay-DPbujsp3.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-page-D77opady.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-B9-PLmlX.js +0 -5
- package/dist/src/gateway/ensure-network-cors.d.ts +0 -3
- package/dist/src/gateway/ensure-network-cors.js +0 -23
|
@@ -20,6 +20,15 @@ export interface InsightCandidate {
|
|
|
20
20
|
consequence: string;
|
|
21
21
|
}>;
|
|
22
22
|
};
|
|
23
|
+
proposedAction?: {
|
|
24
|
+
id: 'create_project_task';
|
|
25
|
+
risk: 'low';
|
|
26
|
+
rationale: string;
|
|
27
|
+
input: {
|
|
28
|
+
title: string;
|
|
29
|
+
objective: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
23
32
|
urgency: 'low' | 'medium' | 'high' | 'critical';
|
|
24
33
|
confidence: number;
|
|
25
34
|
evidenceIds: string[];
|
|
@@ -30,6 +39,11 @@ export interface ProactiveInsight extends InsightCandidate {
|
|
|
30
39
|
subscriptionId: string;
|
|
31
40
|
scenarioKey: string;
|
|
32
41
|
valueScore: number;
|
|
42
|
+
disposition?: 'record_silently' | 'show_in_work' | 'request_approval' | 'auto_execute';
|
|
43
|
+
dispositionReason?: string;
|
|
44
|
+
actionStatus?: 'not_authorized' | 'approval_required' | 'pending' | 'executing' | 'completed' | 'rejected' | 'failed';
|
|
45
|
+
actionResult?: Record<string, unknown>;
|
|
46
|
+
actionError?: string;
|
|
33
47
|
createdAt: string;
|
|
34
48
|
}
|
|
35
49
|
export interface ProactiveAgentExecutor {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getSqliteDatabase, runSqliteWriteTransaction } from "../../storage/sqlite/transaction.js";
|
|
2
|
+
import { decideProactiveDisposition, nextQuietHoursEnd, proactiveDispositionReason } from "../../tasks/project-monitoring-service.js";
|
|
2
3
|
import { randomUUID } from "node:crypto";
|
|
3
4
|
//#region src/proactive/inbox/repository.ts
|
|
4
5
|
const s = (row, key) => String(row[key]);
|
|
@@ -20,6 +21,12 @@ function itemFromRow(row) {
|
|
|
20
21
|
recommendation: s(row, "recommendation"),
|
|
21
22
|
workDone: s(row, "work_done"),
|
|
22
23
|
...row.decision_json ? { decision: JSON.parse(s(row, "decision_json")) } : {},
|
|
24
|
+
...row.proposed_action_json ? { proposedAction: JSON.parse(s(row, "proposed_action_json")) } : {},
|
|
25
|
+
disposition: s(row, "disposition"),
|
|
26
|
+
dispositionReason: s(row, "disposition_reason"),
|
|
27
|
+
...row.action_status ? { actionStatus: s(row, "action_status") } : {},
|
|
28
|
+
...row.action_result_json ? { actionResult: JSON.parse(s(row, "action_result_json")) } : {},
|
|
29
|
+
...row.action_error ? { actionError: s(row, "action_error") } : {},
|
|
23
30
|
urgency: s(row, "urgency"),
|
|
24
31
|
confidence: Number(row.confidence),
|
|
25
32
|
valueScore: Number(row.value_score),
|
|
@@ -28,27 +35,70 @@ function itemFromRow(row) {
|
|
|
28
35
|
};
|
|
29
36
|
}
|
|
30
37
|
const SELECT_ITEM = `SELECT i.*, x.subscription_id, x.scenario_key, x.title, x.summary, x.why_now, x.impact, x.recommendation,
|
|
31
|
-
x.work_done, x.decision_json, x.
|
|
38
|
+
x.work_done, x.decision_json, x.proposed_action_json, x.disposition, x.disposition_reason,
|
|
39
|
+
x.action_status, x.action_result_json, x.action_error, x.urgency, x.confidence, x.value_score, x.evidence_ids_json FROM proactive_inbox_items i
|
|
32
40
|
JOIN proactive_insights x ON x.insight_id = i.insight_id`;
|
|
33
41
|
function projectInsightsToInbox(now = /* @__PURE__ */ new Date()) {
|
|
34
42
|
return runSqliteWriteTransaction((db) => {
|
|
35
|
-
const missing = db.prepare(`SELECT x.insight_id
|
|
43
|
+
const missing = db.prepare(`SELECT x.insight_id, x.confidence, x.value_score, x.decision_json,
|
|
44
|
+
x.proposed_action_json, b.aggregation_key, p.project_id, p.mode, p.quiet_hours_json,
|
|
45
|
+
p.allowed_actions_json, p.confidence_threshold FROM proactive_insights x
|
|
36
46
|
JOIN proactive_runs r ON r.run_id = x.run_id
|
|
37
47
|
JOIN proactive_signal_batches b ON b.batch_id = r.batch_id
|
|
38
48
|
LEFT JOIN project_monitoring_policies p
|
|
39
49
|
ON b.aggregation_key = 'project:' || p.project_id
|
|
40
|
-
WHERE x.
|
|
41
|
-
|
|
42
|
-
ORDER BY x.created_at`).all();
|
|
50
|
+
WHERE x.disposition IS NULL ORDER BY x.created_at`).all();
|
|
51
|
+
let projected = 0;
|
|
43
52
|
for (const row of missing) {
|
|
44
|
-
const
|
|
53
|
+
const aggregationKey = String(row.aggregation_key);
|
|
54
|
+
const projectId = aggregationKey.startsWith("project:") ? aggregationKey.slice(8) : void 0;
|
|
55
|
+
const policy = projectId ? row.project_id ? {
|
|
56
|
+
projectId,
|
|
57
|
+
mode: String(row.mode),
|
|
58
|
+
...row.quiet_hours_json ? { quietHours: JSON.parse(String(row.quiet_hours_json)) } : {},
|
|
59
|
+
allowedActions: JSON.parse(String(row.allowed_actions_json)),
|
|
60
|
+
confidenceThreshold: Number(row.confidence_threshold),
|
|
61
|
+
scenarios: [],
|
|
62
|
+
configured: true
|
|
63
|
+
} : {
|
|
64
|
+
projectId,
|
|
65
|
+
mode: "observe",
|
|
66
|
+
allowedActions: [],
|
|
67
|
+
confidenceThreshold: .75,
|
|
68
|
+
scenarios: [],
|
|
69
|
+
configured: false
|
|
70
|
+
} : {
|
|
71
|
+
projectId: "",
|
|
72
|
+
mode: "ask_before_action",
|
|
73
|
+
allowedActions: [],
|
|
74
|
+
confidenceThreshold: 0,
|
|
75
|
+
scenarios: [],
|
|
76
|
+
configured: false
|
|
77
|
+
};
|
|
78
|
+
const action = row.proposed_action_json ? JSON.parse(String(row.proposed_action_json)) : void 0;
|
|
79
|
+
const dispositionInput = {
|
|
80
|
+
confidence: Number(row.confidence),
|
|
81
|
+
valueScore: Number(row.value_score),
|
|
82
|
+
risk: action?.risk ?? "low",
|
|
83
|
+
...action ? { actionId: action.id } : {},
|
|
84
|
+
requiresApproval: !action && Boolean(row.decision_json)
|
|
85
|
+
};
|
|
86
|
+
const disposition = action && !projectId ? "show_in_work" : decideProactiveDisposition(policy, dispositionInput);
|
|
87
|
+
const reason = action && !projectId ? "Proposed project action cannot run outside a project scope" : proactiveDispositionReason(policy, dispositionInput, disposition);
|
|
88
|
+
const actionStatus = action ? disposition === "auto_execute" ? "pending" : disposition === "request_approval" ? "approval_required" : disposition === "show_in_work" ? "not_authorized" : null : null;
|
|
45
89
|
const nowIso = now.toISOString();
|
|
90
|
+
db.prepare(`UPDATE proactive_insights SET disposition = ?, disposition_reason = ?, action_status = ?,
|
|
91
|
+
disposition_at = ?, action_updated_at = ? WHERE insight_id = ? AND disposition IS NULL`).run(disposition, reason, actionStatus, nowIso, actionStatus ? nowIso : null, String(row.insight_id));
|
|
92
|
+
if (disposition === "record_silently") continue;
|
|
93
|
+
const id = randomUUID();
|
|
46
94
|
db.prepare(`INSERT INTO proactive_inbox_items (inbox_item_id, insight_id, status, created_at, updated_at)
|
|
47
|
-
VALUES (?, ?, 'unread', ?, ?)`).run(id, row.insight_id, nowIso, nowIso);
|
|
95
|
+
VALUES (?, ?, 'unread', ?, ?)`).run(id, String(row.insight_id), nowIso, nowIso);
|
|
96
|
+
const nextAttemptAt = nextQuietHoursEnd(policy.quietHours, now)?.toISOString() ?? nowIso;
|
|
48
97
|
db.prepare(`INSERT INTO proactive_delivery_outbox (delivery_id, inbox_item_id, status, attempt, next_attempt_at, created_at, updated_at)
|
|
49
|
-
VALUES (?, ?, 'pending', 0, ?, ?, ?)`).run(randomUUID(), id,
|
|
98
|
+
VALUES (?, ?, 'pending', 0, ?, ?, ?)`).run(randomUUID(), id, nextAttemptAt, nowIso, nowIso);
|
|
99
|
+
projected += 1;
|
|
50
100
|
}
|
|
51
|
-
return
|
|
101
|
+
return projected;
|
|
52
102
|
});
|
|
53
103
|
}
|
|
54
104
|
function wakeSnoozedItems(now = /* @__PURE__ */ new Date()) {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { listInbox, projectInsightsToInbox,
|
|
1
|
+
import { listInbox, projectInsightsToInbox, recordFeedback, wakeSnoozedItems } from './repository.js';
|
|
2
2
|
import type { InboxStatus } from './types.js';
|
|
3
3
|
export declare class ProactiveInboxService {
|
|
4
4
|
list: typeof listInbox;
|
|
5
5
|
project: typeof projectInsightsToInbox;
|
|
6
6
|
wakeSnoozed: typeof wakeSnoozedItems;
|
|
7
|
-
decide: typeof recordDecision;
|
|
8
7
|
feedback: typeof recordFeedback;
|
|
8
|
+
decide(id: string, choice: string, note?: string): import("./types.js").InboxItem;
|
|
9
9
|
instruct(id: string, instruction: string): {
|
|
10
10
|
revisionId: string;
|
|
11
11
|
};
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { publishInstructionFeedback } from "../scenarios/repository.js";
|
|
2
|
-
import {
|
|
2
|
+
import { resolveProactiveActionDecision } from "../actions/service.js";
|
|
3
|
+
import { getInboxItem, getInboxSubscriptionId, listInbox, projectInsightsToInbox, recordDecision, recordFeedback, transitionInboxItem, wakeSnoozedItems } from "./repository.js";
|
|
3
4
|
//#region src/proactive/inbox/service.ts
|
|
4
5
|
var ProactiveInboxService = class {
|
|
5
6
|
list = listInbox;
|
|
6
7
|
project = projectInsightsToInbox;
|
|
7
8
|
wakeSnoozed = wakeSnoozedItems;
|
|
8
|
-
decide = recordDecision;
|
|
9
9
|
feedback = recordFeedback;
|
|
10
|
+
decide(id, choice, note = "") {
|
|
11
|
+
const item = recordDecision(id, choice, note);
|
|
12
|
+
resolveProactiveActionDecision(id, choice);
|
|
13
|
+
return getInboxItem(id) ?? item;
|
|
14
|
+
}
|
|
10
15
|
instruct(id, instruction) {
|
|
11
16
|
return { revisionId: publishInstructionFeedback({
|
|
12
17
|
subscriptionId: getInboxSubscriptionId(id),
|
|
@@ -23,6 +23,20 @@ export interface InboxItem {
|
|
|
23
23
|
consequence: string;
|
|
24
24
|
}>;
|
|
25
25
|
};
|
|
26
|
+
proposedAction?: {
|
|
27
|
+
id: 'create_project_task';
|
|
28
|
+
risk: 'low';
|
|
29
|
+
rationale: string;
|
|
30
|
+
input: {
|
|
31
|
+
title: string;
|
|
32
|
+
objective: string;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
disposition: 'show_in_work' | 'request_approval' | 'auto_execute';
|
|
36
|
+
dispositionReason: string;
|
|
37
|
+
actionStatus?: 'not_authorized' | 'approval_required' | 'pending' | 'executing' | 'completed' | 'rejected' | 'failed';
|
|
38
|
+
actionResult?: Record<string, unknown>;
|
|
39
|
+
actionError?: string;
|
|
26
40
|
urgency: 'low' | 'medium' | 'high' | 'critical';
|
|
27
41
|
confidence: number;
|
|
28
42
|
valueScore: number;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
|
+
import { executePendingProactiveActions } from "../actions/service.js";
|
|
3
4
|
import { claimDelivery, finishDelivery, projectInsightsToInbox, recoverExpiredDeliveries, wakeSnoozedItems } from "./repository.js";
|
|
4
5
|
//#region src/proactive/inbox/worker.ts
|
|
5
6
|
init_logger();
|
|
@@ -28,6 +29,7 @@ var ProactiveInboxWorker = class {
|
|
|
28
29
|
this.running = true;
|
|
29
30
|
try {
|
|
30
31
|
projectInsightsToInbox();
|
|
32
|
+
executePendingProactiveActions();
|
|
31
33
|
wakeSnoozedItems();
|
|
32
34
|
recoverExpiredDeliveries();
|
|
33
35
|
const claim = claimDelivery();
|
|
@@ -5,8 +5,8 @@ Use only supplied evidence identifiers. Separate observations from inferences.
|
|
|
5
5
|
Do not invent dates, owners, dependencies, impact, or actions.
|
|
6
6
|
Return only the protected output schema.`;
|
|
7
7
|
const OUTPUT_CONTRACT = `Return JSON only with these fields:
|
|
8
|
-
{"title":"...","summary":"...","whyNow":"...","impact":"...","workDone":"What you investigated or ruled out","recommendation":"...","decision":{"question":"The exact user decision","options":[{"id":"stable-id","label":"short choice","consequence":"likely consequence"}]},"urgency":"low|medium|high|critical","confidence":0.0,"evidenceIds":["event-id"]}
|
|
9
|
-
Use decision only when the user must choose; otherwise set decision to null. Provide 2-3 mutually exclusive options. If evidence does not support a timely and actionable insight, return the same shape with urgency "low" and confidence below 0.65. Never add unknown fields.`;
|
|
8
|
+
{"title":"...","summary":"...","whyNow":"...","impact":"...","workDone":"What you investigated or ruled out","recommendation":"...","decision":{"question":"The exact user decision","options":[{"id":"stable-id","label":"short choice","consequence":"likely consequence"}]},"proposedAction":{"id":"create_project_task","risk":"low","rationale":"why this is safe and useful","input":{"title":"...","objective":"..."}},"urgency":"low|medium|high|critical","confidence":0.0,"evidenceIds":["event-id"]}
|
|
9
|
+
Use proposedAction only when the evidence clearly supports creating a follow-up task in the current project; otherwise set it to null. A proposed action must include exactly two decision options in this order: approve, reject. Use decision without proposedAction only when the user must choose; otherwise set decision to null. Provide 2-3 mutually exclusive options. If evidence does not support a timely and actionable insight, return the same shape with urgency "low" and confidence below 0.65. Never add unknown fields.`;
|
|
10
10
|
function composeScenarioPrompt(input) {
|
|
11
11
|
const parts = {
|
|
12
12
|
platformSafety: PLATFORM_SAFETY,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
ALTER TABLE proactive_insights ADD COLUMN proposed_action_json TEXT;
|
|
2
|
+
ALTER TABLE proactive_insights ADD COLUMN disposition TEXT
|
|
3
|
+
CHECK (disposition IN ('record_silently', 'show_in_work', 'request_approval', 'auto_execute'));
|
|
4
|
+
ALTER TABLE proactive_insights ADD COLUMN disposition_reason TEXT;
|
|
5
|
+
ALTER TABLE proactive_insights ADD COLUMN action_status TEXT
|
|
6
|
+
CHECK (action_status IN ('not_authorized', 'approval_required', 'pending', 'executing', 'completed', 'rejected', 'failed'));
|
|
7
|
+
ALTER TABLE proactive_insights ADD COLUMN action_result_json TEXT;
|
|
8
|
+
ALTER TABLE proactive_insights ADD COLUMN action_error TEXT;
|
|
9
|
+
ALTER TABLE proactive_insights ADD COLUMN disposition_at TEXT;
|
|
10
|
+
ALTER TABLE proactive_insights ADD COLUMN action_updated_at TEXT;
|
|
11
|
+
|
|
12
|
+
CREATE INDEX idx_proactive_insights_action_status
|
|
13
|
+
ON proactive_insights(action_status, created_at);
|
|
@@ -3,7 +3,7 @@ import type { ApplyMigrationsOptions } from './types.js';
|
|
|
3
3
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
4
4
|
export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
5
5
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
6
|
-
export declare const XOPC_DB_SCHEMA_VERSION =
|
|
6
|
+
export declare const XOPC_DB_SCHEMA_VERSION = 119;
|
|
7
7
|
export declare function resolveMigrationsDir(override?: string): string;
|
|
8
8
|
/**
|
|
9
9
|
* Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
|
|
@@ -12,7 +12,7 @@ const log = createLogger("Sqlite:Migrations");
|
|
|
12
12
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
13
13
|
const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
14
14
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
15
|
-
const XOPC_DB_SCHEMA_VERSION =
|
|
15
|
+
const XOPC_DB_SCHEMA_VERSION = 119;
|
|
16
16
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
17
17
|
function backupBeforeTaskCutover(db, databasePath) {
|
|
18
18
|
db.exec("PRAGMA wal_checkpoint(FULL)");
|
|
@@ -57,7 +57,7 @@ function applySingleMigration(db, migration) {
|
|
|
57
57
|
* Each step runs in its own transaction and updates schema_meta on success.
|
|
58
58
|
*/
|
|
59
59
|
function applyPendingMigrations(db, options = {}) {
|
|
60
|
-
const targetVersion = options.targetVersion ??
|
|
60
|
+
const targetVersion = options.targetVersion ?? 119;
|
|
61
61
|
let currentVersion = readSchemaVersion(db);
|
|
62
62
|
if (currentVersion > targetVersion) throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
|
|
63
63
|
if (currentVersion >= targetVersion) return currentVersion;
|
|
@@ -94,7 +94,7 @@ function applyPendingMigrations(db, options = {}) {
|
|
|
94
94
|
}
|
|
95
95
|
/** Inspect schema version without mutating the database. */
|
|
96
96
|
function inspectSchemaMigrationStatus(db, options = {}) {
|
|
97
|
-
const appVersion = options.targetVersion ??
|
|
97
|
+
const appVersion = options.targetVersion ?? 119;
|
|
98
98
|
const dbVersion = readSchemaVersion(db);
|
|
99
99
|
if (dbVersion > appVersion) return {
|
|
100
100
|
dbVersion,
|
|
@@ -19,7 +19,7 @@ const log = createLogger('Sqlite:Migrations');
|
|
|
19
19
|
export const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
20
20
|
|
|
21
21
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
22
|
-
export const XOPC_DB_SCHEMA_VERSION =
|
|
22
|
+
export const XOPC_DB_SCHEMA_VERSION = 119;
|
|
23
23
|
|
|
24
24
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
25
25
|
|
|
@@ -105,6 +105,11 @@ export declare function buildHomeBriefing(input: {
|
|
|
105
105
|
recommendation: string;
|
|
106
106
|
confidence: number;
|
|
107
107
|
valueScore?: number;
|
|
108
|
+
disposition?: "show_in_work" | "request_approval" | "auto_execute";
|
|
109
|
+
dispositionReason?: string;
|
|
110
|
+
actionStatus?: "failed" | "completed" | "rejected" | "pending" | "approval_required" | "not_authorized" | "executing";
|
|
111
|
+
proposedActionTitle?: string;
|
|
112
|
+
actionError?: string;
|
|
108
113
|
decision?: {
|
|
109
114
|
question: string;
|
|
110
115
|
options: {
|
|
@@ -451,7 +451,7 @@ var HomeQueryService = class {
|
|
|
451
451
|
kind: "agent_judgment",
|
|
452
452
|
title: item.insight.title,
|
|
453
453
|
detail: item.insight.summary,
|
|
454
|
-
reason: "decision_needed",
|
|
454
|
+
reason: item.insight.disposition === "request_approval" ? "approval_required" : "decision_needed",
|
|
455
455
|
urgency: item.insight.urgency === "critical" || item.insight.urgency === "high" ? "now" : "soon",
|
|
456
456
|
href: `/?judgment=${encodeURIComponent(item.id)}`,
|
|
457
457
|
updatedAt: Date.parse(item.updatedAt),
|
|
@@ -463,7 +463,12 @@ var HomeQueryService = class {
|
|
|
463
463
|
recommendation: item.insight.recommendation,
|
|
464
464
|
confidence: item.insight.confidence,
|
|
465
465
|
valueScore: item.insight.valueScore,
|
|
466
|
-
|
|
466
|
+
disposition: item.insight.disposition,
|
|
467
|
+
dispositionReason: item.insight.dispositionReason,
|
|
468
|
+
...item.insight.actionStatus ? { actionStatus: item.insight.actionStatus } : {},
|
|
469
|
+
...item.insight.proposedAction ? { proposedActionTitle: item.insight.proposedAction.input.title } : {},
|
|
470
|
+
...item.insight.actionError ? { actionError: item.insight.actionError } : {},
|
|
471
|
+
...item.insight.decision && (!item.insight.proposedAction || item.insight.actionStatus === "approval_required") ? { decision: item.insight.decision } : {}
|
|
467
472
|
}
|
|
468
473
|
})),
|
|
469
474
|
...activeTasks.map((task) => decisionFromTask(this.#projector.project(task), task.projectId ? projectsById.get(task.projectId)?.name : void 0)).filter((item) => Boolean(item)),
|
|
@@ -11,6 +11,7 @@ export * from './project-operating-view-service.js';
|
|
|
11
11
|
export * from './project-monitoring-service.js';
|
|
12
12
|
export * from './task-value-metrics-service.js';
|
|
13
13
|
export * from './task-application-service.js';
|
|
14
|
+
export * from './task-change-events.js';
|
|
14
15
|
export * from './task-context-repository.js';
|
|
15
16
|
export * from './task-dependency-service.js';
|
|
16
17
|
export * from './task-read-model-projector.js';
|
package/dist/src/tasks/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { resolveExecutionContext } from "./execution-context.js";
|
|
|
5
5
|
import { AttentionGovernor } from "./attention-governor.js";
|
|
6
6
|
import { HomeQueryService, buildHomeBriefing, buildHomeFocusItems, decisionFromTask } from "./home-query-service.js";
|
|
7
7
|
import { TaskContextRepository } from "./task-context-repository.js";
|
|
8
|
+
import { enqueueTaskChanged, enqueueTaskChangedEvent } from "./task-change-events.js";
|
|
8
9
|
import { TaskDependencyError, TaskDependencyService } from "./task-dependency-service.js";
|
|
9
10
|
import { TaskApplicationService } from "./task-application-service.js";
|
|
10
11
|
import { TaskWorkflowCoordinator } from "./task-workflow-coordinator.js";
|
|
@@ -12,10 +13,10 @@ import { defineTaskContract } from "./task-contract-definition.js";
|
|
|
12
13
|
import { ModelTaskContractPlanner, parseTaskContractResponse } from "./task-contract-planner.js";
|
|
13
14
|
import { assembleTaskContext, buildTaskExecutionDirective, getTaskContextManifest, getTaskExecutionBrief } from "./task-context-assembler.js";
|
|
14
15
|
import { TaskRunCoordinator } from "./task-run-coordinator.js";
|
|
15
|
-
import { ProjectMonitoringService, decideProactiveDisposition } from "./project-monitoring-service.js";
|
|
16
|
-
import { ProjectOperatingViewService,
|
|
16
|
+
import { ProjectMonitoringService, decideProactiveDisposition, isInQuietHours, nextQuietHoursEnd, proactiveDispositionReason } from "./project-monitoring-service.js";
|
|
17
|
+
import { ProjectOperatingViewService, summarizeProjectOperatingView } from "./project-operating-view-service.js";
|
|
17
18
|
import { TaskValueMetricsService } from "./task-value-metrics-service.js";
|
|
18
19
|
import { TaskOutboxDispatcher } from "./task-outbox-dispatcher.js";
|
|
19
20
|
import { TaskRunDispatcher } from "./task-run-dispatcher.js";
|
|
20
21
|
import { TaskSignalService } from "./task-signal-service.js";
|
|
21
|
-
export { AttentionGovernor, HomeQueryService, ModelTaskContractPlanner, ProjectMonitoringService, ProjectOperatingViewService, TaskApplicationService, TaskContextRepository, TaskDependencyError, TaskDependencyService, TaskOutboxDispatcher, TaskReadModelProjector, TaskRepository, TaskRunCoordinator, TaskRunDispatcher, TaskRunRepository, TaskSignalService, TaskValueMetricsService, TaskWorkflowCoordinator, assembleTaskContext, buildHomeBriefing, buildHomeFocusItems, buildTaskExecutionDirective, decideProactiveDisposition, decisionFromTask, defineTaskContract, getTaskContextManifest, getTaskExecutionBrief, parseTaskContractResponse,
|
|
22
|
+
export { AttentionGovernor, HomeQueryService, ModelTaskContractPlanner, ProjectMonitoringService, ProjectOperatingViewService, TaskApplicationService, TaskContextRepository, TaskDependencyError, TaskDependencyService, TaskOutboxDispatcher, TaskReadModelProjector, TaskRepository, TaskRunCoordinator, TaskRunDispatcher, TaskRunRepository, TaskSignalService, TaskValueMetricsService, TaskWorkflowCoordinator, assembleTaskContext, buildHomeBriefing, buildHomeFocusItems, buildTaskExecutionDirective, decideProactiveDisposition, decisionFromTask, defineTaskContract, enqueueTaskChanged, enqueueTaskChangedEvent, getTaskContextManifest, getTaskExecutionBrief, isInQuietHours, nextQuietHoursEnd, parseTaskContractResponse, proactiveDispositionReason, resolveExecutionContext, summarizeProjectOperatingView };
|
|
@@ -7,6 +7,15 @@ export declare function decideProactiveDisposition(policy: ProjectMonitoringPoli
|
|
|
7
7
|
actionId?: string;
|
|
8
8
|
requiresApproval?: boolean;
|
|
9
9
|
}): ProactiveDisposition;
|
|
10
|
+
export declare function proactiveDispositionReason(policy: ProjectMonitoringPolicy, input: {
|
|
11
|
+
confidence: number;
|
|
12
|
+
valueScore: number;
|
|
13
|
+
risk: 'low' | 'medium' | 'high';
|
|
14
|
+
actionId?: string;
|
|
15
|
+
requiresApproval?: boolean;
|
|
16
|
+
}, disposition: ProactiveDisposition): string;
|
|
17
|
+
export declare function isInQuietHours(quietHours: QuietHours | undefined, now?: Date): boolean;
|
|
18
|
+
export declare function nextQuietHoursEnd(quietHours: QuietHours | undefined, now?: Date): Date | undefined;
|
|
10
19
|
export declare class ProjectMonitoringService {
|
|
11
20
|
#private;
|
|
12
21
|
get(projectId: string): ProjectMonitoringPolicy;
|
|
@@ -2,6 +2,7 @@ import { getSqliteDatabase, runSqliteWriteTransaction } from "../storage/sqlite/
|
|
|
2
2
|
import { ProactiveScenarioService } from "../proactive/scenarios/service.js";
|
|
3
3
|
//#region src/tasks/project-monitoring-service.ts
|
|
4
4
|
const DEFAULT_SCENARIOS = ["project_delivery_risk", "blocked_work"];
|
|
5
|
+
const SUPPORTED_ACTIONS = new Set(["create_project_task"]);
|
|
5
6
|
function validHour(value) {
|
|
6
7
|
return Number.isInteger(value) && value >= 0 && value <= 23;
|
|
7
8
|
}
|
|
@@ -19,10 +20,45 @@ function rowToPolicy(row, scenarios) {
|
|
|
19
20
|
}
|
|
20
21
|
function decideProactiveDisposition(policy, input) {
|
|
21
22
|
if (policy.mode === "observe" || input.confidence < policy.confidenceThreshold || input.valueScore < .5) return "record_silently";
|
|
22
|
-
if (input.
|
|
23
|
-
|
|
23
|
+
if (input.actionId) {
|
|
24
|
+
if (input.requiresApproval || input.risk !== "low" || policy.mode === "ask_before_action") return "request_approval";
|
|
25
|
+
if (policy.mode === "auto_low_risk" && policy.allowedActions.includes(input.actionId)) return "auto_execute";
|
|
26
|
+
return "show_in_work";
|
|
27
|
+
}
|
|
28
|
+
if (input.requiresApproval) return "request_approval";
|
|
24
29
|
return "show_in_work";
|
|
25
30
|
}
|
|
31
|
+
function proactiveDispositionReason(policy, input, disposition) {
|
|
32
|
+
if (policy.mode === "observe") return "Project monitoring is observe-only";
|
|
33
|
+
if (input.confidence < policy.confidenceThreshold) return `Confidence ${input.confidence} is below threshold ${policy.confidenceThreshold}`;
|
|
34
|
+
if (input.valueScore < .5) return `Value score ${input.valueScore} is below 0.5`;
|
|
35
|
+
if (disposition === "request_approval") return input.risk === "low" ? "Project policy requires approval before acting" : `${input.risk} risk requires approval`;
|
|
36
|
+
if (disposition === "auto_execute") return `Low-risk action ${input.actionId} is explicitly allowed`;
|
|
37
|
+
if (input.actionId && !policy.allowedActions.includes(input.actionId)) return `Action ${input.actionId} is not in the project allowlist`;
|
|
38
|
+
return "Insight passed the project confidence and value gates";
|
|
39
|
+
}
|
|
40
|
+
function hourInTimezone(now, timezone) {
|
|
41
|
+
const hour = new Intl.DateTimeFormat("en-US", {
|
|
42
|
+
timeZone: timezone,
|
|
43
|
+
hour: "2-digit",
|
|
44
|
+
hourCycle: "h23"
|
|
45
|
+
}).formatToParts(now).find((part) => part.type === "hour")?.value;
|
|
46
|
+
return Number(hour);
|
|
47
|
+
}
|
|
48
|
+
function isInQuietHours(quietHours, now = /* @__PURE__ */ new Date()) {
|
|
49
|
+
if (!quietHours || quietHours.startHour === quietHours.endHour) return false;
|
|
50
|
+
const hour = hourInTimezone(now, quietHours.timezone);
|
|
51
|
+
return quietHours.startHour < quietHours.endHour ? hour >= quietHours.startHour && hour < quietHours.endHour : hour >= quietHours.startHour || hour < quietHours.endHour;
|
|
52
|
+
}
|
|
53
|
+
function nextQuietHoursEnd(quietHours, now = /* @__PURE__ */ new Date()) {
|
|
54
|
+
if (!isInQuietHours(quietHours, now)) return void 0;
|
|
55
|
+
const minute = new Date(now);
|
|
56
|
+
minute.setUTCSeconds(0, 0);
|
|
57
|
+
for (let offset = 1; offset <= 2880; offset += 1) {
|
|
58
|
+
const candidate = new Date(minute.getTime() + offset * 6e4);
|
|
59
|
+
if (!isInQuietHours(quietHours, candidate)) return candidate;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
26
62
|
var ProjectMonitoringService = class {
|
|
27
63
|
#scenarios = new ProactiveScenarioService();
|
|
28
64
|
get(projectId) {
|
|
@@ -40,9 +76,11 @@ var ProjectMonitoringService = class {
|
|
|
40
76
|
configure(input) {
|
|
41
77
|
if (!getSqliteDatabase().prepare("SELECT 1 FROM projects WHERE project_id = ?").get(input.projectId)) throw new Error("Project not found");
|
|
42
78
|
if (input.quietHours && (!validHour(input.quietHours.startHour) || !validHour(input.quietHours.endHour) || !input.quietHours.timezone.trim())) throw new Error("Quiet hours need a timezone and integer hours from 0 through 23");
|
|
79
|
+
if (input.quietHours) hourInTimezone(/* @__PURE__ */ new Date(), input.quietHours.timezone);
|
|
43
80
|
const threshold = input.confidenceThreshold ?? .75;
|
|
44
81
|
if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) throw new Error("confidenceThreshold must be between 0 and 1");
|
|
45
82
|
const allowedActions = [...new Set(input.allowedActions ?? [])].map((action) => action.trim()).filter(Boolean);
|
|
83
|
+
if (allowedActions.some((action) => !SUPPORTED_ACTIONS.has(action))) throw new Error("Unknown proactive action");
|
|
46
84
|
const scenarios = [...new Set(input.scenarios ?? DEFAULT_SCENARIOS)];
|
|
47
85
|
const availableScenarios = new Set(this.#scenarios.list().map((scenario) => scenario.key));
|
|
48
86
|
if (scenarios.some((scenario) => !availableScenarios.has(scenario))) throw new Error("Unknown proactive scenario");
|
|
@@ -73,4 +111,4 @@ var ProjectMonitoringService = class {
|
|
|
73
111
|
}
|
|
74
112
|
};
|
|
75
113
|
//#endregion
|
|
76
|
-
export { ProjectMonitoringService, decideProactiveDisposition };
|
|
114
|
+
export { ProjectMonitoringService, decideProactiveDisposition, isInQuietHours, nextQuietHoursEnd, proactiveDispositionReason };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { ProjectOperatingSummary, ProjectOperatingView
|
|
1
|
+
import type { ProjectOperatingSummary, ProjectOperatingView } from '@xopcai/gateway-contract';
|
|
2
2
|
import type { ProjectService } from '../projects/index.js';
|
|
3
|
-
export declare function projectTaskLane(card: Pick<ProjectTaskCard, 'phase' | 'operationalState' | 'attention'>): ProjectTaskCard['lane'];
|
|
4
3
|
export declare function summarizeProjectOperatingView(view: ProjectOperatingView): ProjectOperatingSummary;
|
|
5
4
|
export declare class ProjectOperatingViewService {
|
|
6
5
|
#private;
|
|
@@ -4,17 +4,6 @@ import { TaskReadModelProjector } from "./task-read-model-projector.js";
|
|
|
4
4
|
import { TaskDependencyService } from "./task-dependency-service.js";
|
|
5
5
|
import { ProjectMonitoringService } from "./project-monitoring-service.js";
|
|
6
6
|
//#region src/tasks/project-operating-view-service.ts
|
|
7
|
-
function projectTaskLane(card) {
|
|
8
|
-
if (card.phase === "closed") return "done";
|
|
9
|
-
if (card.attention.some((item) => item.kind === "input_required" || item.kind === "approval_required")) return "needs_user";
|
|
10
|
-
if ([
|
|
11
|
-
"queued",
|
|
12
|
-
"running",
|
|
13
|
-
"verifying"
|
|
14
|
-
].includes(card.operationalState)) return "moving";
|
|
15
|
-
if (card.operationalState === "waiting" || card.operationalState === "blocked") return "waiting";
|
|
16
|
-
return "ready";
|
|
17
|
-
}
|
|
18
7
|
function summarizeProjectOperatingView(view) {
|
|
19
8
|
const counts = {
|
|
20
9
|
ready: 0,
|
|
@@ -23,8 +12,20 @@ function summarizeProjectOperatingView(view) {
|
|
|
23
12
|
needsUser: 0,
|
|
24
13
|
done: 0
|
|
25
14
|
};
|
|
26
|
-
for (const task of view.tasks)
|
|
27
|
-
|
|
15
|
+
for (const task of view.tasks) {
|
|
16
|
+
if (task.attention.some((item) => item.kind === "input_required" || item.kind === "approval_required")) {
|
|
17
|
+
counts.needsUser += 1;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (task.phase === "closed") counts.done += 1;
|
|
21
|
+
else if ([
|
|
22
|
+
"queued",
|
|
23
|
+
"running",
|
|
24
|
+
"verifying"
|
|
25
|
+
].includes(task.operationalState)) counts.moving += 1;
|
|
26
|
+
else if (task.operationalState === "waiting" || task.operationalState === "blocked") counts.waiting += 1;
|
|
27
|
+
else counts.ready += 1;
|
|
28
|
+
}
|
|
28
29
|
return {
|
|
29
30
|
...view.digest,
|
|
30
31
|
counts,
|
|
@@ -48,7 +49,7 @@ var ProjectOperatingViewService = class {
|
|
|
48
49
|
const model = this.#projector.project(task);
|
|
49
50
|
const latest = this.#runs.getLatestRoot(task.id);
|
|
50
51
|
const receipt = latest ? this.#runs.getReceipt(latest.id) : void 0;
|
|
51
|
-
|
|
52
|
+
return {
|
|
52
53
|
id: task.id,
|
|
53
54
|
title: task.title,
|
|
54
55
|
phase: task.phase,
|
|
@@ -63,20 +64,22 @@ var ProjectOperatingViewService = class {
|
|
|
63
64
|
...receipt ? { latestVerification: receipt.verification.status } : {},
|
|
64
65
|
updatedAt: task.updatedAt
|
|
65
66
|
};
|
|
66
|
-
return {
|
|
67
|
-
...base,
|
|
68
|
-
lane: projectTaskLane(base)
|
|
69
|
-
};
|
|
70
67
|
});
|
|
71
68
|
const recentResults = projectTasks.flatMap((task) => this.#runs.listReceipts(task.id, 10).map((receipt) => ({
|
|
72
69
|
taskId: task.id,
|
|
73
70
|
taskTitle: task.title,
|
|
74
71
|
receipt
|
|
75
72
|
}))).sort((a, b) => b.receipt.finalizedAt - a.receipt.finalizedAt).slice(0, 10);
|
|
76
|
-
const
|
|
73
|
+
const needsUser = (card) => card.attention.some((item) => item.kind === "input_required" || item.kind === "approval_required");
|
|
74
|
+
const moving = (card) => [
|
|
75
|
+
"queued",
|
|
76
|
+
"running",
|
|
77
|
+
"verifying"
|
|
78
|
+
].includes(card.operationalState);
|
|
79
|
+
const attentionCount = cards.filter((card) => card.attention.length > 0).length;
|
|
77
80
|
return {
|
|
78
81
|
project,
|
|
79
|
-
tasks: cards
|
|
82
|
+
tasks: cards,
|
|
80
83
|
dependencyEdges: this.#dependencies.listProjectEdges(project.id),
|
|
81
84
|
blockers: cards.flatMap((card) => card.attention.map((item) => ({
|
|
82
85
|
id: item.sourceId ?? `${card.id}:${item.kind}`,
|
|
@@ -91,12 +94,12 @@ var ProjectOperatingViewService = class {
|
|
|
91
94
|
recentResults,
|
|
92
95
|
digest: {
|
|
93
96
|
health: cards.length === 0 ? "empty" : attentionCount > 0 ? "attention" : "healthy",
|
|
94
|
-
summary: cards.length === 0 ? "No tasks yet" : `${cards.filter(
|
|
95
|
-
recommendedAction: cards.find((card) => card.
|
|
97
|
+
summary: cards.length === 0 ? "No tasks yet" : `${cards.filter(moving).length} moving, ${attentionCount} need attention`,
|
|
98
|
+
recommendedAction: cards.find(needsUser)?.attention[0]?.summary ?? cards.find((card) => card.attention.length > 0)?.attention[0]?.summary ?? cards.find(moving)?.title ?? cards.find((card) => card.phase === "ready")?.title ?? cards.find((card) => card.phase === "backlog")?.title
|
|
96
99
|
},
|
|
97
100
|
monitoring: this.#monitoring.get(project.id)
|
|
98
101
|
};
|
|
99
102
|
}
|
|
100
103
|
};
|
|
101
104
|
//#endregion
|
|
102
|
-
export { ProjectOperatingViewService,
|
|
105
|
+
export { ProjectOperatingViewService, summarizeProjectOperatingView };
|