@xopcai/xopc 0.0.158 → 0.0.160
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/dashscope/src/image-generation-provider.js +1 -1
- package/dist/extensions/fal/src/image-generation-provider.js +1 -1
- package/dist/extensions/google/src/image-generation-provider.js +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{action-boundary-settings-panel-2p1f4-KA.js → action-boundary-settings-panel-CY3sizqC.js} +1 -1
- package/dist/gateway/static/root/assets/{activity-store-C-57rvkX.js → activity-store-3Cso_2qm.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BIl6X2b_.js → agent-avatar-dicebear-B_27aaZQ.js} +1 -1
- package/dist/gateway/static/root/assets/agents-B1l3iii6.js +2 -0
- package/dist/gateway/static/root/assets/{api-BjcBIV2O.js → api-BbzB57CJ.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-CZxw161D.js → app-management-settings-panel-DfmyWcyh.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-CRXNlDhH.js → appearance-settings-B2v0c-K-.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-DaNFLsp0.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-CpVshjXu.js → archive-plugin-DL5Lnx0C.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-M9WqqJnQ.js → attachment-load-InuP-ZjQ.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-CYMsAsYf.js → automation-suggestion-card-CahGuh7B.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-Cd0gMx6p.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-Cjn3zb4u.js → binary-plugins-CdOTGYVu.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-BPKdquaV.js → block-editor-D6ShK1S-.js} +3 -3
- package/dist/gateway/static/root/assets/browser-settings-page-BmUYDL2Z.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-Bzkcsyzn.js → capability-presets-api-BzoLm52Z.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-settings-panel-D4rH4Dax.js → capability-presets-settings-panel-CE7mrWtS.js} +2 -2
- package/dist/gateway/static/root/assets/{channel-recipient-api-CFqrSi5K.js → channel-recipient-api-DbgydMjl.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-D1_gcAHK.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-Cqfqv59m.js +1 -0
- package/dist/gateway/static/root/assets/{connector-logo-ljPKdYKh.js → connector-logo-C3bJC9nt.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-CaLBi6Na.js +2 -0
- package/dist/gateway/static/root/assets/desktop-pet-settings-BrHVvbBw.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-sprite-CFd1jGLJ.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-wacrnJJ1.js +1 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-Dmy1035j.js → directory-picker-path-field-BPI969w-.js} +1 -1
- package/dist/gateway/static/root/assets/{dreaming-settings-tT4WkBkd.js → dreaming-settings-iw6QXD45.js} +3 -3
- package/dist/gateway/static/root/assets/{extension-debug-page-CL79q8AH.js → extension-debug-page-rCHyhEr6.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-hq80wkFI.js → extension-page-C4gQsAPt.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BV-9GDfT.js → extension-settings-page-DB_3eik7.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-CFqoN2NW.js → gateway-config-api-Cdt38iF2.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-DM4BgyGw.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-cxQ94qMm.js → gateway-startup-retry-ChRFNjj_.js} +1 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-BQamcEmF.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-DZUQ0Lf1.js +4 -0
- package/dist/gateway/static/root/assets/{goals-config-api--qJ4qlRq.js → goals-config-api-RvljeEm6.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-BtxVD27C.js +2 -0
- package/dist/gateway/static/root/assets/{goals-settings-h7x9h5lb.js → goals-settings-DMGV4S4L.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-71IhhM3s.js → heartbeat-settings-B8r5EXc5.js} +1 -1
- package/dist/gateway/static/root/assets/index-CHKe7nwC.js +197 -0
- package/dist/gateway/static/root/assets/index-CXhfUIjh.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BZGmHvLu.js → keyboard-shortcuts-settings-1-LYibQ4.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-CTTfyFe3.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-CiLqrMIh.js → local-apps-page-CC4vtUUI.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-C_M71MK9.js → locale-store-CPbGB_6l.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-Bn_taxBV.js +7 -0
- package/dist/gateway/static/root/assets/{markdown-split-r1HPWfCU.js → markdown-split-CprEIf8A.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-D74kd3hn.js → media-plugins-BXt_Sj2O.js} +1 -1
- package/dist/gateway/static/root/assets/{messages-BuT0SR1R.js → messages-DDsiqdJS.js} +4 -4
- package/dist/gateway/static/root/assets/models-hub-panel-DJReXRP9.js +2 -0
- package/dist/gateway/static/root/assets/{notes-page-Dgu02514.js → notes-page-Wf5-eyyM.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-BbdjzYzs.js +4 -0
- package/dist/gateway/static/root/assets/{page-tabs-hinRdDy3.js → page-tabs-dtpK_4Fs.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-C7fw3oGy.js → preview-open-alternatives-DijtEVxG.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-BwG8SOzI.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-nUlyUtxh.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-rf7bjije.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-C3aU8-vO.js → save-bar-controls-qhDisdqY.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-DjegExFD.js → schedule-field-AhJ50NoT.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-DLubnsql.js → schema-form-AuJg2CXU.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-CSoHUe38.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-Db976IEa.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-3syQHsNR.js → settings-collapsible-section-D5ganr_X.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-D4DCZzKg.js → settings-form-section-Db3cKRsg.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-c5K85ylS.js → settings-loading-skeleton-D05YKT2x.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-DNiDlaRq.js → settings-page-DYfXmfTx.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-iTRY1Hy-.js → settings-page-layout-Cv_Cu4I5.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-wBWOFpvw.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-B88Afrhe.js → share-preview-page-CYkZVuse.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-BAMma1-P.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-TWxFFWxM.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-Ca2RA29R.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-DtpTq837.js → system-settings-panel-C1Ng5bXh.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-C8ohIHhw.js → theme-store-LMHIDR9n.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-ndEnUP3b.js → typed-models-lib-Ch2Bdm_5.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-BWzou5ar.js → use-channel-catalog-CZnaMOeL.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-CTumZa0y.js +1 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-DdlMxeqO.js +1 -0
- package/dist/gateway/static/root/assets/work-discovery-page-CcfZhdBi.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page--wxLw1p_.js +1 -0
- package/dist/gateway/static/root/assets/work-page-DyS0XBpV.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-ukmpkj66.js → workflow-run-setup-panel-BFk8TvTw.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-CVVJd4gy.js +11 -0
- package/dist/gateway/static/root/index.html +7 -7
- package/dist/package.js +1 -1
- package/dist/src/agent/embedded/session-tool-result-guard.js +14 -0
- package/dist/src/agent/image/generation/openai-compatible-image-provider.js +1 -1
- package/dist/src/agent/service/process-direct-streaming.js +1 -1
- package/dist/src/agent/tools/image-generate-tool.js +10 -3
- package/dist/src/agent/tools/send-media.js +20 -70
- package/dist/src/agent/tools/tool-media.d.ts +9 -0
- package/dist/src/agent/tools/tool-media.js +58 -0
- 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/agent-run-relay.d.ts +2 -0
- package/dist/src/gateway/agent-run-relay.js +114 -14
- 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 +40 -19
- package/dist/src/gateway/chat-stream/thinking-delta-coalescer.d.ts +7 -0
- package/dist/src/gateway/chat-stream/thinking-delta-coalescer.js +103 -0
- 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 +1 -1
- package/dist/src/gateway/hono/sse.js +1 -1
- package/dist/src/gateway/service/run-gateway-agent.js +5 -1
- package/dist/src/gateway/service.d.ts +2 -0
- package/dist/src/gateway/service.js +27 -2
- package/dist/src/media/session-references.js +9 -6
- 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/session/client-history.d.ts +1 -0
- package/dist/src/session/client-history.js +56 -12
- package/dist/src/session/types.d.ts +1 -0
- package/dist/src/storage/sqlite/knowledge-repository.d.ts +1 -0
- package/dist/src/storage/sqlite/knowledge-repository.js +4 -0
- package/dist/src/storage/sqlite/migrations/047_work_discovery_sources.sql +16 -0
- package/dist/src/storage/sqlite/migrations/048_work_understanding_investigations.sql +40 -0
- package/dist/src/storage/sqlite/migrations/049_work_understanding_threads.sql +52 -0
- package/dist/src/storage/sqlite/migrations/050_work_discovery_source_refreshes.sql +15 -0
- package/dist/src/storage/sqlite/migrations/051_focus_watches.sql +26 -0
- package/dist/src/storage/sqlite/migrations/052_proactive_insights.sql +25 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/work-discovery/analyzer.d.ts +12 -1
- package/dist/src/work-discovery/analyzer.js +126 -7
- package/dist/src/work-discovery/candidate-discovery.d.ts +17 -0
- package/dist/src/work-discovery/candidate-discovery.js +279 -0
- package/dist/src/work-discovery/evaluation.d.ts +11 -0
- package/dist/src/work-discovery/evaluation.js +17 -0
- package/dist/src/work-discovery/governance.d.ts +25 -0
- package/dist/src/work-discovery/governance.js +96 -0
- package/dist/src/work-discovery/incremental-repository.d.ts +13 -0
- package/dist/src/work-discovery/incremental-repository.js +55 -0
- package/dist/src/work-discovery/incremental.d.ts +2 -0
- package/dist/src/work-discovery/incremental.js +7 -0
- package/dist/src/work-discovery/index.d.ts +10 -0
- package/dist/src/work-discovery/index.js +13 -3
- package/dist/src/work-discovery/investigation-repository.d.ts +16 -0
- package/dist/src/work-discovery/investigation-repository.js +122 -0
- package/dist/src/work-discovery/investigator.d.ts +55 -0
- package/dist/src/work-discovery/investigator.js +358 -0
- package/dist/src/work-discovery/probe.d.ts +22 -0
- package/dist/src/work-discovery/probe.js +88 -1
- package/dist/src/work-discovery/service.d.ts +110 -1
- package/dist/src/work-discovery/service.js +578 -8
- package/dist/src/work-discovery/source-repository.d.ts +13 -0
- package/dist/src/work-discovery/source-repository.js +72 -0
- package/dist/src/work-discovery/thread-repository.d.ts +34 -0
- package/dist/src/work-discovery/thread-repository.js +112 -0
- package/dist/src/work-discovery/thread-service.d.ts +8 -0
- package/dist/src/work-discovery/thread-service.js +104 -0
- package/dist/src/work-discovery/types.d.ts +145 -0
- package/package.json +1 -1
- package/skills/creative/hatch-pet/SKILL.md +2 -2
- package/dist/gateway/static/root/assets/agents-bnkKH9EI.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-l4ripRNG.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-Kny7tLiF.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-vr1tXAab.js +0 -8
- package/dist/gateway/static/root/assets/channels-settings-Tlhbi5eh.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-vooEF4lG.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-_XfvjXoY.js +0 -2
- package/dist/gateway/static/root/assets/desktop-pet-DKUlAgm7.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-settings-aWWzjdza.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-sprite-DqZ6fbIj.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-DzR-kM91.js +0 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-BLjHRtqe.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-BRxeujXh.js +0 -4
- package/dist/gateway/static/root/assets/goals-page-BaZ3aw4u.js +0 -2
- package/dist/gateway/static/root/assets/index-EXa_i6BT.js +0 -193
- package/dist/gateway/static/root/assets/index-gpwk8Nro.css +0 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-lUpd6lSo.js +0 -2
- package/dist/gateway/static/root/assets/logs-page-FPDTmcWe.js +0 -7
- package/dist/gateway/static/root/assets/models-hub-panel-BXrdePhv.js +0 -2
- package/dist/gateway/static/root/assets/notes-workbench-D-EAhXv1.js +0 -4
- package/dist/gateway/static/root/assets/project-detail-page-BjpB1wrp.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-BxyjqOfC.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BFv6Upke.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-BX4DH8J7.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-CJ9MbRP8.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-BH4xQKM9.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-B6kKkvOb.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DtjS-nUK.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-CQEoPv0u.js +0 -2
- package/dist/gateway/static/root/assets/sliding-segmented-uWmrxE3P.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-Dn2T3itn.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-wtWc3SfN.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-page-Ckxhj66e.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-_1QgGPNw.js +0 -1
- package/dist/gateway/static/root/assets/work-page-BPdf2iuv.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-DdKTx0Hs.js +0 -11
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
//#region src/gateway/chat-stream/thinking-delta-coalescer.ts
|
|
2
|
+
const DEFAULT_WINDOW_MS = 24;
|
|
3
|
+
const DEFAULT_MAX_CHARS = 256;
|
|
4
|
+
function isThinkingDelta(event) {
|
|
5
|
+
return event.type === "thinking_delta";
|
|
6
|
+
}
|
|
7
|
+
function belongsToSameThinkingStream(left, right) {
|
|
8
|
+
return left.runId === right.runId && left.sessionKey === right.sessionKey && left.payload.messageId === right.payload.messageId;
|
|
9
|
+
}
|
|
10
|
+
function mergeThinkingDelta(left, right) {
|
|
11
|
+
return {
|
|
12
|
+
...left,
|
|
13
|
+
payload: {
|
|
14
|
+
...left.payload,
|
|
15
|
+
delta: left.payload.delta + right.payload.delta
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async function nextBeforeDeadline(next, deadlineAt) {
|
|
20
|
+
let timer;
|
|
21
|
+
try {
|
|
22
|
+
return await Promise.race([next.then((result) => ({
|
|
23
|
+
kind: "next",
|
|
24
|
+
result
|
|
25
|
+
})), new Promise((resolve) => {
|
|
26
|
+
timer = setTimeout(() => resolve({ kind: "timeout" }), Math.max(0, deadlineAt - Date.now()));
|
|
27
|
+
})]);
|
|
28
|
+
} finally {
|
|
29
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function* coalesceThinkingDeltas(source, options = {}) {
|
|
33
|
+
const windowMs = options.windowMs ?? DEFAULT_WINDOW_MS;
|
|
34
|
+
const maxChars = options.maxChars ?? DEFAULT_MAX_CHARS;
|
|
35
|
+
if (!Number.isFinite(windowMs) || windowMs <= 0) throw new Error("thinking delta coalescer windowMs must be greater than zero");
|
|
36
|
+
if (!Number.isInteger(maxChars) || maxChars <= 0) throw new Error("thinking delta coalescer maxChars must be a positive integer");
|
|
37
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
38
|
+
let next = iterator.next();
|
|
39
|
+
let pending;
|
|
40
|
+
let deadlineAt = 0;
|
|
41
|
+
try {
|
|
42
|
+
while (true) {
|
|
43
|
+
if (!pending) {
|
|
44
|
+
const result = await next;
|
|
45
|
+
if (result.done) break;
|
|
46
|
+
next = iterator.next();
|
|
47
|
+
if (!isThinkingDelta(result.value)) {
|
|
48
|
+
yield result.value;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
pending = result.value;
|
|
52
|
+
deadlineAt = Date.now() + windowMs;
|
|
53
|
+
if (pending.payload.delta.length >= maxChars) {
|
|
54
|
+
yield pending;
|
|
55
|
+
pending = void 0;
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const outcome = await nextBeforeDeadline(next, deadlineAt);
|
|
60
|
+
if (outcome.kind === "timeout") {
|
|
61
|
+
yield pending;
|
|
62
|
+
pending = void 0;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const result = outcome.result;
|
|
66
|
+
if (result.done) {
|
|
67
|
+
yield pending;
|
|
68
|
+
pending = void 0;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
next = iterator.next();
|
|
72
|
+
const event = result.value;
|
|
73
|
+
if (!isThinkingDelta(event) || !belongsToSameThinkingStream(pending, event)) {
|
|
74
|
+
yield pending;
|
|
75
|
+
pending = void 0;
|
|
76
|
+
if (isThinkingDelta(event)) {
|
|
77
|
+
pending = event;
|
|
78
|
+
deadlineAt = Date.now() + windowMs;
|
|
79
|
+
if (pending.payload.delta.length >= maxChars) {
|
|
80
|
+
yield pending;
|
|
81
|
+
pending = void 0;
|
|
82
|
+
}
|
|
83
|
+
} else yield event;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
pending = mergeThinkingDelta(pending, event);
|
|
87
|
+
if (pending.payload.delta.length >= maxChars) {
|
|
88
|
+
yield pending;
|
|
89
|
+
pending = void 0;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (pending) {
|
|
94
|
+
yield pending;
|
|
95
|
+
pending = void 0;
|
|
96
|
+
}
|
|
97
|
+
throw error;
|
|
98
|
+
} finally {
|
|
99
|
+
await iterator.return?.();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
export { coalesceThinkingDeltas };
|
|
@@ -3,6 +3,10 @@ import "../../../storage/sqlite/index.js";
|
|
|
3
3
|
import { GoalService } from "../../../goals/goal-service.js";
|
|
4
4
|
import "../../../goals/index.js";
|
|
5
5
|
import { listGatewayAgents } from "../../agents-admin.js";
|
|
6
|
+
import { listFocusCalendarSignals } from "../../../proactive/calendar-signals.js";
|
|
7
|
+
import { FocusService } from "../../../proactive/focus-service.js";
|
|
8
|
+
import { listProactiveInsights } from "../../../proactive/insight-repository.js";
|
|
9
|
+
import "../../../proactive/index.js";
|
|
6
10
|
import { WorkItemService } from "../../../work-items/work-item-service.js";
|
|
7
11
|
import "../../../work-items/index.js";
|
|
8
12
|
import { getTunnelService } from "../../../tunnel/tunnel-service.js";
|
|
@@ -155,6 +159,7 @@ function buildHomeBriefing(input) {
|
|
|
155
159
|
function registerHomeRoutes(authenticated, deps) {
|
|
156
160
|
const { service } = deps;
|
|
157
161
|
const goals = new GoalService();
|
|
162
|
+
const focuses = new FocusService(service.automationServiceInstance);
|
|
158
163
|
authenticated.get("/api/home", async (c) => {
|
|
159
164
|
const notes = service.notesServiceInstance;
|
|
160
165
|
const sessions = service.sessions;
|
|
@@ -165,7 +170,8 @@ function registerHomeRoutes(authenticated, deps) {
|
|
|
165
170
|
const health = service.getHealth();
|
|
166
171
|
const workItems = new WorkItemService();
|
|
167
172
|
const nowMs = Date.now();
|
|
168
|
-
const
|
|
173
|
+
const focusViews = focuses.list();
|
|
174
|
+
const [recentlyOpened, inbox, pendingTasks, recentSessions, workflowRuns, automations, automationRuns, projects, allWorkItems, activeGoals, connectorApprovals, proactiveInsights, calendarSignals] = await Promise.all([
|
|
169
175
|
notes.listNotes({
|
|
170
176
|
sortBy: "lastOpenedAt",
|
|
171
177
|
sortOrder: "desc",
|
|
@@ -204,7 +210,12 @@ function registerHomeRoutes(authenticated, deps) {
|
|
|
204
210
|
principalId: "local-owner",
|
|
205
211
|
status: "pending",
|
|
206
212
|
limit: 100
|
|
207
|
-
}))
|
|
213
|
+
})),
|
|
214
|
+
Promise.resolve(listProactiveInsights({
|
|
215
|
+
status: ["unread"],
|
|
216
|
+
limit: 10
|
|
217
|
+
})),
|
|
218
|
+
Promise.resolve(listFocusCalendarSignals(focusViews, nowMs))
|
|
208
219
|
]);
|
|
209
220
|
const projectsById = new Map(projects.items.map((project) => [project.id, project]));
|
|
210
221
|
const activeWorkItems = allWorkItems.items.filter((item) => item.status !== "done" && item.status !== "cancelled").sort((left, right) => {
|
|
@@ -338,6 +349,8 @@ function registerHomeRoutes(authenticated, deps) {
|
|
|
338
349
|
},
|
|
339
350
|
briefing,
|
|
340
351
|
decisions,
|
|
352
|
+
proactiveInsights,
|
|
353
|
+
calendarSignals,
|
|
341
354
|
work: {
|
|
342
355
|
attentionCount: activeWorkItems.filter((item) => workItemAttentionRank(item, nowMs) < 3).length,
|
|
343
356
|
overdueCount: activeWorkItems.filter((item) => item.dueAt != null && item.dueAt < nowMs).length,
|
|
@@ -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,
|
|
@@ -23,7 +23,7 @@ export declare function createAgentSSEHandler(config: SSEHandlerConfig): (c: Con
|
|
|
23
23
|
* POST /api/agent/resume — Re-attach to an in-progress agent run via SSE.
|
|
24
24
|
*
|
|
25
25
|
* Request body: { runId, sessionKey }
|
|
26
|
-
* The relay replays
|
|
26
|
+
* The relay replays retained buffered events from the beginning and then live-tails
|
|
27
27
|
* until the run completes.
|
|
28
28
|
*
|
|
29
29
|
* SSE events are identical to those from POST /api/agent.
|
|
@@ -168,7 +168,7 @@ function createAgentSSEHandler(config) {
|
|
|
168
168
|
* POST /api/agent/resume — Re-attach to an in-progress agent run via SSE.
|
|
169
169
|
*
|
|
170
170
|
* Request body: { runId, sessionKey }
|
|
171
|
-
* The relay replays
|
|
171
|
+
* The relay replays retained buffered events from the beginning and then live-tails
|
|
172
172
|
* until the run completes.
|
|
173
173
|
*
|
|
174
174
|
* SSE events are identical to those from POST /api/agent.
|
|
@@ -7,6 +7,7 @@ import { shouldSkipWebchatInboundByAbortCutoff } from "../../session/abort-cutof
|
|
|
7
7
|
import "../chat-limits.js";
|
|
8
8
|
import { resolveWebchatSessionKey } from "../resolve-webchat-session-key.js";
|
|
9
9
|
import { ChatStreamMapper } from "../chat-stream/mapper.js";
|
|
10
|
+
import { coalesceThinkingDeltas } from "../chat-stream/thinking-delta-coalescer.js";
|
|
10
11
|
import crypto from "crypto";
|
|
11
12
|
//#region src/gateway/service/run-gateway-agent.ts
|
|
12
13
|
init_logger();
|
|
@@ -90,7 +91,10 @@ async function* runGatewayAgent(deps, message, channel, chatId, attachments, thi
|
|
|
90
91
|
signal: mergedSignal,
|
|
91
92
|
runId
|
|
92
93
|
});
|
|
93
|
-
|
|
94
|
+
const mappedEvents = (async function* () {
|
|
95
|
+
for await (const event of eventStream) yield* mapper.map(event);
|
|
96
|
+
})();
|
|
97
|
+
for await (const event of coalesceThinkingDeltas(mappedEvents)) yield* emitAndYield([event]);
|
|
94
98
|
const endStatus = mergedSignal.aborted ? "cancelled" : "success";
|
|
95
99
|
const endSummary = mergedSignal.aborted ? "Interrupted" : "Message processed successfully";
|
|
96
100
|
yield* emitAndYield(mapper.end(endStatus, endSummary));
|
|
@@ -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[];
|