@xopcai/xopc 0.0.159 → 0.0.161
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{action-boundary-settings-panel-CaRK2vIy.js → action-boundary-settings-panel-BIVBUiNT.js} +1 -1
- package/dist/gateway/static/root/assets/activity-store-o7GRljfD.js +1 -0
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BeVf03hx.js → agent-avatar-dicebear-9R3szLMc.js} +1 -1
- package/dist/gateway/static/root/assets/agents-_qbUaYbs.js +2 -0
- package/dist/gateway/static/root/assets/{api-BjcBIV2O.js → api-jSTqFMq9.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-BG2ulMYw.js → app-management-settings-panel-D81cyE76.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-0Kjenr2T.js → appearance-settings-Ct6bZQCV.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-0sPqD0FK.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-ChiNnH1e.js → archive-plugin-CfD7AZKn.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-pbBlQR-g.js → attachment-load-CP_Gaxt3.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-CTZDOyDy.js → automation-suggestion-card-BGKM_NDZ.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-BxWBuBsE.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-BeZGzpGF.js → binary-plugins-IPr2EVnS.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-BnDmhN5Z.js → block-editor-B6aHEGA6.js} +3 -3
- package/dist/gateway/static/root/assets/browser-settings-page-C499b9AH.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-Ii36j_7v.js → capability-presets-api-CCX9-LjU.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-settings-panel-Cl3dw4yh.js → capability-presets-settings-panel-Dva9SiPK.js} +2 -2
- package/dist/gateway/static/root/assets/{channel-recipient-api-CFqrSi5K.js → channel-recipient-api-MmLMLDWf.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-DNAU7u9o.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-CIOH7tgn.js +1 -0
- package/dist/gateway/static/root/assets/check-DUBLcKHA.js +1 -0
- package/dist/gateway/static/root/assets/circle-alert-BEjgurr2.js +1 -0
- package/dist/gateway/static/root/assets/cn-JdwAhgNB.js +1 -0
- package/dist/gateway/static/root/assets/{connector-logo-CiLxuZ8S.js → connector-logo-BbjzIs87.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-TwiBp8vY.js +2 -0
- package/dist/gateway/static/root/assets/createLucideIcon-DPHK1VkS.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-CYH5ZSXy.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-settings-DQLqGLc0.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-sprite-DTqXSpqf.js +1 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-CC2VL5Mc.js → directory-picker-path-field-ClsiXhmz.js} +1 -1
- package/dist/gateway/static/root/assets/{dreaming-settings-KY7ZfVsy.js → dreaming-settings-CjMPPdzg.js} +3 -3
- package/dist/gateway/static/root/assets/{extension-debug-page-C8KBeucs.js → extension-debug-page-57eizjhB.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CV-5Kcyk.js → extension-page-CsbSb3u1.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DvUtDrAu.js → extension-settings-page-DKA9-lyP.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-DXfVeJLc.js → gateway-config-api-DIPJuzBZ.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-CDLqAQY7.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-cxQ94qMm.js → gateway-startup-retry-DrfSJtvT.js} +1 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-Q3B0E-0s.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-C5VAK04G.js +4 -0
- package/dist/gateway/static/root/assets/{goals-config-api-P8VhLqYF.js → goals-config-api-lGC10UkU.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-BJb-5IYw.js +2 -0
- package/dist/gateway/static/root/assets/{goals-settings-DHXyaH_O.js → goals-settings-BWq0pdrA.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-CNckP_Dv.js → heartbeat-settings-D4o8BJVv.js} +1 -1
- package/dist/gateway/static/root/assets/index-CkUHH170.css +1 -0
- package/dist/gateway/static/root/assets/index-vq_cJZJC.js +199 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-jXmv1RVX.js → keyboard-shortcuts-settings-6T2QYehS.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-euH17Kvb.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-Cs5LKjtc.js → local-apps-page-DCjg2K9f.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-C_M71MK9.js → locale-store-Dar-xqQr.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-CyYCBqym.js +7 -0
- package/dist/gateway/static/root/assets/{markdown-split-BFqeSNte.js → markdown-split-rt1XO43g.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-Cc8379gq.js → media-plugins-Bwo8CJuh.js} +1 -1
- package/dist/gateway/static/root/assets/messages-CxySDn9d.js +5 -0
- package/dist/gateway/static/root/assets/models-hub-panel-DObxxjc9.js +2 -0
- package/dist/gateway/static/root/assets/{notes-page-DlGU4ipo.js → notes-page-Dp-pkX7d.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-CCb4RXcZ.js +4 -0
- package/dist/gateway/static/root/assets/{page-tabs-kFOswGlG.js → page-tabs-CzZ4iKOb.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-CvjuvABg.js → preview-open-alternatives-3E_Qa-58.js} +1 -1
- package/dist/gateway/static/root/assets/product-delivery-BTRT9kCS.js +1 -0
- package/dist/gateway/static/root/assets/product-open-page-B2T48Sbt.js +1 -0
- package/dist/gateway/static/root/assets/project-detail-page-BvEcriRC.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-B_e79MRJ.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-DGcqnYfR.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-DwrD7ODx.js → save-bar-controls-CkeKasGm.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-Ba4WzTmE.js → schedule-field-TkqUL3wm.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-DvydL8eF.js → schema-form-DC-iU5IG.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DQZhrDJ8.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BkdeN99Y.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-LaQqGGej.js → settings-collapsible-section-Dpqth3oA.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-DruI__qO.js → settings-form-section-CNqF6IwY.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-UvA-eDhF.js → settings-loading-skeleton-DkvwitK_.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-DG-XMIPu.js → settings-page-layout-DWvNgISx.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-5oCGSebm.js → settings-page-qPG7I2zd.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-lwRAh9aw.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-FLVeTrRt.js → share-preview-page-DGpSPW-n.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-BGj9IVUr.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-B2UBAs5U.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-DdjzFZ8I.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-CeK93xVi.js → system-settings-panel-8TxxSbUw.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-C8ohIHhw.js → theme-store-B5FzMWOx.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-D1Lr2gG0.js → typed-models-lib-DEGtSA5h.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-BWzou5ar.js → use-channel-catalog-BDU6FneG.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-BTTZpAGV.js +1 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-DuDQd-WU.js +1 -0
- package/dist/gateway/static/root/assets/work-discovery-page-BUVkVCsW.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-DeEPPu4n.js +1 -0
- package/dist/gateway/static/root/assets/work-page-DQNUyTUq.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Bynqe4kV.js → workflow-run-setup-panel-Cwi8Pz7n.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-BIy4wrzO.js +11 -0
- package/dist/gateway/static/root/index.html +12 -10
- package/dist/package.js +1 -1
- package/dist/src/automations/service/action-executor.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/chat-stream/mapper.d.ts +1 -0
- package/dist/src/gateway/chat-stream/mapper.js +43 -7
- package/dist/src/gateway/hono/routes/home.js +15 -2
- package/dist/src/gateway/hono/routes/work-discovery.js +451 -0
- package/dist/src/gateway/hono/sse.d.ts +2 -0
- package/dist/src/gateway/hono/sse.js +10 -3
- package/dist/src/gateway/run-loop.js +17 -7
- package/dist/src/gateway/server.d.ts +1 -0
- package/dist/src/gateway/server.js +9 -0
- package/dist/src/gateway/service.d.ts +2 -0
- package/dist/src/gateway/service.js +27 -2
- package/dist/src/local-apps/preview-runtime-bridge.d.ts +1 -1
- package/dist/src/local-apps/preview-runtime-bridge.js +14 -11
- package/dist/src/mobile/notification-service.js +1 -0
- package/dist/src/pets/factory.js +23 -26
- package/dist/src/pets/manifest.d.ts +2 -1
- package/dist/src/pets/manifest.js +15 -8
- package/dist/src/pets/validator.js +8 -3
- package/dist/src/proactive/calendar-signals.d.ts +4 -0
- package/dist/src/proactive/calendar-signals.js +120 -0
- package/dist/src/proactive/focus-service.d.ts +62 -0
- package/dist/src/proactive/focus-service.js +173 -0
- package/dist/src/proactive/index.d.ts +7 -0
- package/dist/src/proactive/index.js +7 -0
- package/dist/src/proactive/insight-repository.d.ts +19 -0
- package/dist/src/proactive/insight-repository.js +106 -0
- package/dist/src/proactive/run-processor.d.ts +19 -0
- package/dist/src/proactive/run-processor.js +125 -0
- package/dist/src/proactive/trial-expiry-reconciler.d.ts +5 -0
- package/dist/src/proactive/trial-expiry-reconciler.js +30 -0
- package/dist/src/proactive/types.d.ts +64 -0
- package/dist/src/proactive/types.js +1 -0
- package/dist/src/proactive/watch-repository.d.ts +28 -0
- package/dist/src/proactive/watch-repository.js +129 -0
- package/dist/src/storage/sqlite/knowledge-repository.d.ts +1 -0
- package/dist/src/storage/sqlite/knowledge-repository.js +4 -0
- package/dist/src/storage/sqlite/migrations/047_work_discovery_sources.sql +16 -0
- package/dist/src/storage/sqlite/migrations/048_work_understanding_investigations.sql +40 -0
- package/dist/src/storage/sqlite/migrations/049_work_understanding_threads.sql +52 -0
- package/dist/src/storage/sqlite/migrations/050_work_discovery_source_refreshes.sql +15 -0
- package/dist/src/storage/sqlite/migrations/051_focus_watches.sql +26 -0
- package/dist/src/storage/sqlite/migrations/052_proactive_insights.sql +25 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/work-discovery/analyzer.d.ts +12 -1
- package/dist/src/work-discovery/analyzer.js +126 -7
- package/dist/src/work-discovery/candidate-discovery.d.ts +17 -0
- package/dist/src/work-discovery/candidate-discovery.js +279 -0
- package/dist/src/work-discovery/evaluation.d.ts +11 -0
- package/dist/src/work-discovery/evaluation.js +17 -0
- package/dist/src/work-discovery/governance.d.ts +25 -0
- package/dist/src/work-discovery/governance.js +96 -0
- package/dist/src/work-discovery/incremental-repository.d.ts +13 -0
- package/dist/src/work-discovery/incremental-repository.js +55 -0
- package/dist/src/work-discovery/incremental.d.ts +2 -0
- package/dist/src/work-discovery/incremental.js +7 -0
- package/dist/src/work-discovery/index.d.ts +10 -0
- package/dist/src/work-discovery/index.js +13 -3
- package/dist/src/work-discovery/investigation-repository.d.ts +16 -0
- package/dist/src/work-discovery/investigation-repository.js +122 -0
- package/dist/src/work-discovery/investigator.d.ts +55 -0
- package/dist/src/work-discovery/investigator.js +358 -0
- package/dist/src/work-discovery/probe.d.ts +22 -0
- package/dist/src/work-discovery/probe.js +88 -1
- package/dist/src/work-discovery/service.d.ts +110 -1
- package/dist/src/work-discovery/service.js +578 -8
- package/dist/src/work-discovery/source-repository.d.ts +13 -0
- package/dist/src/work-discovery/source-repository.js +72 -0
- package/dist/src/work-discovery/thread-repository.d.ts +34 -0
- package/dist/src/work-discovery/thread-repository.js +112 -0
- package/dist/src/work-discovery/thread-service.d.ts +8 -0
- package/dist/src/work-discovery/thread-service.js +104 -0
- package/dist/src/work-discovery/types.d.ts +145 -0
- package/package.json +1 -1
- package/skills/creative/hatch-pet/SKILL.md +2 -2
- package/dist/gateway/static/root/assets/activity-store-C-57rvkX.js +0 -1
- package/dist/gateway/static/root/assets/agents-P05C4A-3.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-DANGrBpN.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-BXfGL_cm.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-534luV0B.js +0 -8
- package/dist/gateway/static/root/assets/channels-settings-M91zFJVi.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-vooEF4lG.js +0 -1
- package/dist/gateway/static/root/assets/check-BODqo66o.js +0 -1
- package/dist/gateway/static/root/assets/circle-alert-RzVebuX2.js +0 -1
- package/dist/gateway/static/root/assets/cn-BQ_8x0bT.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-CV5UulW6.js +0 -2
- package/dist/gateway/static/root/assets/desktop-pet-Cil2eSpW.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-settings-DwsC6l5F.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-sprite-DqZ6fbIj.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-BpJ0v56l.js +0 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-lMHD-2CX.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-yPmF-G7b.js +0 -4
- package/dist/gateway/static/root/assets/goals-page-BHTCKobG.js +0 -2
- package/dist/gateway/static/root/assets/index-BgZhEOkN.css +0 -1
- package/dist/gateway/static/root/assets/index-BuqGh2SW.js +0 -193
- package/dist/gateway/static/root/assets/local-app-workbench-page-zpp_wciE.js +0 -2
- package/dist/gateway/static/root/assets/logs-page-B4CRYqYR.js +0 -7
- package/dist/gateway/static/root/assets/messages-BuT0SR1R.js +0 -5
- package/dist/gateway/static/root/assets/models-hub-panel-1hdoAoB_.js +0 -2
- package/dist/gateway/static/root/assets/notes-workbench-DQEAZTA5.js +0 -4
- package/dist/gateway/static/root/assets/project-detail-page-Bp9bC7A6.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-RNorFXTw.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BqRTcM_6.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-B2EfW1cm.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-CklL85T4.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-D3my1HgB.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-DVxgjrsP.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-Bcu58ZaF.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-CDd-84FE.js +0 -2
- package/dist/gateway/static/root/assets/sliding-segmented-uWmrxE3P.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-1OyJnTyR.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-B2ncRwHS.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-page-BZlY4PZL.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-DPXMrneY.js +0 -1
- package/dist/gateway/static/root/assets/work-page-CTERoqyv.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-CLgsnqFQ.js +0 -11
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { getAgentDefaultModelRef, init_schema } from "../../../config/schema.js";
|
|
2
2
|
import { init_providers, resolveModel } from "../../../providers/index.js";
|
|
3
3
|
import { isLocalModelBaseUrl } from "../../../providers/model-call.js";
|
|
4
|
+
import { listFocusCalendarSignals } from "../../../proactive/calendar-signals.js";
|
|
5
|
+
import { FocusService } from "../../../proactive/focus-service.js";
|
|
6
|
+
import { claimProactiveInsightApproval, getProactiveInsight, setProactiveInsightStatus } from "../../../proactive/insight-repository.js";
|
|
7
|
+
import "../../../proactive/index.js";
|
|
4
8
|
import { previewWorkDiscoveryRoot } from "../../../work-discovery/probe.js";
|
|
5
9
|
import { WorkDiscoveryService } from "../../../work-discovery/service.js";
|
|
6
10
|
//#region src/gateway/hono/routes/work-discovery.ts
|
|
@@ -26,6 +30,7 @@ function stringField(body, field) {
|
|
|
26
30
|
}
|
|
27
31
|
function registerWorkDiscoveryRoutes(authenticated, deps) {
|
|
28
32
|
const service = workDiscoveryService(deps);
|
|
33
|
+
const focuses = new FocusService(deps.service.automationServiceInstance);
|
|
29
34
|
const limited = deps.strictRateLimitMiddleware;
|
|
30
35
|
authenticated.get("/api/onboarding/work-discovery", (c) => c.json({
|
|
31
36
|
enabled: service.isEnabled(),
|
|
@@ -77,6 +82,178 @@ function registerWorkDiscoveryRoutes(authenticated, deps) {
|
|
|
77
82
|
}, 400);
|
|
78
83
|
}
|
|
79
84
|
});
|
|
85
|
+
authenticated.post("/api/work-discovery/candidates", limited, async (c) => {
|
|
86
|
+
if (!service.isEnabled()) return c.json({
|
|
87
|
+
ok: false,
|
|
88
|
+
error: "Work discovery is disabled"
|
|
89
|
+
}, 404);
|
|
90
|
+
try {
|
|
91
|
+
const candidates = await service.discoverCandidates(c.req.raw.signal);
|
|
92
|
+
return c.json({
|
|
93
|
+
ok: true,
|
|
94
|
+
candidates
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return c.json({
|
|
98
|
+
ok: false,
|
|
99
|
+
error: error instanceof Error ? error.message : String(error)
|
|
100
|
+
}, 500);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
authenticated.get("/api/work-discovery/sources/directories", (c) => c.json({
|
|
104
|
+
ok: true,
|
|
105
|
+
sources: service.listDirectorySources()
|
|
106
|
+
}));
|
|
107
|
+
authenticated.post("/api/work-discovery/sources/directories", limited, async (c) => {
|
|
108
|
+
const rootPath = stringField(await c.req.json().catch(() => null), "rootPath");
|
|
109
|
+
if (!rootPath) return c.json({
|
|
110
|
+
ok: false,
|
|
111
|
+
error: "Missing rootPath"
|
|
112
|
+
}, 400);
|
|
113
|
+
try {
|
|
114
|
+
const source = await service.grantDirectorySource(rootPath);
|
|
115
|
+
return c.json({
|
|
116
|
+
ok: true,
|
|
117
|
+
source
|
|
118
|
+
}, 201);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
return c.json({
|
|
121
|
+
ok: false,
|
|
122
|
+
error: error instanceof Error ? error.message : String(error)
|
|
123
|
+
}, 400);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
authenticated.delete("/api/work-discovery/sources/directories/:sourceId", limited, (c) => {
|
|
127
|
+
const source = service.revokeDirectorySource(c.req.param("sourceId"));
|
|
128
|
+
return source ? c.json({
|
|
129
|
+
ok: true,
|
|
130
|
+
source
|
|
131
|
+
}) : c.json({
|
|
132
|
+
ok: false,
|
|
133
|
+
error: "Source not found"
|
|
134
|
+
}, 404);
|
|
135
|
+
});
|
|
136
|
+
authenticated.post("/api/work-discovery/sources/directories/:sourceId/runs", limited, async (c) => {
|
|
137
|
+
const idempotencyKey = stringField(await c.req.json().catch(() => null), "idempotencyKey");
|
|
138
|
+
if (!idempotencyKey) return c.json({
|
|
139
|
+
ok: false,
|
|
140
|
+
error: "Missing idempotencyKey"
|
|
141
|
+
}, 400);
|
|
142
|
+
try {
|
|
143
|
+
const run = await service.rescanDirectorySource({
|
|
144
|
+
id: c.req.param("sourceId"),
|
|
145
|
+
idempotencyKey
|
|
146
|
+
});
|
|
147
|
+
return c.json({
|
|
148
|
+
ok: true,
|
|
149
|
+
run
|
|
150
|
+
}, 202);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
return c.json({
|
|
153
|
+
ok: false,
|
|
154
|
+
error: error instanceof Error ? error.message : String(error)
|
|
155
|
+
}, 400);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
authenticated.post("/api/work-discovery/sources/directories/check", limited, async (c) => {
|
|
159
|
+
return c.json({
|
|
160
|
+
ok: true,
|
|
161
|
+
checks: await service.checkDirectorySources()
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
authenticated.post("/api/work-discovery/sources/directories/:sourceId/refresh-if-changed", limited, async (c) => {
|
|
165
|
+
const idempotencyKey = stringField(await c.req.json().catch(() => null), "idempotencyKey");
|
|
166
|
+
if (!idempotencyKey) return c.json({
|
|
167
|
+
ok: false,
|
|
168
|
+
error: "Missing idempotencyKey"
|
|
169
|
+
}, 400);
|
|
170
|
+
try {
|
|
171
|
+
const result = await service.refreshDirectorySourceIfChanged({
|
|
172
|
+
id: c.req.param("sourceId"),
|
|
173
|
+
idempotencyKey
|
|
174
|
+
});
|
|
175
|
+
return c.json({
|
|
176
|
+
ok: true,
|
|
177
|
+
...result
|
|
178
|
+
}, result.changed ? 202 : 200);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
return c.json({
|
|
181
|
+
ok: false,
|
|
182
|
+
error: error instanceof Error ? error.message : String(error)
|
|
183
|
+
}, 400);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
authenticated.post("/api/work-discovery/personal-context/import", limited, async (c) => {
|
|
187
|
+
if (!service.isEnabled()) return c.json({
|
|
188
|
+
ok: false,
|
|
189
|
+
error: "Work discovery is disabled"
|
|
190
|
+
}, 404);
|
|
191
|
+
const body = await c.req.json().catch(() => null);
|
|
192
|
+
const items = body && typeof body === "object" && Array.isArray(body.items) ? body.items.slice(0, 150) : [];
|
|
193
|
+
const runId = stringField(body, "runId");
|
|
194
|
+
if (!items.length) return c.json({
|
|
195
|
+
ok: false,
|
|
196
|
+
error: "No personal context was provided"
|
|
197
|
+
}, 400);
|
|
198
|
+
try {
|
|
199
|
+
const result = await service.importPersonalContext(items, c.req.raw.signal, runId || void 0);
|
|
200
|
+
return c.json({
|
|
201
|
+
ok: true,
|
|
202
|
+
...result
|
|
203
|
+
}, 201);
|
|
204
|
+
} catch (error) {
|
|
205
|
+
return c.json({
|
|
206
|
+
ok: false,
|
|
207
|
+
error: error instanceof Error ? error.message : String(error)
|
|
208
|
+
}, 400);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
authenticated.post("/api/work-discovery/personal-context/profile", limited, async (c) => {
|
|
212
|
+
const body = await c.req.json().catch(() => null);
|
|
213
|
+
const decisions = (body && typeof body === "object" && Array.isArray(body.decisions) ? body.decisions : []).flatMap((value) => {
|
|
214
|
+
if (!value || typeof value !== "object") return [];
|
|
215
|
+
const item = value;
|
|
216
|
+
const memoryRecordId = typeof item.memoryRecordId === "string" ? item.memoryRecordId.trim() : "";
|
|
217
|
+
const status = item.status === "accepted" || item.status === "edited" || item.status === "rejected" ? item.status : void 0;
|
|
218
|
+
if (!memoryRecordId || !status) return [];
|
|
219
|
+
return [{
|
|
220
|
+
memoryRecordId,
|
|
221
|
+
status,
|
|
222
|
+
...typeof item.statement === "string" ? { statement: item.statement } : {}
|
|
223
|
+
}];
|
|
224
|
+
}).slice(0, 10);
|
|
225
|
+
if (!decisions.length) return c.json({
|
|
226
|
+
ok: false,
|
|
227
|
+
error: "At least one valid decision is required"
|
|
228
|
+
}, 400);
|
|
229
|
+
return c.json({
|
|
230
|
+
ok: true,
|
|
231
|
+
decisions: service.updatePersonalContextProfile({ decisions })
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
authenticated.post("/api/work-discovery/quick-runs", limited, async (c) => {
|
|
235
|
+
if (!service.isEnabled()) return c.json({
|
|
236
|
+
ok: false,
|
|
237
|
+
error: "Work discovery is disabled"
|
|
238
|
+
}, 404);
|
|
239
|
+
const idempotencyKey = stringField(await c.req.json().catch(() => null), "idempotencyKey");
|
|
240
|
+
if (!idempotencyKey) return c.json({
|
|
241
|
+
ok: false,
|
|
242
|
+
error: "Missing idempotencyKey"
|
|
243
|
+
}, 400);
|
|
244
|
+
try {
|
|
245
|
+
const run = await service.startQuickRun({ idempotencyKey });
|
|
246
|
+
return c.json({
|
|
247
|
+
ok: true,
|
|
248
|
+
run
|
|
249
|
+
}, 202);
|
|
250
|
+
} catch (error) {
|
|
251
|
+
return c.json({
|
|
252
|
+
ok: false,
|
|
253
|
+
error: error instanceof Error ? error.message : String(error)
|
|
254
|
+
}, 400);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
80
257
|
authenticated.post("/api/work-discovery/runs", limited, async (c) => {
|
|
81
258
|
if (!service.isEnabled()) return c.json({
|
|
82
259
|
ok: false,
|
|
@@ -117,6 +294,250 @@ function registerWorkDiscoveryRoutes(authenticated, deps) {
|
|
|
117
294
|
error: "Run not found"
|
|
118
295
|
}, 404);
|
|
119
296
|
});
|
|
297
|
+
authenticated.get("/api/work-discovery/runs/:runId/investigation", (c) => {
|
|
298
|
+
const result = service.getInvestigation(c.req.param("runId"));
|
|
299
|
+
return result ? c.json({
|
|
300
|
+
ok: true,
|
|
301
|
+
...result
|
|
302
|
+
}) : c.json({
|
|
303
|
+
ok: false,
|
|
304
|
+
error: "Investigation not found"
|
|
305
|
+
}, 404);
|
|
306
|
+
});
|
|
307
|
+
authenticated.get("/api/work-understanding/threads", (c) => {
|
|
308
|
+
const projectId = c.req.query("projectId")?.trim();
|
|
309
|
+
const limit = Number(c.req.query("limit"));
|
|
310
|
+
return c.json({
|
|
311
|
+
ok: true,
|
|
312
|
+
threads: service.listWorkThreads({
|
|
313
|
+
...projectId ? { projectId } : {},
|
|
314
|
+
...Number.isFinite(limit) ? { limit } : {}
|
|
315
|
+
})
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
authenticated.get("/api/work-understanding/metrics", (c) => c.json({
|
|
319
|
+
ok: true,
|
|
320
|
+
metrics: service.getUnderstandingMetrics()
|
|
321
|
+
}));
|
|
322
|
+
authenticated.get("/api/work-understanding/sources/:sourceId/lineage", (c) => {
|
|
323
|
+
const lineage = service.getSourceLineage(c.req.param("sourceId"));
|
|
324
|
+
return lineage ? c.json({
|
|
325
|
+
ok: true,
|
|
326
|
+
lineage
|
|
327
|
+
}) : c.json({
|
|
328
|
+
ok: false,
|
|
329
|
+
error: "Source not found"
|
|
330
|
+
}, 404);
|
|
331
|
+
});
|
|
332
|
+
authenticated.delete("/api/work-understanding/sources/:sourceId/derived-data", limited, (c) => {
|
|
333
|
+
const deleted = service.deleteSourceDerivedData(c.req.param("sourceId"));
|
|
334
|
+
return deleted ? c.json({
|
|
335
|
+
ok: true,
|
|
336
|
+
deleted
|
|
337
|
+
}) : c.json({
|
|
338
|
+
ok: false,
|
|
339
|
+
error: "Source not found"
|
|
340
|
+
}, 404);
|
|
341
|
+
});
|
|
342
|
+
authenticated.get("/api/work-understanding/threads/:threadId", (c) => {
|
|
343
|
+
const thread = service.getWorkThread(c.req.param("threadId"));
|
|
344
|
+
return thread ? c.json({
|
|
345
|
+
ok: true,
|
|
346
|
+
thread
|
|
347
|
+
}) : c.json({
|
|
348
|
+
ok: false,
|
|
349
|
+
error: "Work thread not found"
|
|
350
|
+
}, 404);
|
|
351
|
+
});
|
|
352
|
+
authenticated.patch("/api/work-understanding/threads/:threadId", limited, async (c) => {
|
|
353
|
+
const body = await c.req.json().catch(() => null);
|
|
354
|
+
const decision = stringField(body, "decision");
|
|
355
|
+
if (![
|
|
356
|
+
"confirmed",
|
|
357
|
+
"corrected",
|
|
358
|
+
"rejected",
|
|
359
|
+
"paused",
|
|
360
|
+
"completed"
|
|
361
|
+
].includes(decision)) return c.json({
|
|
362
|
+
ok: false,
|
|
363
|
+
error: "Invalid work thread decision"
|
|
364
|
+
}, 400);
|
|
365
|
+
const thread = service.updateWorkThread({
|
|
366
|
+
id: c.req.param("threadId"),
|
|
367
|
+
decision,
|
|
368
|
+
...stringField(body, "correctedTitle") ? { correctedTitle: stringField(body, "correctedTitle") } : {},
|
|
369
|
+
...stringField(body, "correctedSummary") ? { correctedSummary: stringField(body, "correctedSummary") } : {}
|
|
370
|
+
});
|
|
371
|
+
return thread ? c.json({
|
|
372
|
+
ok: true,
|
|
373
|
+
thread
|
|
374
|
+
}) : c.json({
|
|
375
|
+
ok: false,
|
|
376
|
+
error: "Work thread not found"
|
|
377
|
+
}, 404);
|
|
378
|
+
});
|
|
379
|
+
authenticated.get("/api/focuses", (c) => {
|
|
380
|
+
return c.json({
|
|
381
|
+
ok: true,
|
|
382
|
+
focuses: focuses.list({ includeUnreviewed: c.req.query("includeUnreviewed") === "true" })
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
authenticated.post("/api/focuses/:focusId/confirm", limited, (c) => {
|
|
386
|
+
const focus = focuses.confirm(c.req.param("focusId"));
|
|
387
|
+
return focus ? c.json({
|
|
388
|
+
ok: true,
|
|
389
|
+
focus
|
|
390
|
+
}) : c.json({
|
|
391
|
+
ok: false,
|
|
392
|
+
error: "Focus not found"
|
|
393
|
+
}, 404);
|
|
394
|
+
});
|
|
395
|
+
authenticated.post("/api/focuses/:focusId/watches/trial", limited, async (c) => {
|
|
396
|
+
const requestedKind = stringField(await c.req.json().catch(() => null), "kind");
|
|
397
|
+
const kind = [
|
|
398
|
+
"progress",
|
|
399
|
+
"staleness",
|
|
400
|
+
"deadline",
|
|
401
|
+
"intelligence"
|
|
402
|
+
].includes(requestedKind) ? requestedKind : void 0;
|
|
403
|
+
try {
|
|
404
|
+
const result = await focuses.activateTrial({
|
|
405
|
+
threadId: c.req.param("focusId"),
|
|
406
|
+
...kind ? { kind } : {}
|
|
407
|
+
});
|
|
408
|
+
return c.json({
|
|
409
|
+
ok: true,
|
|
410
|
+
...result
|
|
411
|
+
}, 201);
|
|
412
|
+
} catch (error) {
|
|
413
|
+
return c.json({
|
|
414
|
+
ok: false,
|
|
415
|
+
error: error instanceof Error ? error.message : String(error)
|
|
416
|
+
}, 400);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
authenticated.post("/api/focuses/:focusId/watches/:watchId/pause", limited, async (c) => {
|
|
420
|
+
const watch = focuses.list({ includeUnreviewed: true }).find((item) => item.id === c.req.param("focusId"))?.watches.find((item) => item.id === c.req.param("watchId"));
|
|
421
|
+
if (!watch) return c.json({
|
|
422
|
+
ok: false,
|
|
423
|
+
error: "Watch not found"
|
|
424
|
+
}, 404);
|
|
425
|
+
const paused = await focuses.pauseWatch(watch.id);
|
|
426
|
+
return c.json({
|
|
427
|
+
ok: true,
|
|
428
|
+
watch: paused
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
authenticated.patch("/api/proactive/insights/:insightId", limited, async (c) => {
|
|
432
|
+
const requested = stringField(await c.req.json().catch(() => null), "status");
|
|
433
|
+
const status = requested === "read" || requested === "dismissed" ? requested : void 0;
|
|
434
|
+
if (!status) return c.json({
|
|
435
|
+
ok: false,
|
|
436
|
+
error: "Status must be read or dismissed"
|
|
437
|
+
}, 400);
|
|
438
|
+
const insight = setProactiveInsightStatus(c.req.param("insightId"), status, Date.now(), "unread");
|
|
439
|
+
if (!insight) return c.json({
|
|
440
|
+
ok: false,
|
|
441
|
+
error: "Insight was already handled or not found"
|
|
442
|
+
}, 409);
|
|
443
|
+
if (status === "dismissed") await focuses.recordInsightFeedback(insight.watchId, false);
|
|
444
|
+
return c.json({
|
|
445
|
+
ok: true,
|
|
446
|
+
insight
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
authenticated.post("/api/proactive/insights/:insightId/approve", limited, async (c) => {
|
|
450
|
+
const insight = getProactiveInsight(c.req.param("insightId"));
|
|
451
|
+
if (!insight) return c.json({
|
|
452
|
+
ok: false,
|
|
453
|
+
error: "Insight not found"
|
|
454
|
+
}, 404);
|
|
455
|
+
if (insight.status !== "unread") return c.json({
|
|
456
|
+
ok: false,
|
|
457
|
+
error: `Insight is ${insight.status}`
|
|
458
|
+
}, 409);
|
|
459
|
+
const focus = focuses.list({ includeUnreviewed: true }).find((item) => item.watches.some((watch) => watch.id === insight.watchId));
|
|
460
|
+
if (!focus) return c.json({
|
|
461
|
+
ok: false,
|
|
462
|
+
error: "Focus not found"
|
|
463
|
+
}, 404);
|
|
464
|
+
if (!claimProactiveInsightApproval(insight.id)) return c.json({
|
|
465
|
+
ok: false,
|
|
466
|
+
error: "Insight was already handled"
|
|
467
|
+
}, 409);
|
|
468
|
+
let automationId;
|
|
469
|
+
try {
|
|
470
|
+
const automation = await deps.service.automationServiceInstance.create({
|
|
471
|
+
name: `Investigate: ${insight.title}`.slice(0, 200),
|
|
472
|
+
description: `User-approved read-only proposal from proactive insight ${insight.id}.`,
|
|
473
|
+
...focus.projectIds[0] ? { projectId: focus.projectIds[0] } : {},
|
|
474
|
+
trigger: { kind: "manual" },
|
|
475
|
+
action: {
|
|
476
|
+
kind: "agent",
|
|
477
|
+
instruction: [
|
|
478
|
+
"The user approved this evidence-backed proposal for read-only investigation and preparation.",
|
|
479
|
+
`Focus: ${focus.title}`,
|
|
480
|
+
`Observed change: ${insight.summary}`,
|
|
481
|
+
`Why it matters: ${insight.whyItMatters}`,
|
|
482
|
+
`Approved next step: ${insight.nextAction}`,
|
|
483
|
+
`Evidence: ${JSON.stringify(insight.evidence)}`,
|
|
484
|
+
"Investigate or prepare the requested material. Return a concise result with evidence. Do not modify files or external systems."
|
|
485
|
+
].join("\n"),
|
|
486
|
+
timeoutSeconds: 300
|
|
487
|
+
},
|
|
488
|
+
safety: { mode: "suggest_only" },
|
|
489
|
+
afterRun: { kind: "none" },
|
|
490
|
+
reliability: {
|
|
491
|
+
timeoutSeconds: 300,
|
|
492
|
+
disableAfterConsecutiveFailures: 1
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
automationId = automation.id;
|
|
496
|
+
const run = await deps.service.automationServiceInstance.runNow(automation.id);
|
|
497
|
+
await focuses.recordInsightFeedback(insight.watchId, true);
|
|
498
|
+
return c.json({
|
|
499
|
+
ok: true,
|
|
500
|
+
automationId: automation.id,
|
|
501
|
+
runId: run.id
|
|
502
|
+
}, 202);
|
|
503
|
+
} catch (error) {
|
|
504
|
+
if (automationId) await deps.service.automationServiceInstance.remove(automationId);
|
|
505
|
+
setProactiveInsightStatus(insight.id, "unread", Date.now(), "approved");
|
|
506
|
+
return c.json({
|
|
507
|
+
ok: false,
|
|
508
|
+
error: error instanceof Error ? error.message : String(error)
|
|
509
|
+
}, 500);
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
authenticated.post("/api/focuses/:focusId/calendar/:signalId/prepare", limited, async (c) => {
|
|
513
|
+
const focus = focuses.list().find((item) => item.id === c.req.param("focusId"));
|
|
514
|
+
if (!focus) return c.json({
|
|
515
|
+
ok: false,
|
|
516
|
+
error: "Focus not found"
|
|
517
|
+
}, 404);
|
|
518
|
+
const signal = listFocusCalendarSignals([focus]).find((item) => item.id === c.req.param("signalId"));
|
|
519
|
+
if (!signal) return c.json({
|
|
520
|
+
ok: false,
|
|
521
|
+
error: "Calendar event not found"
|
|
522
|
+
}, 404);
|
|
523
|
+
const eventContext = `${signal.title} at ${new Date(signal.startsAt).toISOString()}`;
|
|
524
|
+
try {
|
|
525
|
+
const result = await focuses.activateTrial({
|
|
526
|
+
threadId: focus.id,
|
|
527
|
+
kind: "deadline",
|
|
528
|
+
eventContext
|
|
529
|
+
});
|
|
530
|
+
return c.json({
|
|
531
|
+
ok: true,
|
|
532
|
+
...result
|
|
533
|
+
});
|
|
534
|
+
} catch (error) {
|
|
535
|
+
return c.json({
|
|
536
|
+
ok: false,
|
|
537
|
+
error: error instanceof Error ? error.message : String(error)
|
|
538
|
+
}, 500);
|
|
539
|
+
}
|
|
540
|
+
});
|
|
120
541
|
authenticated.post("/api/work-discovery/runs/:runId/cancel", (c) => {
|
|
121
542
|
const run = service.cancelRun(c.req.param("runId"));
|
|
122
543
|
return run ? c.json({
|
|
@@ -169,6 +590,36 @@ function registerWorkDiscoveryRoutes(authenticated, deps) {
|
|
|
169
590
|
}, 400);
|
|
170
591
|
}
|
|
171
592
|
});
|
|
593
|
+
authenticated.post("/api/work-discovery/runs/:runId/profile", limited, async (c) => {
|
|
594
|
+
const body = await c.req.json().catch(() => null);
|
|
595
|
+
const decisions = (body && typeof body === "object" && Array.isArray(body.decisions) ? body.decisions : []).flatMap((value) => {
|
|
596
|
+
if (!value || typeof value !== "object") return [];
|
|
597
|
+
const item = value;
|
|
598
|
+
const id = typeof item.id === "string" ? item.id.trim() : "";
|
|
599
|
+
const status = item.status === "accepted" || item.status === "edited" || item.status === "rejected" ? item.status : void 0;
|
|
600
|
+
if (!id || !status) return [];
|
|
601
|
+
return [{
|
|
602
|
+
id,
|
|
603
|
+
status,
|
|
604
|
+
...typeof item.statement === "string" ? { statement: item.statement } : {}
|
|
605
|
+
}];
|
|
606
|
+
}).slice(0, 10);
|
|
607
|
+
if (!decisions.length) return c.json({
|
|
608
|
+
ok: false,
|
|
609
|
+
error: "At least one valid decision is required"
|
|
610
|
+
}, 400);
|
|
611
|
+
const run = service.updateProfileCandidates({
|
|
612
|
+
runId: c.req.param("runId"),
|
|
613
|
+
decisions
|
|
614
|
+
});
|
|
615
|
+
return run ? c.json({
|
|
616
|
+
ok: true,
|
|
617
|
+
run
|
|
618
|
+
}) : c.json({
|
|
619
|
+
ok: false,
|
|
620
|
+
error: "Profile candidates not found"
|
|
621
|
+
}, 404);
|
|
622
|
+
});
|
|
172
623
|
authenticated.post("/api/work-discovery/runs/:runId/suggestions/:suggestionId/select", (c) => {
|
|
173
624
|
return service.selectSuggestion(c.req.param("runId"), c.req.param("suggestionId")) ? c.json({ ok: true }) : c.json({
|
|
174
625
|
ok: false,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Context } from 'hono';
|
|
2
2
|
import type { GatewayService } from '../service.js';
|
|
3
|
+
/** Close long-lived gateway event streams before the HTTP server starts draining. */
|
|
4
|
+
export declare function closeAllEventStreams(): void;
|
|
3
5
|
export interface SSEHandlerConfig {
|
|
4
6
|
service: GatewayService;
|
|
5
7
|
maxSseConnections?: number;
|
|
@@ -9,6 +9,10 @@ import { streamSSE } from "hono/streaming";
|
|
|
9
9
|
init_logger();
|
|
10
10
|
const log = createLogger("Gateway:SSE");
|
|
11
11
|
const activeConnections = /* @__PURE__ */ new Map();
|
|
12
|
+
/** Close long-lived gateway event streams before the HTTP server starts draining. */
|
|
13
|
+
function closeAllEventStreams() {
|
|
14
|
+
for (const controller of activeConnections.values()) controller.abort();
|
|
15
|
+
}
|
|
12
16
|
function isValidAgentRequest(body) {
|
|
13
17
|
if (!body || typeof body !== "object") return false;
|
|
14
18
|
const b = body;
|
|
@@ -341,17 +345,20 @@ function createEventsSSEHandler(config) {
|
|
|
341
345
|
}
|
|
342
346
|
}, 3e4);
|
|
343
347
|
await new Promise((resolve) => {
|
|
344
|
-
|
|
348
|
+
const finish = () => {
|
|
345
349
|
aborted = true;
|
|
346
350
|
clearInterval(keepAlive);
|
|
347
351
|
cleanup();
|
|
348
352
|
activeConnections.delete(sessionId);
|
|
349
353
|
log.debug({ sessionId }, "Event stream disconnected");
|
|
350
354
|
resolve();
|
|
351
|
-
}
|
|
355
|
+
};
|
|
356
|
+
stream.onAbort(finish);
|
|
357
|
+
abortController.signal.addEventListener("abort", () => stream.abort(), { once: true });
|
|
358
|
+
if (abortController.signal.aborted) stream.abort();
|
|
352
359
|
});
|
|
353
360
|
});
|
|
354
361
|
};
|
|
355
362
|
}
|
|
356
363
|
//#endregion
|
|
357
|
-
export { createAgentResumeHandler, createAgentSSEHandler, createEventsSSEHandler, createSendHandler };
|
|
364
|
+
export { closeAllEventStreams, createAgentResumeHandler, createAgentSSEHandler, createEventsSSEHandler, createSendHandler };
|
|
@@ -15,7 +15,8 @@ async function runGatewayLoop(opts) {
|
|
|
15
15
|
let lock = await acquireGatewayLock(opts.configPath, { port: opts.port });
|
|
16
16
|
let server = null;
|
|
17
17
|
let shuttingDown = false;
|
|
18
|
-
let
|
|
18
|
+
let forceCloseRequested = false;
|
|
19
|
+
let loopResolver = null;
|
|
19
20
|
const cleanupSignals = () => {
|
|
20
21
|
process.removeListener("SIGTERM", onSigterm);
|
|
21
22
|
process.removeListener("SIGINT", onSigint);
|
|
@@ -51,20 +52,27 @@ async function runGatewayLoop(opts) {
|
|
|
51
52
|
return;
|
|
52
53
|
}
|
|
53
54
|
shuttingDown = false;
|
|
54
|
-
|
|
55
|
+
forceCloseRequested = false;
|
|
56
|
+
loopResolver?.("restart");
|
|
55
57
|
};
|
|
56
58
|
const handleStopAfterClose = async () => {
|
|
57
59
|
await releaseLock();
|
|
58
|
-
|
|
60
|
+
loopResolver?.("stop");
|
|
59
61
|
};
|
|
60
62
|
const DRAIN_TIMEOUT_MS = 3e4;
|
|
61
63
|
const SHUTDOWN_TIMEOUT_MS = 15e3;
|
|
62
64
|
const requestShutdown = (action, signal) => {
|
|
63
|
-
if (shuttingDown)
|
|
65
|
+
if (shuttingDown) {
|
|
66
|
+
if (!forceCloseRequested && (signal === "SIGINT" || signal === "SIGTERM")) {
|
|
67
|
+
forceCloseRequested = true;
|
|
68
|
+
console.warn(`[GatewayRunLoop] Received ${signal} again; force closing connections`);
|
|
69
|
+
server?.forceCloseConnections();
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
64
73
|
shuttingDown = true;
|
|
65
74
|
const isRestart = action === "restart";
|
|
66
75
|
console.log(`[GatewayRunLoop] Received ${signal}; ${isRestart ? "restarting" : "shutting down"}`);
|
|
67
|
-
cleanupSignals();
|
|
68
76
|
const forceExitMs = isRestart ? DRAIN_TIMEOUT_MS + SHUTDOWN_TIMEOUT_MS : SHUTDOWN_TIMEOUT_MS;
|
|
69
77
|
const forceExitTimer = setTimeout(() => {
|
|
70
78
|
console.error("[GatewayRunLoop] Shutdown timed out; force exiting");
|
|
@@ -129,9 +137,11 @@ async function runGatewayLoop(opts) {
|
|
|
129
137
|
exitProcess(1);
|
|
130
138
|
return;
|
|
131
139
|
}
|
|
132
|
-
await new Promise((resolve) => {
|
|
133
|
-
|
|
140
|
+
const action = await new Promise((resolve) => {
|
|
141
|
+
loopResolver = resolve;
|
|
134
142
|
});
|
|
143
|
+
loopResolver = null;
|
|
144
|
+
if (action === "stop") return;
|
|
135
145
|
console.log("[GatewayRunLoop] Restart signal received, restarting gateway...");
|
|
136
146
|
}
|
|
137
147
|
} finally {
|
|
@@ -2,6 +2,7 @@ import { resolveGatewayBindHost, resolveGatewayListenHosts } from "../config/gat
|
|
|
2
2
|
import { ensureXopcCliOnPath } from "../infra/path-env.js";
|
|
3
3
|
import { resolveGatewayListenPlan } from "./listen.js";
|
|
4
4
|
import { GatewayService } from "./service.js";
|
|
5
|
+
import { closeAllEventStreams } from "./hono/sse.js";
|
|
5
6
|
import { handleSiteShareUpgrade } from "../share/site-share-router.js";
|
|
6
7
|
import { createHonoApp } from "./hono/app.js";
|
|
7
8
|
import { serve } from "@hono/node-server";
|
|
@@ -94,6 +95,7 @@ var GatewayServer = class {
|
|
|
94
95
|
}
|
|
95
96
|
async stop() {
|
|
96
97
|
console.log("[GatewayServer] Stopping gateway server...");
|
|
98
|
+
closeAllEventStreams();
|
|
97
99
|
const closeServer = async (server) => {
|
|
98
100
|
if (!server) return;
|
|
99
101
|
const forceClose = setTimeout(() => {
|
|
@@ -113,6 +115,13 @@ var GatewayServer = class {
|
|
|
113
115
|
await this.service.stop();
|
|
114
116
|
console.log("[GatewayServer] Gateway server stopped");
|
|
115
117
|
}
|
|
118
|
+
forceCloseConnections() {
|
|
119
|
+
const close = (server) => {
|
|
120
|
+
server?.closeAllConnections?.();
|
|
121
|
+
};
|
|
122
|
+
close(this.server);
|
|
123
|
+
for (const extra of this.extraServers) close(extra);
|
|
124
|
+
}
|
|
116
125
|
get isRunning() {
|
|
117
126
|
return this.server !== void 0;
|
|
118
127
|
}
|
|
@@ -61,6 +61,7 @@ export declare class GatewayService {
|
|
|
61
61
|
private connectedKnowledgeCoordinator;
|
|
62
62
|
private stopAutomationProductEventBridge;
|
|
63
63
|
private stopSessionTranscriptAutomationEvents;
|
|
64
|
+
private stopFocusTrialExpiryReconciler;
|
|
64
65
|
/**
|
|
65
66
|
* Webchat agent invocation surface (`runAgent`, `abortAgentRun`, `steer*`,
|
|
66
67
|
* `submitClarifyResponse`, clarify-bridge dispatch). Owns the
|
|
@@ -267,6 +268,7 @@ export declare class GatewayService {
|
|
|
267
268
|
processDirect(content: string, sessionKey?: string): Promise<string>;
|
|
268
269
|
subscribe(sessionId: string, listener: (event: ServiceEvent) => Promise<void> | void): () => void;
|
|
269
270
|
emit(type: string, payload: unknown): void;
|
|
271
|
+
private handleAutomationRunCompleted;
|
|
270
272
|
private startAutomationProductEventBridge;
|
|
271
273
|
/** Replay events since `lastEventId` for SSE reconnection. */
|
|
272
274
|
getEventsSince(sessionId: string, lastEventId: string): ServiceEvent[];
|