@xopcai/xopc 0.0.284 → 0.0.285
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-CwbIMGLY.js → activity-store-B0BTIk9j.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DjiOld-S.js → agent-avatar-dicebear-Dc-0QZTQ.js} +1 -1
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-CcmqK1Sc.js +1 -0
- package/dist/gateway/static/root/assets/agents-B_7PVt7S.js +1 -0
- package/dist/gateway/static/root/assets/api-COG_Tzpv.js +1 -0
- package/dist/gateway/static/root/assets/{api-3lphBVuY.js → api-Coly9ab6.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-B2nwCXxE.js → app-management-settings-panel-TWBO-msg.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-BXO4Ej88.js → appearance-settings-CpTZ2KuT.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-6qxxKFsK.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-6ZRqNjla.js → archive-plugin-z_u6alob.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-CmPZalcW.js → attachment-load-C2nS04TC.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-Bc3WbK5n.js → automations-page-aIBsw9zR.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-BRQlU4qk.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-OWR6cxpc.js → binary-plugins-CGmXoZnl.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-Ca-tDQXm.js → block-editor-CtvzCZhC.js} +3 -3
- package/dist/gateway/static/root/assets/{browser-automation-inputs-Dxy2_p3U.js → browser-automation-inputs-BOTU1isH.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-CibQIxpi.js → browser-automations-page-BwU5V8J0.js} +2 -2
- package/dist/gateway/static/root/assets/browser-settings-page-DDjbDeHQ.js +8 -0
- package/dist/gateway/static/root/assets/capabilities-settings-panel-DM1Bcf1x.js +8 -0
- package/dist/gateway/static/root/assets/channels-settings-Bq36CUxL.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-Bh-G9mdZ.js → channels-status-swr-Dfi0Desh.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-lbzbSpV3.js → chat-terminal-dock-B0MvcO6j.js} +2 -2
- package/dist/gateway/static/root/assets/connectors-page-L8JehrxF.js +2 -0
- package/dist/gateway/static/root/assets/{date-picker-tYvbTfZT.js → date-picker-DXOvbHCr.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-B8yo7-7V.js → dependency-picker-BEnC-eS0.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-BUGkGB4-.js → desktop-pet-DtPNvznL.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-DNeVGZNp.js → desktop-pet-settings-CYyjcSn6.js} +1 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard-BPMGvAKE.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-CiK0QI2p.js → extension-debug-page-H_aPMGXW.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CirPNHJd.js → extension-page-C1JnpeMt.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-Baua1_Le.js → extension-settings-page-C75kCOqe.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-Co4hHe0i.js → gateway-config-swr-G2lQt7Ak.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-BCF732TU.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-DBPNJG-e.js → gateway-startup-retry-GP31PUrG.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-DTM_kYpE.js → heartbeat-settings-C7fjiowt.js} +1 -1
- package/dist/gateway/static/root/assets/home-page-DZXeXdUL.js +1 -0
- package/dist/gateway/static/root/assets/{image-generation-api-D0rMTumD.js → image-generation-api-CKFhfHSw.js} +1 -1
- package/dist/gateway/static/root/assets/imports-page-GROumhj8.js +1 -0
- package/dist/gateway/static/root/assets/{index-C9aQA4Dr.js → index-ChIrvv3t.js} +47 -47
- package/dist/gateway/static/root/assets/index-R1VNlHjj.css +2 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CMUaF61L.js → keyboard-shortcuts-settings-zcI822m_.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-CAEKGTAg.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-CHxyT09t.js → local-apps-page-BtgDJ92Q.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-DO6JJ1l2.js → locale-store-DlOv7896.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-DHZ4BEkG.js +2 -0
- package/dist/gateway/static/root/assets/{management-settings-jXbA2S54.js → management-settings-DasZxCyD.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-view-CKInRsDh.js → markdown-view-6oYdBahT.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-BaPkSvW7.js → media-plugins-CxthbBvk.js} +1 -1
- package/dist/gateway/static/root/assets/{messages-BgZRsCkG.js → messages-Ls1Aaiqd.js} +1 -1
- package/dist/gateway/static/root/assets/note-detail-panel-BcWq3ddw.js +5 -0
- package/dist/gateway/static/root/assets/notes-page-DfZ8QWyv.js +8 -0
- package/dist/gateway/static/root/assets/{page-tabs-CdYemUOu.js → page-tabs-BG39KidE.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-DzgF1yYO.js → preview-open-alternatives-Bevpbq9f.js} +1 -1
- package/dist/gateway/static/root/assets/proactive-page-D2U_xQaS.js +1 -0
- package/dist/gateway/static/root/assets/{product-open-page-iQ-eW6Aj.js → product-open-page-Bi1seZpM.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-CzlGTW4F.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-mQsM-TsP.js +1 -0
- package/dist/gateway/static/root/assets/{registry-api-DMNudT60.js → registry-api-D0ddwBFj.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-Cb56JcO1.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-u_wVepf7.js → runtime-tools-settings-panel-Wkru8_My.js} +2 -2
- package/dist/gateway/static/root/assets/{schema-form-YdZj2xdI.js → schema-form-CSMRzj9Y.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-Hj9uFcIO.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-CV4-_EVA.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-Bw6xKOX5.js → settings-form-section-wcAdCkK8.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-CV8kVdnE.js → settings-page-BcQGAnCs.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-DzxmsaQG.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-BHwjHlrV.js → share-preview-page-DQocUl-1.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-xlJh1ccG.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-wSXJRSxX.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-DRKdb6O5.js → skills-page-AmqtBJo7.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page.utils-DYfaLtts.js → skills-page.utils-DtlfCgJp.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-CWrWpKV-.js → system-settings-panel-Ct698Nf-.js} +1 -1
- package/dist/gateway/static/root/assets/task-detail-page-sQpwq5-c.js +1 -0
- package/dist/gateway/static/root/assets/{theme-store-CDc7Wqy5.js → theme-store-ng8KJsAT.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-Bmsy-5Fs.js → tunnel-api-Boajol0M.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-D-FRib85.js → use-autosave-BSyxRHt2.js} +1 -1
- package/dist/gateway/static/root/assets/user-model-page-DIEHnAER.js +2 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-iQ7EBzII.js → voice-api-key-field-SE3OQI1s.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-DNMWA2tx.js → voice-config-api-VNuCmdFe.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-BesuhDlH.js → work-discovery-overlay-Dn1vy2oq.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Du81FoCy.js → workflow-run-setup-panel-BJEfGY-k.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-DgRNIyr7.js +6 -0
- package/dist/gateway/static/root/index.html +10 -10
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/proactive.js +13 -4
- package/dist/src/agent/coding/repository-instructions.js +1 -1
- package/dist/src/agent/coding/workspace-revision.js +1 -1
- package/dist/src/agent/commands/command-isolation.js +1 -1
- package/dist/src/agent/tools/factory.js +7 -3
- package/dist/src/agent/tools/review-workspace.js +1 -1
- package/dist/src/browser/providers/browser-ext-install.js +10 -2
- package/dist/src/cli/command-manifest.js +2 -2
- package/dist/src/cli/commands/agent.js +2 -2
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +2 -2
- package/dist/src/cli/commands/gateway/run-foreground.js +3 -0
- package/dist/src/cli/commands/import.js +54 -6
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/cli/commands/platform.js +1 -1
- package/dist/src/cli/commands/profile.js +1 -1
- package/dist/src/cli/commands/tunnel.js +1 -1
- package/dist/src/cli/commands/update.js +1 -1
- package/dist/src/cli/extension-cli-register.js +1 -1
- package/dist/src/config/index.js +1 -1
- package/dist/src/discussions/action-tasks.js +1 -1
- package/dist/src/discussions/edits.js +1 -1
- package/dist/src/discussions/index.js +1 -1
- package/dist/src/discussions/organizer.js +1 -1
- package/dist/src/discussions/recording.js +1 -1
- package/dist/src/discussions/sealer.js +1 -1
- package/dist/src/discussions/service.js +2 -2
- package/dist/src/endpoint-tools/invocation-service.d.ts +2 -0
- package/dist/src/endpoint-tools/invocation-service.js +48 -19
- package/dist/src/gateway/heartbeat/check-store.d.ts +18 -0
- package/dist/src/gateway/heartbeat/check-store.js +73 -0
- package/dist/src/gateway/heartbeat/service.d.ts +12 -3
- package/dist/src/gateway/heartbeat/service.js +62 -45
- package/dist/src/gateway/hono/routes/discussions.js +2 -2
- package/dist/src/gateway/hono/routes/imports.js +18 -6
- package/dist/src/gateway/hono/routes/proactive-control.js +2 -1
- package/dist/src/gateway/run-loop.js +1 -1
- package/dist/src/gateway/service.d.ts +7 -0
- package/dist/src/gateway/service.js +24 -9
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/imports/importService.d.ts +17 -12
- package/dist/src/imports/importService.js +67 -70
- package/dist/src/imports/inventory.d.ts +11 -0
- package/dist/src/imports/inventory.js +202 -0
- package/dist/src/imports/productImport.d.ts +4 -4
- package/dist/src/imports/productImport.js +201 -108
- package/dist/src/imports/runtime.js +7 -2
- package/dist/src/imports/scanner.d.ts +1 -1
- package/dist/src/imports/scanner.js +4 -4
- package/dist/src/imports/types.d.ts +53 -0
- package/dist/src/infra/background-task.d.ts +2 -0
- package/dist/src/infra/background-task.js +25 -0
- package/dist/src/infra/process-diagnostics.d.ts +5 -0
- package/dist/src/infra/process-diagnostics.js +97 -0
- package/dist/src/infra/unhandled-rejections.js +6 -3
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/knowledge/ingestion-service.js +1 -0
- package/dist/src/media-shared/http/ssrf-guard.js +1 -1
- package/dist/src/notifications/proactive-policy.js +7 -5
- package/dist/src/proactive/execution/authorization.d.ts +2 -0
- package/dist/src/proactive/execution/authorization.js +10 -1
- package/dist/src/proactive/execution/context.js +1 -0
- package/dist/src/proactive/execution/repository.d.ts +2 -0
- package/dist/src/proactive/execution/repository.js +19 -2
- package/dist/src/proactive/execution/worker.js +14 -4
- package/dist/src/proactive/experience.d.ts +4 -1
- package/dist/src/proactive/experience.js +3 -0
- package/dist/src/proactive/follow-ups.d.ts +5 -0
- package/dist/src/proactive/follow-ups.js +18 -7
- package/dist/src/proactive/inbox/cards.js +2 -1
- package/dist/src/proactive/inbox/delivery.js +6 -7
- package/dist/src/proactive/inbox/digest.d.ts +1 -1
- package/dist/src/proactive/inbox/digest.js +23 -13
- package/dist/src/proactive/inbox/notification-policy.d.ts +1 -1
- package/dist/src/proactive/inbox/notification-policy.js +9 -6
- package/dist/src/proactive/inbox/repository.js +3 -2
- package/dist/src/proactive/inbox/service.d.ts +6 -0
- package/dist/src/proactive/inbox/service.js +18 -0
- package/dist/src/proactive/inbox/types.d.ts +1 -0
- package/dist/src/proactive/policy/presence.d.ts +1 -1
- package/dist/src/proactive/policy/presence.js +7 -5
- package/dist/src/proactive/policy/service.d.ts +8 -5
- package/dist/src/proactive/policy/service.js +12 -12
- package/dist/src/proactive/scenarios/control.d.ts +3 -3
- package/dist/src/proactive/scenarios/control.js +12 -2
- package/dist/src/proactive/source-freshness.d.ts +8 -0
- package/dist/src/proactive/source-freshness.js +27 -0
- package/dist/src/proactive/status.d.ts +2 -0
- package/dist/src/proactive/status.js +21 -0
- package/dist/src/proactive/temporal/worker.js +2 -1
- package/dist/src/realtime/runtime.js +31 -5
- package/dist/src/realtime/writer.js +16 -9
- package/dist/src/storage/sqlite/import-repository.d.ts +2 -3
- package/dist/src/storage/sqlite/import-repository.js +3 -3
- package/dist/src/storage/sqlite/import-selection-repository.d.ts +15 -0
- package/dist/src/storage/sqlite/import-selection-repository.js +28 -0
- package/dist/src/storage/sqlite/knowledge-repository.d.ts +1 -0
- package/dist/src/storage/sqlite/knowledge-repository.js +2 -2
- package/dist/src/storage/sqlite/migrations/175_import_selections.sql +8 -0
- package/dist/src/storage/sqlite/migrations/176_proactive_quality.sql +5 -0
- package/dist/src/storage/sqlite/migrations/177_proactive_check_controls.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/candidate-discovery.js +1 -1
- package/dist/src/work-discovery/probe.js +1 -1
- package/package.json +2 -2
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-D9qP10BK.js +0 -1
- package/dist/gateway/static/root/assets/agents-D-bkOuAl.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-BKwBBwJN.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace-B2-f9qZu.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-Dtn4rMeq.js +0 -8
- package/dist/gateway/static/root/assets/capabilities-settings-panel-DiwtR9MD.js +0 -8
- package/dist/gateway/static/root/assets/channels-settings-pTdR1q1K.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-BSQ7Hs2w.js +0 -2
- package/dist/gateway/static/root/assets/device-pairing-wizard-CYkT7ecp.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-CzpSYWex.js +0 -1
- package/dist/gateway/static/root/assets/home-page-CSSJvdd6.js +0 -1
- package/dist/gateway/static/root/assets/imports-page-XqVVYbAp.js +0 -1
- package/dist/gateway/static/root/assets/index-B_Sg5BKz.css +0 -2
- package/dist/gateway/static/root/assets/local-app-workbench-page-CZSgTYuI.js +0 -2
- package/dist/gateway/static/root/assets/logs-page-z5ktnEzX.js +0 -2
- package/dist/gateway/static/root/assets/note-detail-panel-DSaZfkna.js +0 -5
- package/dist/gateway/static/root/assets/notes-page-CePEyv_C.js +0 -8
- package/dist/gateway/static/root/assets/proactive-page-Cy2Gm2f8.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-BliwYpns.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-Dm-BqnQn.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-C_XuQ9XR.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-Drt1aS2G.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BAUd06_9.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-BHI9e5Tw.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-DOAOjZBQ.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DU0VQpHj.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-DiPgMmBM.js +0 -1
- package/dist/gateway/static/root/assets/user-model-page-DxRaRdA0.js +0 -2
- package/dist/gateway/static/root/assets/workflows-page-CZikdgNw.js +0 -6
|
@@ -70,10 +70,63 @@ export declare class ImportError extends Error {
|
|
|
70
70
|
readonly status: 400 | 403 | 404 | 409 | 413;
|
|
71
71
|
constructor(code: string, message: string, status?: 400 | 403 | 404 | 409 | 413);
|
|
72
72
|
}
|
|
73
|
+
export type InventoryStatus = 'ready' | 'existing' | 'conflict' | 'blocked';
|
|
74
|
+
export interface InventoryItem {
|
|
75
|
+
id: string;
|
|
76
|
+
kind: 'skill' | 'context' | 'project';
|
|
77
|
+
parentId?: string;
|
|
78
|
+
name: string;
|
|
79
|
+
description: string;
|
|
80
|
+
displayPath: string;
|
|
81
|
+
scope: ImportScope;
|
|
82
|
+
status: InventoryStatus;
|
|
83
|
+
reason?: string;
|
|
84
|
+
suggested: boolean;
|
|
85
|
+
targetName?: string;
|
|
86
|
+
}
|
|
87
|
+
export interface ImportInventory {
|
|
88
|
+
id: string;
|
|
89
|
+
source: ImportSource;
|
|
90
|
+
createdAt: number;
|
|
91
|
+
expiresAt: number;
|
|
92
|
+
candidates: InventoryItem[];
|
|
93
|
+
notices: string[];
|
|
94
|
+
complete: boolean;
|
|
95
|
+
}
|
|
96
|
+
export interface StoredInventoryItem extends InventoryItem {
|
|
97
|
+
location: string;
|
|
98
|
+
scanId?: string;
|
|
99
|
+
candidate?: ImportCandidate;
|
|
100
|
+
targetRoot?: string;
|
|
101
|
+
projectId?: string;
|
|
102
|
+
}
|
|
103
|
+
export interface StoredInventory extends Omit<ImportInventory, 'candidates'> {
|
|
104
|
+
candidates: StoredInventoryItem[];
|
|
105
|
+
scanIds: string[];
|
|
106
|
+
}
|
|
107
|
+
export interface ImportSelection {
|
|
108
|
+
inventoryId: string;
|
|
109
|
+
candidateIds: string[];
|
|
110
|
+
requestId: string;
|
|
111
|
+
retryOf?: string;
|
|
112
|
+
}
|
|
113
|
+
export interface ImportRunItem {
|
|
114
|
+
candidateId: string;
|
|
115
|
+
kind: InventoryItem['kind'];
|
|
116
|
+
name: string;
|
|
117
|
+
status: 'pending' | 'imported' | 'existing' | 'failed';
|
|
118
|
+
targetId?: string;
|
|
119
|
+
jobId?: string;
|
|
120
|
+
error?: string;
|
|
121
|
+
}
|
|
73
122
|
export interface ProductImportResult {
|
|
74
123
|
id: string;
|
|
75
124
|
source: ImportSource;
|
|
76
125
|
createdAt: number;
|
|
126
|
+
inventoryId: string;
|
|
127
|
+
selectionHash: string;
|
|
128
|
+
status: 'running' | 'completed' | 'partial';
|
|
129
|
+
items: ImportRunItem[];
|
|
77
130
|
skills: number;
|
|
78
131
|
context: number;
|
|
79
132
|
projects: number;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/infra/background-task.ts
|
|
2
|
+
/** Run one background tick at a time, reporting once per consecutive failure period. */
|
|
3
|
+
function createBackgroundTask(run, onError) {
|
|
4
|
+
let running = false;
|
|
5
|
+
let failed = false;
|
|
6
|
+
return () => {
|
|
7
|
+
if (running) return;
|
|
8
|
+
running = true;
|
|
9
|
+
(async () => {
|
|
10
|
+
try {
|
|
11
|
+
await run();
|
|
12
|
+
failed = false;
|
|
13
|
+
} catch (error) {
|
|
14
|
+
if (!failed) {
|
|
15
|
+
failed = true;
|
|
16
|
+
onError(error);
|
|
17
|
+
}
|
|
18
|
+
} finally {
|
|
19
|
+
running = false;
|
|
20
|
+
}
|
|
21
|
+
})();
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { createBackgroundTask };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function recordProcessDiagnostic(event: string, detail: unknown): void;
|
|
2
|
+
/** Independent of SQLite and async logger transports; safe to use during process failure. */
|
|
3
|
+
export declare function createProcessDiagnosticWriter(path: string): (event: string, detail: unknown) => void;
|
|
4
|
+
/** Observe fatal exceptions without resuming execution in an unknown process state. */
|
|
5
|
+
export declare function installProcessDiagnostics(path: string): () => void;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { init_redact, redactObject, redactSensitiveInfo } from "../utils/logger/redact.js";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { appendFileSync, mkdirSync, renameSync, rmSync, statSync } from "node:fs";
|
|
4
|
+
import { inspect } from "node:util";
|
|
5
|
+
//#region src/infra/process-diagnostics.ts
|
|
6
|
+
init_redact();
|
|
7
|
+
const MAX_LOG_BYTES = 2 * 1024 * 1024;
|
|
8
|
+
const MAX_DETAIL_CHARS = 64 * 1024;
|
|
9
|
+
function diagnosticValue(value, depth = 0) {
|
|
10
|
+
if (depth > 5) return "[MaxDepth]";
|
|
11
|
+
if (value instanceof Error) return {
|
|
12
|
+
name: value.name,
|
|
13
|
+
message: value.message,
|
|
14
|
+
stack: value.stack,
|
|
15
|
+
...value.cause === void 0 ? {} : { cause: diagnosticValue(value.cause, depth + 1) }
|
|
16
|
+
};
|
|
17
|
+
if (Array.isArray(value)) return value.slice(0, 30).map((item) => diagnosticValue(item, depth + 1));
|
|
18
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).slice(0, 30).map(([key, item]) => [key, diagnosticValue(item, depth + 1)]));
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
let activeWriter;
|
|
22
|
+
function recordProcessDiagnostic(event, detail) {
|
|
23
|
+
activeWriter?.(event, detail);
|
|
24
|
+
}
|
|
25
|
+
/** Independent of SQLite and async logger transports; safe to use during process failure. */
|
|
26
|
+
function createProcessDiagnosticWriter(path) {
|
|
27
|
+
return (event, detail) => {
|
|
28
|
+
try {
|
|
29
|
+
const text = typeof detail === "string" ? detail : inspect(redactObject(diagnosticValue(detail)), {
|
|
30
|
+
depth: 5,
|
|
31
|
+
maxArrayLength: 30,
|
|
32
|
+
maxStringLength: MAX_DETAIL_CHARS,
|
|
33
|
+
customInspect: false,
|
|
34
|
+
getters: false
|
|
35
|
+
});
|
|
36
|
+
const line = JSON.stringify({
|
|
37
|
+
time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
38
|
+
pid: process.pid,
|
|
39
|
+
event,
|
|
40
|
+
detail: redactSensitiveInfo(text).slice(-MAX_DETAIL_CHARS)
|
|
41
|
+
}) + "\n";
|
|
42
|
+
mkdirSync(dirname(path), {
|
|
43
|
+
recursive: true,
|
|
44
|
+
mode: 448
|
|
45
|
+
});
|
|
46
|
+
let size = 0;
|
|
47
|
+
try {
|
|
48
|
+
size = statSync(path).size;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
if (err.code !== "ENOENT") throw err;
|
|
51
|
+
}
|
|
52
|
+
if (size + Buffer.byteLength(line) > MAX_LOG_BYTES) {
|
|
53
|
+
rmSync(`${path}.2`, { force: true });
|
|
54
|
+
try {
|
|
55
|
+
renameSync(`${path}.1`, `${path}.2`);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
if (err.code !== "ENOENT") throw err;
|
|
58
|
+
}
|
|
59
|
+
renameSync(path, `${path}.1`);
|
|
60
|
+
}
|
|
61
|
+
appendFileSync(path, line, {
|
|
62
|
+
encoding: "utf8",
|
|
63
|
+
mode: 384
|
|
64
|
+
});
|
|
65
|
+
} catch {}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Observe fatal exceptions without resuming execution in an unknown process state. */
|
|
69
|
+
function installProcessDiagnostics(path) {
|
|
70
|
+
const write = createProcessDiagnosticWriter(path);
|
|
71
|
+
const previousWriter = activeWriter;
|
|
72
|
+
activeWriter = write;
|
|
73
|
+
const onFatal = (error, origin) => write("uncaught_exception", {
|
|
74
|
+
origin,
|
|
75
|
+
error,
|
|
76
|
+
uptimeSeconds: process.uptime(),
|
|
77
|
+
memory: process.memoryUsage()
|
|
78
|
+
});
|
|
79
|
+
const onExit = (code) => write("process_exit", {
|
|
80
|
+
code,
|
|
81
|
+
uptimeSeconds: process.uptime()
|
|
82
|
+
});
|
|
83
|
+
process.on("uncaughtExceptionMonitor", onFatal);
|
|
84
|
+
process.on("exit", onExit);
|
|
85
|
+
write("process_start", {
|
|
86
|
+
node: process.version,
|
|
87
|
+
platform: process.platform,
|
|
88
|
+
arch: process.arch
|
|
89
|
+
});
|
|
90
|
+
return () => {
|
|
91
|
+
if (activeWriter === write) activeWriter = previousWriter;
|
|
92
|
+
process.removeListener("uncaughtExceptionMonitor", onFatal);
|
|
93
|
+
process.removeListener("exit", onExit);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { createProcessDiagnosticWriter, installProcessDiagnostics, recordProcessDiagnostic };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createLogger } from "../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
3
|
import { resolveGlobalSingleton } from "../utils/global-singleton.js";
|
|
4
|
+
import { recordProcessDiagnostic } from "./process-diagnostics.js";
|
|
4
5
|
import { isTransientSqliteError } from "./sqlite-errors.js";
|
|
5
6
|
//#region src/infra/unhandled-rejections.ts
|
|
6
7
|
init_logger();
|
|
@@ -10,12 +11,14 @@ function installSqliteTransientRejectionHandler() {
|
|
|
10
11
|
const state = resolveGlobalSingleton(installStateKey, () => ({ installed: false }));
|
|
11
12
|
if (state.installed) return;
|
|
12
13
|
process.on("unhandledRejection", (reason) => {
|
|
13
|
-
|
|
14
|
+
recordProcessDiagnostic("unhandled_rejection", reason);
|
|
14
15
|
const em = reason instanceof Error ? reason.message : String(reason);
|
|
15
|
-
|
|
16
|
+
const fields = {
|
|
16
17
|
err: reason instanceof Error ? reason : void 0,
|
|
17
18
|
errorMessage: em
|
|
18
|
-
}
|
|
19
|
+
};
|
|
20
|
+
if (isTransientSqliteError(reason)) log.warn(fields, `Transient SQLite rejection: ${em}`);
|
|
21
|
+
else log.error(fields, `Unhandled promise rejection: ${em}`);
|
|
19
22
|
});
|
|
20
23
|
state.installed = true;
|
|
21
24
|
}
|
|
@@ -3,9 +3,9 @@ import { init_logger } from "../utils/logger.js";
|
|
|
3
3
|
import { init_paths, resolveConfigPath } from "../config/paths.js";
|
|
4
4
|
import { init_loader, loadConfig } from "../config/loader.js";
|
|
5
5
|
import { isRestartEnabled } from "../config/commands.flags.js";
|
|
6
|
-
import { resolveGatewayService } from "../daemon/service.js";
|
|
7
6
|
import { authorizeGatewaySigusr1Restart, writeGatewayRestartIntentSync } from "./restart.js";
|
|
8
7
|
import { findVerifiedGatewayListenerPidsOnPortSync, signalVerifiedGatewayPidSync } from "./gateway-processes.js";
|
|
8
|
+
import { resolveGatewayService } from "../daemon/service.js";
|
|
9
9
|
import { refreshGatewayServiceAfterUpdate } from "./update-service-refresh.js";
|
|
10
10
|
//#region src/infra/update-restart.ts
|
|
11
11
|
init_loader();
|
|
@@ -89,6 +89,7 @@ var KnowledgeIngestionService = class {
|
|
|
89
89
|
const cursorBefore = this.state.getCursor(params.instanceId, params.collectionScope);
|
|
90
90
|
const run = startKnowledgeSyncRun({
|
|
91
91
|
sourceInstanceId: params.instanceId,
|
|
92
|
+
collectionScope: params.collectionScope,
|
|
92
93
|
cursorBefore
|
|
93
94
|
});
|
|
94
95
|
const controller = params.signal ? null : new AbortController();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getSqliteDatabase } from "../storage/sqlite/transaction.js";
|
|
2
|
-
import { proactivePreferences, quietHoursEnd } from "../proactive/policy/service.js";
|
|
3
|
-
import {
|
|
2
|
+
import { proactiveChecksAllowed, proactivePreferences, quietHoursEnd } from "../proactive/policy/service.js";
|
|
3
|
+
import { viewingProactiveCard } from "../proactive/policy/presence.js";
|
|
4
4
|
import { digestCards } from "../proactive/inbox/digest.js";
|
|
5
5
|
import { recheckProactiveNotification } from "../proactive/inbox/notification-policy.js";
|
|
6
6
|
//#region src/notifications/proactive-policy.ts
|
|
@@ -19,11 +19,13 @@ function recheckNotificationDelivery(notification, channel, now = /* @__PURE__ *
|
|
|
19
19
|
"auto",
|
|
20
20
|
channel
|
|
21
21
|
].includes(preferences.preferredChannel)) return "cancel";
|
|
22
|
-
if (preferences
|
|
23
|
-
if (preferences.suppressWhileViewing &&
|
|
22
|
+
if (!proactiveChecksAllowed(preferences, now) || preferences.notificationsMuted) return "cancel";
|
|
23
|
+
if (preferences.suppressWhileViewing && notification.target.kind === "insight" && viewingProactiveCard(workspace, notification.target.inboxItemId, Number(notification.payload.notificationRevision ?? 1), now.getTime())) return new Date(now.getTime() + 6e4);
|
|
24
24
|
if (notification.target.kind === "insight") return recheckProactiveNotification(notification.target.inboxItemId, now, notification.payload.notificationRevision);
|
|
25
25
|
if (notification.target.kind === "proactive_digest") {
|
|
26
|
-
|
|
26
|
+
const decisions = digestCards(notification.target.digestId, workspace, true).map((card) => recheckProactiveNotification(card.id, now, card.notificationRevision, true));
|
|
27
|
+
if (!decisions.length || decisions.every((value) => value === "cancel")) return "cancel";
|
|
28
|
+
if (!decisions.includes("send")) return decisions.find((value) => value instanceof Date) ?? "cancel";
|
|
27
29
|
return quietHoursEnd(preferences, now) ?? "send";
|
|
28
30
|
}
|
|
29
31
|
return "cancel";
|
|
@@ -3,3 +3,5 @@ export declare function insightSourcesAuthorized(insightId: string): boolean;
|
|
|
3
3
|
/** Compare versions, not generated prose, before showing or executing prepared work. */
|
|
4
4
|
export declare function insightSourcesChanged(insightId: string): boolean;
|
|
5
5
|
export declare function authorizedConnectedSource(itemId: string, workspaceId: string, scenarioKey: string, agentId?: string | null): import("../../knowledge/types.js").KnowledgeSourceItem;
|
|
6
|
+
/** Delivery eligibility is stricter than reading historical artifacts. */
|
|
7
|
+
export declare function insightSourcesFresh(insightId: string): boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getSqliteDatabase } from "../../storage/sqlite/transaction.js";
|
|
2
2
|
import { getKnowledgeSourceItem } from "../../storage/sqlite/knowledge-repository.js";
|
|
3
3
|
import { getConnectorSyncPolicyForConnection } from "../../storage/sqlite/connector-sync-policy-repository.js";
|
|
4
|
+
import { sourceFreshness } from "../source-freshness.js";
|
|
4
5
|
import { readMailThread } from "../mail-thread.js";
|
|
5
6
|
//#region src/proactive/execution/authorization.ts
|
|
6
7
|
/** Recheck connector consent for stored cards and actions, not just model input. */
|
|
@@ -57,5 +58,13 @@ function authorizedConnectedSource(itemId, workspaceId, scenarioKey, agentId) {
|
|
|
57
58
|
const policy = getConnectorSyncPolicyForConnection(connectionId);
|
|
58
59
|
return policy?.scanEnabled && policy.proactiveEnabled && (!policy.allowedScenarioKeys.length || policy.allowedScenarioKeys.includes(scenarioKey)) ? item : null;
|
|
59
60
|
}
|
|
61
|
+
/** Delivery eligibility is stricter than reading historical artifacts. */
|
|
62
|
+
function insightSourcesFresh(insightId) {
|
|
63
|
+
const row = getSqliteDatabase().prepare(`SELECT c.evidence_ids_json, c.content_json FROM proactive_insights x JOIN proactive_runs r USING(run_id)
|
|
64
|
+
JOIN proactive_context_snapshots c ON c.snapshot_id = r.context_snapshot_id WHERE x.insight_id = ?`).get(insightId);
|
|
65
|
+
if (!row) return true;
|
|
66
|
+
const follow = JSON.parse(row.content_json).follow_up;
|
|
67
|
+
return JSON.parse(row.evidence_ids_json).every((id) => !id.startsWith("source-item:") || sourceFreshness(id.slice(12), follow?.deadlinePassed ? follow.dueAt : void 0).fresh);
|
|
68
|
+
}
|
|
60
69
|
//#endregion
|
|
61
|
-
export { authorizedConnectedSource, insightSourcesAuthorized, insightSourcesChanged };
|
|
70
|
+
export { authorizedConnectedSource, insightSourcesAuthorized, insightSourcesChanged, insightSourcesFresh };
|
|
@@ -31,6 +31,7 @@ function authorizedConnectedSourceItem(event, scenarioKey) {
|
|
|
31
31
|
const item = authorizedConnectedSource(event.subject_id, event.workspace_id, scenarioKey, event.agent_id);
|
|
32
32
|
if (item && event.type === "connected_source.calendar_window.v1") {
|
|
33
33
|
const payload = JSON.parse(event.payload_json);
|
|
34
|
+
if (!item.occurredAt || Date.parse(item.occurredAt) <= Date.now()) return null;
|
|
34
35
|
if (payload.meetingStartsAt !== item.occurredAt || payload.contentHash && payload.contentHash !== item.contentHash) return null;
|
|
35
36
|
}
|
|
36
37
|
return item;
|
|
@@ -22,5 +22,7 @@ export declare function finishRun(input: {
|
|
|
22
22
|
outcomeReason?: string;
|
|
23
23
|
modelRef?: string;
|
|
24
24
|
}, now?: Date): ProactiveInsight | null;
|
|
25
|
+
/** Stale evidence waits without spending model attempts; never retain work indefinitely. */
|
|
26
|
+
export declare function deferRunForSource(run: ClaimedRun, now?: Date): void;
|
|
25
27
|
export declare function failRun(run: ClaimedRun, error: unknown, retryable: boolean, now?: Date): void;
|
|
26
28
|
export declare function listInsights(limit?: number): ProactiveInsight[];
|
|
@@ -30,7 +30,7 @@ function claimNextRun(owner, now = /* @__PURE__ */ new Date(), leaseSeconds = 12
|
|
|
30
30
|
WHERE status = 'running' AND lease_expires_at <= ? AND attempt >= 3`).run(nowIso, nowIso);
|
|
31
31
|
const retry = db.prepare(`SELECT * FROM proactive_runs WHERE status = 'retryable' AND next_attempt_at <= ? ORDER BY next_attempt_at LIMIT 1`).get(nowIso);
|
|
32
32
|
if (retry) {
|
|
33
|
-
db.prepare(`UPDATE proactive_runs SET status = 'running', attempt = attempt + 1, lease_owner = ?, lease_expires_at = ?, next_attempt_at = NULL, updated_at = ? WHERE run_id = ?`).run(owner, new Date(now.getTime() + leaseSeconds * 1e3).toISOString(), nowIso, str(retry, "run_id"));
|
|
33
|
+
db.prepare(`UPDATE proactive_runs SET status = 'running', attempt = attempt + CASE WHEN outcome_reason = 'source_stale' THEN 0 ELSE 1 END, lease_owner = ?, lease_expires_at = ?, next_attempt_at = NULL, updated_at = ? WHERE run_id = ?`).run(owner, new Date(now.getTime() + leaseSeconds * 1e3).toISOString(), nowIso, str(retry, "run_id"));
|
|
34
34
|
db.prepare("UPDATE proactive_signal_batches SET status = 'processing', updated_at = ? WHERE batch_id = ?").run(nowIso, str(retry, "batch_id"));
|
|
35
35
|
return runFromRow(db.prepare("SELECT * FROM proactive_runs WHERE run_id = ?").get(str(retry, "run_id")));
|
|
36
36
|
}
|
|
@@ -153,6 +153,23 @@ function finishRun(input, now = /* @__PURE__ */ new Date()) {
|
|
|
153
153
|
return insight;
|
|
154
154
|
});
|
|
155
155
|
}
|
|
156
|
+
/** Stale evidence waits without spending model attempts; never retain work indefinitely. */
|
|
157
|
+
function deferRunForSource(run, now = /* @__PURE__ */ new Date()) {
|
|
158
|
+
runSqliteWriteTransaction((db) => {
|
|
159
|
+
const row = db.prepare("SELECT started_at FROM proactive_runs WHERE run_id = ?").get(run.id);
|
|
160
|
+
if (now.getTime() - Date.parse(row.started_at) >= 864e5) {
|
|
161
|
+
finishRun({
|
|
162
|
+
run,
|
|
163
|
+
rawOutput: "",
|
|
164
|
+
outcomeReason: "source_unavailable"
|
|
165
|
+
}, now);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (db.prepare(`UPDATE proactive_runs SET status = 'retryable', outcome_reason = 'source_stale',
|
|
169
|
+
lease_owner = NULL, lease_expires_at = NULL, next_attempt_at = ?, updated_at = ?
|
|
170
|
+
WHERE run_id = ? AND status = 'running' AND attempt = ?`).run(new Date(now.getTime() + 6e4).toISOString(), now.toISOString(), run.id, run.attempt).changes) db.prepare("UPDATE proactive_signal_batches SET status = 'failed_retryable', updated_at = ? WHERE batch_id = ?").run(now.toISOString(), run.batchId);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
156
173
|
function failRun(run, error, retryable, now = /* @__PURE__ */ new Date()) {
|
|
157
174
|
runSqliteWriteTransaction((db) => {
|
|
158
175
|
const finalRetryable = retryable && run.attempt < 3;
|
|
@@ -190,4 +207,4 @@ function listInsights(limit = 50) {
|
|
|
190
207
|
}));
|
|
191
208
|
}
|
|
192
209
|
//#endregion
|
|
193
|
-
export { attachSnapshot, claimNextRun, eventIdsForBatch, failRun, finishRun, listInsights, saveSnapshot };
|
|
210
|
+
export { attachSnapshot, claimNextRun, deferRunForSource, eventIdsForBatch, failRun, finishRun, listInsights, saveSnapshot };
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
3
|
import { getSqliteDatabase } from "../../storage/sqlite/transaction.js";
|
|
4
|
+
import { evidenceSourcesFresh } from "../source-freshness.js";
|
|
4
5
|
import { authorizedConnectedSource } from "./authorization.js";
|
|
5
6
|
import { effectiveProactivePolicy } from "../policy/service.js";
|
|
6
7
|
import { getPromptRevision, getScenario } from "../scenarios/repository.js";
|
|
7
8
|
import { composeScenarioPrompt } from "../scenarios/prompt-composer.js";
|
|
8
9
|
import { markReadyBatches } from "../routing/batch-repository.js";
|
|
9
10
|
import { ContextProviderRegistry } from "./context.js";
|
|
10
|
-
import { attachSnapshot, claimNextRun, eventIdsForBatch, failRun, finishRun, saveSnapshot } from "./repository.js";
|
|
11
|
+
import { attachSnapshot, claimNextRun, deferRunForSource, eventIdsForBatch, failRun, finishRun, saveSnapshot } from "./repository.js";
|
|
11
12
|
import { isValuableInsight, parseAnalysisResult, scoreInsight } from "./insight.js";
|
|
12
|
-
import { randomUUID } from "node:crypto";
|
|
13
13
|
import { isDeepStrictEqual } from "node:util";
|
|
14
|
+
import { randomUUID } from "node:crypto";
|
|
14
15
|
//#region src/proactive/execution/worker.ts
|
|
15
16
|
init_logger();
|
|
16
17
|
const log = createLogger("ProactiveWorker");
|
|
@@ -69,6 +70,10 @@ var ProactiveWorker = class {
|
|
|
69
70
|
});
|
|
70
71
|
return;
|
|
71
72
|
}
|
|
73
|
+
if (!evidenceSourcesFresh(context.evidenceIds, context.content)) {
|
|
74
|
+
deferRunForSource(run);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
72
77
|
const snapshot = saveSnapshot(run.batchId, context.snapshotContent ?? context.content, context.evidenceIds);
|
|
73
78
|
attachSnapshot(run.id, snapshot.id);
|
|
74
79
|
const prompt = composeScenarioPrompt({
|
|
@@ -89,10 +94,15 @@ var ProactiveWorker = class {
|
|
|
89
94
|
});
|
|
90
95
|
const revoked = context.evidenceIds.some((id) => !latestContext.evidenceIds.includes(id));
|
|
91
96
|
const changed = !isDeepStrictEqual(context.snapshotContent ?? context.content, latestContext.snapshotContent ?? latestContext.content);
|
|
97
|
+
const stale = !evidenceSourcesFresh(latestContext.evidenceIds, latestContext.content);
|
|
98
|
+
if (stale && !revoked) {
|
|
99
|
+
deferRunForSource(run);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
92
102
|
const unavailable = context.evidenceIds.some((id) => id.startsWith("source-item:") && !authorizedConnectedSource(id.slice(12), effectiveProactivePolicy(run.subscriptionId).workspaceId, run.scenarioKey));
|
|
93
|
-
const result = revoked || changed ? {
|
|
103
|
+
const result = revoked || changed || stale ? {
|
|
94
104
|
result: "no_insight",
|
|
95
|
-
reason: unavailable ? "source_unavailable" : "source_changed"
|
|
105
|
+
reason: unavailable ? "source_unavailable" : stale ? "source_stale" : "source_changed"
|
|
96
106
|
} : parseAnalysisResult(output.text, new Set(latestContext.evidenceIds));
|
|
97
107
|
const candidate = result.result === "insight" ? result.candidate : void 0;
|
|
98
108
|
if (run.scenarioKey === "communication_follow_up" && (candidate?.decision || candidate?.proposedAction)) throw new Error("Communication preparation must return a draft or update; actions require the conversation confirmation flow");
|
|
@@ -21,7 +21,7 @@ interface SceneMoment {
|
|
|
21
21
|
}
|
|
22
22
|
export declare function startDelegation(workspace: string, value: unknown): {
|
|
23
23
|
revision: number;
|
|
24
|
-
level: "
|
|
24
|
+
level: "active" | "quiet" | "balanced";
|
|
25
25
|
delivery: "digest" | "inbox" | "important";
|
|
26
26
|
completedAt: string;
|
|
27
27
|
scanIntervalMinutes: number;
|
|
@@ -58,6 +58,7 @@ export declare function delegationOverview(workspace: string): {
|
|
|
58
58
|
latestDirection: string;
|
|
59
59
|
lastSyncedAt: string;
|
|
60
60
|
syncFailed: boolean;
|
|
61
|
+
sourceFresh: boolean;
|
|
61
62
|
}[];
|
|
62
63
|
delegations: {
|
|
63
64
|
id: string;
|
|
@@ -67,6 +68,8 @@ export declare function delegationOverview(workspace: string): {
|
|
|
67
68
|
enabled: boolean;
|
|
68
69
|
revision: number;
|
|
69
70
|
delivery: "digest" | "inbox" | "important";
|
|
71
|
+
scanIntervalMinutes: number;
|
|
72
|
+
checkStatus: import("@xopcai/gateway-contract/src/proactive.js").ProactiveCheckStatus;
|
|
70
73
|
completedAt: string;
|
|
71
74
|
userInstructions: string;
|
|
72
75
|
updatedAt: string;
|
|
@@ -4,6 +4,7 @@ import { controlledSubscriptions, createControlledSubscription, requireSubscript
|
|
|
4
4
|
import { ProactiveScenarioService } from "./scenarios/service.js";
|
|
5
5
|
import { ProactiveEventService } from "./service.js";
|
|
6
6
|
import { listMailFollowUps } from "./follow-ups.js";
|
|
7
|
+
import { followUpCheckStatus } from "./status.js";
|
|
7
8
|
import { getCard } from "./inbox/cards.js";
|
|
8
9
|
import { scanDueProjects } from "./temporal/schedule.js";
|
|
9
10
|
import { z } from "zod";
|
|
@@ -69,6 +70,8 @@ function delegationOverview(workspace) {
|
|
|
69
70
|
enabled: sub.enabled,
|
|
70
71
|
revision: sub.revision,
|
|
71
72
|
delivery: sub.delivery,
|
|
73
|
+
scanIntervalMinutes: sub.scanIntervalMinutes ?? 120,
|
|
74
|
+
checkStatus: followUpCheckStatus(sub.id, sub.scopeKind === "project"),
|
|
72
75
|
completedAt: sub.completedAt,
|
|
73
76
|
userInstructions: sub.userInstructions,
|
|
74
77
|
updatedAt: sub.updatedAt,
|
|
@@ -36,6 +36,7 @@ export declare function mailFollowUpView(follow: MailFollowUp): {
|
|
|
36
36
|
latestDirection: string;
|
|
37
37
|
lastSyncedAt: string;
|
|
38
38
|
syncFailed: boolean;
|
|
39
|
+
sourceFresh: boolean;
|
|
39
40
|
};
|
|
40
41
|
export declare function listMailFollowUps(workspace: string): {
|
|
41
42
|
id: string;
|
|
@@ -53,6 +54,7 @@ export declare function listMailFollowUps(workspace: string): {
|
|
|
53
54
|
latestDirection: string;
|
|
54
55
|
lastSyncedAt: string;
|
|
55
56
|
syncFailed: boolean;
|
|
57
|
+
sourceFresh: boolean;
|
|
56
58
|
}[];
|
|
57
59
|
export declare function mailFollowUpSources(workspace: string): {
|
|
58
60
|
id: string;
|
|
@@ -77,6 +79,7 @@ export declare function startMailFollowUp(workspace: string, value: unknown): {
|
|
|
77
79
|
latestDirection: string;
|
|
78
80
|
lastSyncedAt: string;
|
|
79
81
|
syncFailed: boolean;
|
|
82
|
+
sourceFresh: boolean;
|
|
80
83
|
};
|
|
81
84
|
export declare function updateMailFollowUp(workspace: string, id: string, value: unknown): {
|
|
82
85
|
id: string;
|
|
@@ -94,6 +97,7 @@ export declare function updateMailFollowUp(workspace: string, id: string, value:
|
|
|
94
97
|
latestDirection: string;
|
|
95
98
|
lastSyncedAt: string;
|
|
96
99
|
syncFailed: boolean;
|
|
100
|
+
sourceFresh: boolean;
|
|
97
101
|
};
|
|
98
102
|
/** Keep the existing chat and its exact connector confirmation flow as the write boundary. */
|
|
99
103
|
export declare function continueMailFollowUp(workspace: string, id: string, sessionKey: string): {
|
|
@@ -113,6 +117,7 @@ export declare function continueMailFollowUp(workspace: string, id: string, sess
|
|
|
113
117
|
latestDirection: string;
|
|
114
118
|
lastSyncedAt: string;
|
|
115
119
|
syncFailed: boolean;
|
|
120
|
+
sourceFresh: boolean;
|
|
116
121
|
};
|
|
117
122
|
connectionId: unknown;
|
|
118
123
|
sourceItemId: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getSqliteDatabase, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
|
|
2
2
|
import { listKnowledgeSourceItems } from "../storage/sqlite/knowledge-repository.js";
|
|
3
3
|
import { getConnectorConnection } from "../storage/sqlite/connector-repository.js";
|
|
4
|
+
import { sourceFreshness } from "./source-freshness.js";
|
|
4
5
|
import { emailFields, readMailThread } from "./mail-thread.js";
|
|
5
6
|
import { authorizedConnectedSource } from "./execution/authorization.js";
|
|
6
7
|
import { ProactiveConflict, effectiveProactivePolicy } from "./policy/service.js";
|
|
@@ -41,7 +42,7 @@ function authorizedMailThread(follow) {
|
|
|
41
42
|
function mailFollowUpView(follow) {
|
|
42
43
|
const thread = authorizedMailThread(follow);
|
|
43
44
|
const latest = thread?.items.at(-1);
|
|
44
|
-
const sync =
|
|
45
|
+
const sync = sourceFreshness(follow.source_item_id, Date.parse(follow.due_at) <= Date.now() ? follow.due_at : void 0);
|
|
45
46
|
return {
|
|
46
47
|
id: follow.id,
|
|
47
48
|
subscriptionId: follow.subscription_id,
|
|
@@ -52,12 +53,13 @@ function mailFollowUpView(follow) {
|
|
|
52
53
|
lastCheckedAt: follow.last_checked_at,
|
|
53
54
|
sessionKey: follow.session_key,
|
|
54
55
|
sourceAvailable: Boolean(thread),
|
|
55
|
-
enabled: effectiveProactivePolicy(follow.subscription_id).enabled,
|
|
56
|
+
enabled: follow.status === "watching" && effectiveProactivePolicy(follow.subscription_id).enabled,
|
|
56
57
|
subject: thread ? emailFields(thread.origin.normalizedText).subject ?? "Email follow-up" : null,
|
|
57
58
|
latestMessageAt: latest?.occurredAt ?? null,
|
|
58
59
|
latestDirection: latest ? emailFields(latest.normalizedText).labels?.includes("SENT") ? "sent" : emailFields(latest.normalizedText).labels?.includes("INBOX") ? "received" : "unknown" : null,
|
|
59
|
-
lastSyncedAt: sync
|
|
60
|
-
syncFailed:
|
|
60
|
+
lastSyncedAt: sync.lastSyncedAt,
|
|
61
|
+
syncFailed: sync.syncFailed,
|
|
62
|
+
sourceFresh: sync.fresh
|
|
61
63
|
};
|
|
62
64
|
}
|
|
63
65
|
function listMailFollowUps(workspace) {
|
|
@@ -137,11 +139,20 @@ function scanMailFollowUps(events, now = /* @__PURE__ */ new Date()) {
|
|
|
137
139
|
const db = getSqliteDatabase();
|
|
138
140
|
let published = 0;
|
|
139
141
|
for (const follow of db.prepare("SELECT * FROM proactive_follow_ups WHERE status = 'watching'").all()) {
|
|
140
|
-
|
|
142
|
+
const policy = effectiveProactivePolicy(follow.subscription_id, now);
|
|
143
|
+
if (!policy.enabled) continue;
|
|
141
144
|
const thread = authorizedMailThread(follow);
|
|
142
145
|
if (!thread) continue;
|
|
146
|
+
const freshness = sourceFreshness(follow.source_item_id, Date.parse(follow.due_at) <= now.getTime() ? follow.due_at : void 0, now.getTime());
|
|
147
|
+
if (!freshness.fresh) {
|
|
148
|
+
db.prepare("UPDATE proactive_follow_ups SET last_fingerprint = NULL WHERE id = ?").run(follow.id);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
143
151
|
const fingerprint = `${follow.revision}:${thread.fingerprint}:${Date.parse(follow.due_at) <= now.getTime() ? "due" : "waiting"}`;
|
|
144
|
-
|
|
152
|
+
const awaitingSource = db.prepare(`SELECT 1 FROM proactive_runs r JOIN proactive_batch_events be USING(batch_id)
|
|
153
|
+
JOIN proactive_events e USING(event_id) WHERE r.subscription_id = ? AND r.status = 'retryable'
|
|
154
|
+
AND r.outcome_reason = 'source_stale' AND e.subject_kind = 'mail_follow_up' AND e.subject_id = ? LIMIT 1`).get(follow.subscription_id, follow.id);
|
|
155
|
+
if (fingerprint !== follow.last_fingerprint && !awaitingSource) {
|
|
145
156
|
if (events.publish({
|
|
146
157
|
type: "proactive.follow_up.v1",
|
|
147
158
|
schemaVersion: 1,
|
|
@@ -156,7 +167,7 @@ function scanMailFollowUps(events, now = /* @__PURE__ */ new Date()) {
|
|
|
156
167
|
actor: { kind: "system" },
|
|
157
168
|
scope: { workspaceId: follow.workspace_id },
|
|
158
169
|
occurredAt: now.toISOString(),
|
|
159
|
-
dedupeKey: `mail-follow-up:${follow.id}:${fingerprint}`,
|
|
170
|
+
dedupeKey: `mail-follow-up:${follow.id}:${fingerprint}:${freshness.version}:${policy.preferences.revision}`,
|
|
160
171
|
sensitivity: "personal",
|
|
161
172
|
payload: { followUpId: follow.id }
|
|
162
173
|
}, now).inserted) published++;
|
|
@@ -164,6 +164,7 @@ function applyCardAction(card, workspaceId, input) {
|
|
|
164
164
|
if (input.taskDraft && (input.actionId !== "decide" || input.choice !== "approve" || !card.taskDraft)) throw new Error("Task draft requires a pending approval");
|
|
165
165
|
if (input.artifact && input.actionId !== "edit_artifact") throw new Error("Artifact requires an edit action");
|
|
166
166
|
if (input.instruction && input.actionId !== "refine") throw new Error("Instruction requires a refine action");
|
|
167
|
+
if (input.feedbackReason && input.actionId !== "not_useful") throw new Error("Feedback reason requires negative feedback");
|
|
167
168
|
switch (input.actionId) {
|
|
168
169
|
case "edit_artifact":
|
|
169
170
|
if (!input.artifact || !card.artifact || input.artifact.kind !== card.artifact.kind) throw new Error("An existing artifact is required");
|
|
@@ -177,7 +178,7 @@ function applyCardAction(card, workspaceId, input) {
|
|
|
177
178
|
break;
|
|
178
179
|
case "useful":
|
|
179
180
|
case "not_useful":
|
|
180
|
-
service.feedback(card.id, input.actionId);
|
|
181
|
+
service.feedback(card.id, input.actionId, input.feedbackReason);
|
|
181
182
|
break;
|
|
182
183
|
case "refine":
|
|
183
184
|
if (!input.instruction) throw new Error("Refinement instruction required");
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { getSqliteDatabase, runSqliteWriteTransaction } from "../../storage/sqlite/transaction.js";
|
|
2
|
-
import { insightSourcesAuthorized, insightSourcesChanged } from "../execution/authorization.js";
|
|
2
|
+
import { insightSourcesAuthorized, insightSourcesChanged, insightSourcesFresh } from "../execution/authorization.js";
|
|
3
3
|
import { effectiveProactivePolicy, localProactiveDay, nextDigestTime, quietHoursEnd, reserveProactiveNotification } from "../policy/service.js";
|
|
4
4
|
import { insightCorrelation } from "./lifecycle.js";
|
|
5
5
|
import { getInboxItem } from "./repository.js";
|
|
6
|
-
import {
|
|
6
|
+
import { viewingProactiveCard } from "../policy/presence.js";
|
|
7
7
|
import { queueDigest } from "./digest.js";
|
|
8
8
|
import { notificationPlanFromGatewayEvent } from "../../notifications/planner.js";
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
@@ -12,8 +12,10 @@ import { createHash } from "node:crypto";
|
|
|
12
12
|
function deliverProactiveCard(item, notifications, publish) {
|
|
13
13
|
const result = runSqliteWriteTransaction(() => {
|
|
14
14
|
const current = getInboxItem(item.id);
|
|
15
|
-
if (!current || current.withdrawnAt || !insightSourcesAuthorized(current.insightId) || current.status
|
|
15
|
+
if (!current || current.withdrawnAt || !insightSourcesAuthorized(current.insightId) || ["resolved", "read"].includes(current.status) || current.expiresAt && Date.parse(current.expiresAt) <= Date.now()) return null;
|
|
16
16
|
if (current.insight.actionStatus !== "completed" && insightSourcesChanged(current.insightId)) return null;
|
|
17
|
+
if (current.actionableUntil && Date.parse(current.actionableUntil) <= Date.now()) return null;
|
|
18
|
+
if (!insightSourcesFresh(current.insightId)) return { retryAt: new Date(Date.now() + 6e4).toISOString() };
|
|
17
19
|
if (current.status === "snoozed") return { retryAt: current.snoozedUntil };
|
|
18
20
|
const policy = effectiveProactivePolicy(current.subscriptionId);
|
|
19
21
|
if (!policy.enabled || policy.settings.delivery === "inbox") return null;
|
|
@@ -32,10 +34,7 @@ function deliverProactiveCard(item, notifications, publish) {
|
|
|
32
34
|
const db = getSqliteDatabase();
|
|
33
35
|
const key = `proactive:${insightCorrelation(current.insightId)}:${createHash("sha256").update(JSON.stringify([current.insight.urgency, current.insight.decision ?? null])).digest("hex")}:${localProactiveDay(/* @__PURE__ */ new Date(), policy.preferences.timezone)}`;
|
|
34
36
|
if (db.prepare("SELECT 1 FROM proactive_delivery_decisions WHERE notification_key = ?").get(key)) return null;
|
|
35
|
-
if (policy.preferences.suppressWhileViewing &&
|
|
36
|
-
db.prepare("INSERT INTO proactive_delivery_decisions VALUES (?, ?, ?, ?)").run(key, policy.workspaceId, "visible_in_app", (/* @__PURE__ */ new Date()).toISOString());
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
37
|
+
if (policy.preferences.suppressWhileViewing && viewingProactiveCard(policy.workspaceId, current.id, current.notificationRevision)) return { retryAt: new Date(Date.now() + 6e4).toISOString() };
|
|
39
38
|
const decision = reserveProactiveNotification(current.subscriptionId, key);
|
|
40
39
|
if (decision instanceof Date) return { retryAt: decision.toISOString() };
|
|
41
40
|
if (decision === "suppressed") return null;
|
|
@@ -3,6 +3,6 @@ import type { NotificationPlan } from '../../notifications/planner.js';
|
|
|
3
3
|
import type { InboxItem } from './types.js';
|
|
4
4
|
export { nextDigestTime } from '../policy/service.js';
|
|
5
5
|
export declare function queueDigest(item: InboxItem, mode: 'daily' | 'quiet', dueAt: Date): void;
|
|
6
|
-
export declare function digestCards(id: string, workspaceId: string): import("@xopcai/gateway-contract").ProactiveCard[];
|
|
6
|
+
export declare function digestCards(id: string, workspaceId: string, forDelivery?: boolean): import("@xopcai/gateway-contract").ProactiveCard[];
|
|
7
7
|
export declare function reserveWorkspaceAttention(workspaceId: string, key: string, now?: Date): boolean;
|
|
8
8
|
export declare function flushDueDigests(persist: (plan: NotificationPlan) => ProductNotification | null, now?: Date): ProductNotification[];
|