@xopcai/xopc 0.0.176 → 0.0.177
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-DsFM1upC.js → activity-store-Wu2ikfbM.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DvdMLOga.js → agent-avatar-dicebear-B0_biISn.js} +1 -1
- package/dist/gateway/static/root/assets/agents-TpIpJk59.js +2 -0
- package/dist/gateway/static/root/assets/{api-BdPLCCBm.js → api-DljBt-N5.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-DMLDOHul.js → app-management-settings-panel-CKoZRi7Y.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-DbI9foFR.js → appearance-settings-DOdXhNDo.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-Cxbn5hy8.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-Dfdxuwvb.js → archive-plugin-D18gyP76.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-Dn3RlnYD.js → attachment-load-CZh9fc8u.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-Bc0Y7A9u.js → automation-suggestion-card-DKrVoiiK.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-DYv16QMU.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-0XtB-xC0.js → binary-plugins-DdSyGL6M.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-C4fXWbEE.js → block-editor-BOpNApiX.js} +6 -6
- package/dist/gateway/static/root/assets/{browser-settings-page-lC86-drr.js → browser-settings-page-DlcqaGu1.js} +4 -4
- package/dist/gateway/static/root/assets/{browser-workflow-inputs-CZmzNymN.js → browser-workflow-inputs-C0SnSj_R.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-workflows-page-CBYM9HqG.js → browser-workflows-page-CzlzH7HA.js} +2 -2
- package/dist/gateway/static/root/assets/capabilities-settings-panel-QPX4wsGZ.js +7 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-BiJxmmhW.js → capability-presets-api-RM6S7ES5.js} +1 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-DmCeusO8.js +1 -0
- package/dist/gateway/static/root/assets/{channel-recipient-api-BKTUZDp-.js → channel-recipient-api-B4sha-30.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-HcKoP1wm.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-D3xr2enH.js +1 -0
- package/dist/gateway/static/root/assets/{connector-logo-Pr4RIKjj.js → connector-logo-BPsKIyoM.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-DwbThFvd.js +2 -0
- package/dist/gateway/static/root/assets/desktop-pet-DjL1GWON.js +1 -0
- package/dist/gateway/static/root/assets/{desktop-pet-settings-CEQacUDF.js → desktop-pet-settings-u_abAXh7.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-DWnxdZtc.js → directory-picker-path-field-BoZGualI.js} +1 -1
- package/dist/gateway/static/root/assets/{dreaming-settings-D2e2w2Ho.js → dreaming-settings-s3gn3f9h.js} +3 -3
- package/dist/gateway/static/root/assets/{extension-debug-page-BLjTGbv3.js → extension-debug-page-DIbMngCF.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-D2_UC6hy.js → extension-page-D94f2bWQ.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BsH6o_PX.js → extension-settings-page-B7TI13ex.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-C-DkC6UJ.js → gateway-config-api-Do77rV8R.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-DEOGiimF.js → gateway-settings-Dt1PNJmb.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-BlSLh1TF.js → gateway-startup-retry-WaXTw3fi.js} +1 -1
- package/dist/gateway/static/root/assets/{goal-create-dialog-CSFcRE_N.js → goal-create-dialog-DEiOOLGU.js} +1 -1
- package/dist/gateway/static/root/assets/goal-detail-page-C1Qj4o1K.js +4 -0
- package/dist/gateway/static/root/assets/{goals-config-api-CcdS9qek.js → goals-config-api-BL0fAEns.js} +1 -1
- package/dist/gateway/static/root/assets/{goals-page-BAcErdch.js → goals-page-qJDcEDzc.js} +2 -2
- package/dist/gateway/static/root/assets/{goals-settings-BY7crl0z.js → goals-settings-DV3ZlrdI.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-DtTkS0gs.js → heartbeat-settings-C3kzg6ai.js} +1 -1
- package/dist/gateway/static/root/assets/{image-generation-api-WGgkbwDZ.js → image-generation-api-BgISY8i-.js} +1 -1
- package/dist/gateway/static/root/assets/{index-2UImfRDC.js → index-C1h0zkNo.js} +65 -65
- package/dist/gateway/static/root/assets/index-Da36E3WH.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-C5lpWQ3f.js → keyboard-shortcuts-settings-0KSmaPu0.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-DcD4mVId.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-b05wWvXG.js → local-apps-page-BMrU8u7w.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-sToMfHX_.js → locale-store-DVGhFXRz.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-cDE1jLJv.js → logs-page-CW5m9Cov.js} +3 -3
- package/dist/gateway/static/root/assets/{markdown-split-0gPVst5y.js → markdown-split-BVqbtN9o.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-Rxyq_KTm.js → media-plugins-Dr_wtkNJ.js} +1 -1
- package/dist/gateway/static/root/assets/messages-r8L6Ur3A.js +3 -0
- package/dist/gateway/static/root/assets/{notes-page-DRhNDl_Q.js → notes-page-BWZRXsq6.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-DdnwNz9x.js +4 -0
- package/dist/gateway/static/root/assets/{page-tabs-cOEOQqHh.js → page-tabs-DWWgy0bF.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-BopOLFvu.js → preview-open-alternatives-Bnfs93ZP.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-DWVPmmOq.js → product-open-page-BR7QFmT-.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-B8GeS4dl.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-DOeu1Hk_.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-De94rxnE.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-BfZmKe1t.js → save-bar-controls-BzS-rRTP.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-Cpi4jCRV.js → schedule-field-Cl2IIDAN.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-OXFkcZQe.js → schema-form-BjKjp5eZ.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-A7p8G0OJ.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BsGzjmqx.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-BwqkZCHJ.js → settings-form-section-BSDNBfHn.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-BbqH_S3Y.js → settings-loading-skeleton-BD4peoAv.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-BoxC-JSa.js → settings-page-S-P3U7at.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-Ddu2iGaW.js → settings-page-layout-BQTLh-86.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-z1Lwv0js.js → setup-status-panel-W3p6kNyA.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-CVazOFjy.js → share-preview-page-DQjMxyvw.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-Xo3N99PO.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-ByPEG5t1.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-C019Ml5q.js +2 -0
- package/dist/gateway/static/root/assets/{skills-page.utils-DgyB1tSo.js → skills-page.utils-CXnpudm6.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-BoLSQL65.js → system-settings-panel-BhFwdYJP.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-CBNcGOfj.js → theme-store-DQI-HPL-.js} +1 -1
- package/dist/gateway/static/root/assets/{time-picker-gochWrny.js → time-picker-C0DCya4U.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-DvRnr7YB.js → typed-models-lib-C-M3Fv2N.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-CA9BFVB7.js → use-channel-catalog-Bndq4y4I.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-fYBzpus-.js +1 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-v8tgylEZ.js → voice-api-key-field-p_g4UI1-.js} +1 -1
- package/dist/gateway/static/root/assets/work-discovery-page-DI0gCZRw.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-DnBRP2wI.js +1 -0
- package/dist/gateway/static/root/assets/work-page-Z-nUaS_n.js +3 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-CC3cJn5k.js → workflow-run-setup-panel-CHEXVvhH.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-BDfwnM-Y.js +11 -0
- package/dist/gateway/static/root/index.html +7 -7
- package/dist/package.js +1 -1
- package/dist/src/automations/service/automation-service.d.ts +3 -0
- package/dist/src/automations/service/automation-service.js +37 -7
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/update.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/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/routes/browser-install.js +1 -1
- package/dist/src/gateway/hono/routes/home.d.ts +18 -2
- package/dist/src/gateway/hono/routes/home.js +19 -0
- package/dist/src/gateway/hono/routes/index.js +2 -2
- package/dist/src/gateway/hono/routes/local-apps.js +1 -1
- package/dist/src/gateway/hono/routes/proactive.d.ts +4 -0
- package/dist/src/gateway/hono/routes/proactive.js +157 -0
- package/dist/src/gateway/hono/routes/projects.js +2 -2
- package/dist/src/gateway/hono/routes/update.js +2 -2
- package/dist/src/gateway/index.js +1 -1
- package/dist/src/gateway/run-loop.js +1 -1
- package/dist/src/gateway/service.d.ts +10 -1
- package/dist/src/gateway/service.js +36 -36
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/infra/gateway-processes.js +1 -1
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/infra/update-runner.js +2 -2
- package/dist/src/infra/update-startup.js +1 -1
- package/dist/src/mcp/channel-bridge.js +1 -1
- package/dist/src/mobile/notification-service.d.ts +1 -0
- package/dist/src/mobile/notification-service.js +38 -18
- package/dist/src/mobile/notification-types.d.ts +2 -2
- package/dist/src/proactive/events/envelope.d.ts +2 -0
- package/dist/src/proactive/events/envelope.js +54 -0
- package/dist/src/proactive/events/publisher.d.ts +4 -0
- package/dist/src/proactive/events/repository.d.ts +9 -0
- package/dist/src/proactive/events/repository.js +62 -0
- package/dist/src/proactive/events/types.d.ts +44 -0
- package/dist/src/proactive/events/types.js +15 -0
- package/dist/src/proactive/execution/agent-executor.d.ts +16 -0
- package/dist/src/proactive/execution/agent-executor.js +104 -0
- package/dist/src/proactive/execution/context.d.ts +31 -0
- package/dist/src/proactive/execution/context.js +72 -0
- package/dist/src/proactive/execution/insight.d.ts +4 -0
- package/dist/src/proactive/execution/insight.js +74 -0
- package/dist/src/proactive/execution/repository.d.ts +23 -0
- package/dist/src/proactive/execution/repository.js +122 -0
- package/dist/src/proactive/execution/types.d.ts +54 -0
- package/dist/src/proactive/execution/types.js +1 -0
- package/dist/src/proactive/execution/worker.d.ts +15 -0
- package/dist/src/proactive/execution/worker.js +96 -0
- package/dist/src/proactive/inbox/repository.d.ts +24 -0
- package/dist/src/proactive/inbox/repository.js +117 -0
- package/dist/src/proactive/inbox/service.d.ts +17 -0
- package/dist/src/proactive/inbox/service.js +22 -0
- package/dist/src/proactive/inbox/types.d.ts +38 -0
- package/dist/src/proactive/inbox/types.js +1 -0
- package/dist/src/proactive/inbox/worker.d.ts +12 -0
- package/dist/src/proactive/inbox/worker.js +56 -0
- package/dist/src/proactive/index.d.ts +17 -0
- package/dist/src/proactive/index.js +11 -0
- package/dist/src/proactive/routing/batch-repository.d.ts +27 -0
- package/dist/src/proactive/routing/batch-repository.js +56 -0
- package/dist/src/proactive/routing/matcher.d.ts +5 -0
- package/dist/src/proactive/routing/matcher.js +56 -0
- package/dist/src/proactive/routing/types.d.ts +44 -0
- package/dist/src/proactive/routing/types.js +1 -0
- package/dist/src/proactive/scenarios/prompt-composer.d.ts +14 -0
- package/dist/src/proactive/scenarios/prompt-composer.js +24 -0
- package/dist/src/proactive/scenarios/repository.d.ts +23 -0
- package/dist/src/proactive/scenarios/repository.js +162 -0
- package/dist/src/proactive/scenarios/service.d.ts +15 -0
- package/dist/src/proactive/scenarios/service.js +26 -0
- package/dist/src/proactive/scenarios/types.d.ts +36 -0
- package/dist/src/proactive/scenarios/types.js +1 -0
- package/dist/src/proactive/service.d.ts +18 -0
- package/dist/src/proactive/service.js +61 -0
- package/dist/src/projects/project-service.d.ts +3 -1
- package/dist/src/projects/project-service.js +62 -32
- package/dist/src/storage/sqlite/migrations/068_proactive_event_spine.sql +71 -0
- package/dist/src/storage/sqlite/migrations/069_proactive_scenario_control.sql +69 -0
- package/dist/src/storage/sqlite/migrations/070_proactive_execution.sql +55 -0
- package/dist/src/storage/sqlite/migrations/071_proactive_inbox.sql +45 -0
- package/dist/src/storage/sqlite/migrations/072_proactive_judgments.sql +14 -0
- package/dist/src/storage/sqlite/migrations/073_enable_default_proactive_scenarios.sql +13 -0
- package/dist/src/storage/sqlite/migrations/074_deduplicate_blocked_work_routing.sql +5 -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/thread-service.d.ts +1 -3
- package/dist/src/work-discovery/thread-service.js +13 -25
- package/dist/src/work-items/work-item-service.d.ts +3 -1
- package/dist/src/work-items/work-item-service.js +67 -36
- package/package.json +1 -1
- package/dist/gateway/static/root/assets/agents-BVjlpbeZ.js +0 -2
- package/dist/gateway/static/root/assets/api-Bop4IQRW.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-njqxLWYD.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-D-eweHZJ.js +0 -6
- package/dist/gateway/static/root/assets/capabilities-settings-panel-6tYNfHqJ.js +0 -7
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-Co-ymbd9.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-D46eVjaQ.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-BMRAO4zZ.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-Dw1eYbRi.js +0 -2
- package/dist/gateway/static/root/assets/copy-VueTIC2x.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-B8cZuQUt.js +0 -1
- package/dist/gateway/static/root/assets/focus-detail-page-BBXUtxRB.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-VjRArfAJ.js +0 -4
- package/dist/gateway/static/root/assets/index-CS9sVShT.css +0 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-Cl0fcPL4.js +0 -2
- package/dist/gateway/static/root/assets/messages-CarlxqXV.js +0 -3
- package/dist/gateway/static/root/assets/notes-workbench-BKNBk0Uf.js +0 -4
- package/dist/gateway/static/root/assets/project-detail-page-BhjEXNQW.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-B80e69PH.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CwdVob6H.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-BGt3Asj6.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BnTdM9Nn.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-C4lXuyQj.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DSuIDkeI.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-xgZczqVe.js +0 -2
- package/dist/gateway/static/root/assets/user-context-page-CEfIy4WZ.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-page-B-3MClaf.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-CEsFEdax.js +0 -1
- package/dist/gateway/static/root/assets/work-page-CT9AHnq6.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-C-1-_Zp1.js +0 -11
- package/dist/src/focuses/candidate-repository.d.ts +0 -15
- package/dist/src/focuses/candidate-repository.js +0 -91
- package/dist/src/focuses/index.d.ts +0 -7
- package/dist/src/focuses/index.js +0 -7
- package/dist/src/focuses/insight-repository.d.ts +0 -22
- package/dist/src/focuses/insight-repository.js +0 -77
- package/dist/src/focuses/prompts.d.ts +0 -2
- package/dist/src/focuses/prompts.js +0 -18
- package/dist/src/focuses/reconciler.d.ts +0 -5
- package/dist/src/focuses/reconciler.js +0 -26
- package/dist/src/focuses/repository.d.ts +0 -61
- package/dist/src/focuses/repository.js +0 -214
- package/dist/src/focuses/run-processor.d.ts +0 -18
- package/dist/src/focuses/run-processor.js +0 -162
- package/dist/src/focuses/service.d.ts +0 -42
- package/dist/src/focuses/service.js +0 -241
- package/dist/src/focuses/types.d.ts +0 -89
- package/dist/src/gateway/hono/routes/focuses.d.ts +0 -3
- package/dist/src/gateway/hono/routes/focuses.js +0 -319
- /package/dist/src/{focuses/types.js → proactive/events/publisher.js} +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { init_sync_provider_auth, resolveProviderApiKeySync } from "../../auth/sync-provider-auth.js";
|
|
2
|
+
import { getApiKeySync, getDefaultModelSync, init_providers, resolveModel } from "../../providers/index.js";
|
|
3
|
+
import { createExtensionAwareStreamFn } from "../../providers/extension-stream-bridge.js";
|
|
4
|
+
import { runAgentTurnWithTimeout } from "../../agent/orchestration/run-agent-turn-with-timeout.js";
|
|
5
|
+
import { isAssistantTurnAborted, isAssistantTurnFailed } from "../../agent/orchestration/llm-turn-retry.js";
|
|
6
|
+
import { Agent } from "@earendil-works/pi-agent-core";
|
|
7
|
+
import { Type } from "@sinclair/typebox";
|
|
8
|
+
//#region src/proactive/execution/agent-executor.ts
|
|
9
|
+
init_sync_provider_auth();
|
|
10
|
+
init_providers();
|
|
11
|
+
const MAX_TOOL_CALLS = 6;
|
|
12
|
+
const AGENT_TIMEOUT_MS = 9e4;
|
|
13
|
+
function assistantText(messages) {
|
|
14
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
15
|
+
const message = messages[index];
|
|
16
|
+
if (message.role !== "assistant") continue;
|
|
17
|
+
if (Array.isArray(message.content)) return message.content.flatMap((block) => block && typeof block === "object" && block.type === "text" ? [String(block.text ?? "")] : []).join("").trim();
|
|
18
|
+
}
|
|
19
|
+
return "";
|
|
20
|
+
}
|
|
21
|
+
function inspectionTool(context) {
|
|
22
|
+
return {
|
|
23
|
+
name: "inspect_authorized_context",
|
|
24
|
+
label: "Inspect authorized work context",
|
|
25
|
+
description: "Read one authorized evidence section. This tool cannot write or trigger any action.",
|
|
26
|
+
parameters: Type.Object({ section: Type.String({ description: "A top-level section name from the availableSections list." }) }),
|
|
27
|
+
async execute(_toolCallId, args) {
|
|
28
|
+
const values = args;
|
|
29
|
+
const section = typeof values.section === "string" ? values.section : "";
|
|
30
|
+
if (!Object.prototype.hasOwnProperty.call(context, section)) return {
|
|
31
|
+
content: [{
|
|
32
|
+
type: "text",
|
|
33
|
+
text: `Unknown section. Available: ${Object.keys(context).join(", ")}`
|
|
34
|
+
}],
|
|
35
|
+
details: { found: false }
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
content: [{
|
|
39
|
+
type: "text",
|
|
40
|
+
text: JSON.stringify({
|
|
41
|
+
section,
|
|
42
|
+
evidence: context[section]
|
|
43
|
+
}, null, 2).slice(0, 6e4)
|
|
44
|
+
}],
|
|
45
|
+
details: {
|
|
46
|
+
found: true,
|
|
47
|
+
section
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Runs an isolated, read-only agent over only the context authorized by the signal batch. */
|
|
54
|
+
var ReadonlyProactiveAgentExecutor = class {
|
|
55
|
+
constructor(config) {
|
|
56
|
+
this.config = config;
|
|
57
|
+
}
|
|
58
|
+
async execute(input) {
|
|
59
|
+
const model = resolveModel(getDefaultModelSync(this.config()));
|
|
60
|
+
let toolCalls = 0;
|
|
61
|
+
const agent = new Agent({
|
|
62
|
+
initialState: {
|
|
63
|
+
systemPrompt: `${input.systemPrompt}\n\nYou are an autonomous background analysis agent. You MUST inspect relevant authorized context with the provided read-only tool before reaching a conclusion. You have no mutation, messaging, shell, network, or filesystem tools.`,
|
|
64
|
+
model,
|
|
65
|
+
thinkingLevel: "low",
|
|
66
|
+
tools: [inspectionTool(input.authorizedContext)],
|
|
67
|
+
messages: []
|
|
68
|
+
},
|
|
69
|
+
streamFn: createExtensionAwareStreamFn(),
|
|
70
|
+
getApiKey: (provider) => resolveProviderApiKeySync(provider) ?? getApiKeySync(provider) ?? "",
|
|
71
|
+
beforeToolCall: async () => {
|
|
72
|
+
if (toolCalls >= MAX_TOOL_CALLS) return {
|
|
73
|
+
block: true,
|
|
74
|
+
reason: `Maximum inspection calls reached (${MAX_TOOL_CALLS}).`
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
afterToolCall: async () => {
|
|
78
|
+
toolCalls += 1;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
const abort = () => agent.abort();
|
|
82
|
+
input.signal?.addEventListener("abort", abort, { once: true });
|
|
83
|
+
try {
|
|
84
|
+
const sections = Object.keys(input.authorizedContext);
|
|
85
|
+
await runAgentTurnWithTimeout(agent, async () => {
|
|
86
|
+
await agent.prompt(`${input.userPrompt}\n\nAvailable authorized evidence sections: ${sections.join(", ") || "(none)"}.`);
|
|
87
|
+
await agent.waitForIdle();
|
|
88
|
+
}, AGENT_TIMEOUT_MS);
|
|
89
|
+
if (isAssistantTurnAborted(agent)) throw new Error("Proactive agent execution aborted");
|
|
90
|
+
if (isAssistantTurnFailed(agent)) throw new Error("Proactive agent execution failed");
|
|
91
|
+
const text = assistantText(agent.state.messages);
|
|
92
|
+
if (!text) throw new Error("Proactive agent returned empty output");
|
|
93
|
+
if (toolCalls === 0) throw new Error("Proactive agent did not inspect authorized evidence");
|
|
94
|
+
return {
|
|
95
|
+
text,
|
|
96
|
+
modelRef: `${model.provider}/${model.id}`
|
|
97
|
+
};
|
|
98
|
+
} finally {
|
|
99
|
+
input.signal?.removeEventListener("abort", abort);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
//#endregion
|
|
104
|
+
export { ReadonlyProactiveAgentExecutor };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ContextProvider } from './types.js';
|
|
2
|
+
export declare class EventBatchContextProvider implements ContextProvider {
|
|
3
|
+
readonly id = "event_batch";
|
|
4
|
+
supports(): boolean;
|
|
5
|
+
collect(input: {
|
|
6
|
+
eventIds: string[];
|
|
7
|
+
}): Promise<Record<string, unknown>>;
|
|
8
|
+
}
|
|
9
|
+
export declare class ProjectStateContextProvider implements ContextProvider {
|
|
10
|
+
readonly id = "project_state";
|
|
11
|
+
supports(scenarioKey: string): boolean;
|
|
12
|
+
collect(input: {
|
|
13
|
+
eventIds: string[];
|
|
14
|
+
}): Promise<Record<string, unknown>>;
|
|
15
|
+
}
|
|
16
|
+
export declare class AutomationStateContextProvider implements ContextProvider {
|
|
17
|
+
readonly id = "automation_state";
|
|
18
|
+
supports(scenarioKey: string): boolean;
|
|
19
|
+
collect(input: {
|
|
20
|
+
eventIds: string[];
|
|
21
|
+
}): Promise<Record<string, unknown>>;
|
|
22
|
+
}
|
|
23
|
+
export declare class ContextProviderRegistry {
|
|
24
|
+
private readonly providers;
|
|
25
|
+
constructor(providers?: ContextProvider[]);
|
|
26
|
+
collect(scenarioKey: string, input: {
|
|
27
|
+
batchId: string;
|
|
28
|
+
eventIds: string[];
|
|
29
|
+
subscriptionId: string;
|
|
30
|
+
}): Promise<Record<string, unknown>>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { getSqliteDatabase } from "../../storage/sqlite/transaction.js";
|
|
2
|
+
//#region src/proactive/execution/context.ts
|
|
3
|
+
var EventBatchContextProvider = class {
|
|
4
|
+
id = "event_batch";
|
|
5
|
+
supports() {
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
8
|
+
async collect(input) {
|
|
9
|
+
if (input.eventIds.length === 0) return { events: [] };
|
|
10
|
+
return { events: getSqliteDatabase().prepare(`SELECT event_id, type, subject_kind, subject_id, payload_json, occurred_at
|
|
11
|
+
FROM proactive_events WHERE event_id IN (${input.eventIds.map(() => "?").join(",")}) ORDER BY occurred_at`).all(...input.eventIds).map((row) => ({
|
|
12
|
+
evidenceId: row.event_id,
|
|
13
|
+
type: row.type,
|
|
14
|
+
subject: {
|
|
15
|
+
kind: row.subject_kind,
|
|
16
|
+
id: row.subject_id
|
|
17
|
+
},
|
|
18
|
+
payload: JSON.parse(row.payload_json),
|
|
19
|
+
occurredAt: row.occurred_at
|
|
20
|
+
})) };
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var ProjectStateContextProvider = class {
|
|
24
|
+
id = "project_state";
|
|
25
|
+
supports(scenarioKey) {
|
|
26
|
+
return scenarioKey === "project_delivery_risk" || scenarioKey === "blocked_work";
|
|
27
|
+
}
|
|
28
|
+
async collect(input) {
|
|
29
|
+
if (!input.eventIds.length) return {};
|
|
30
|
+
const project = getSqliteDatabase().prepare(`SELECT p.* FROM proactive_events e JOIN projects p ON p.project_id = e.project_id
|
|
31
|
+
WHERE e.event_id IN (${input.eventIds.map(() => "?").join(",")}) AND e.project_id IS NOT NULL ORDER BY e.occurred_at DESC LIMIT 1`).get(...input.eventIds);
|
|
32
|
+
if (!project) return {};
|
|
33
|
+
return {
|
|
34
|
+
project,
|
|
35
|
+
workItems: getSqliteDatabase().prepare(`SELECT id, title, status, priority, owner_agent_id, next_action,
|
|
36
|
+
blocked_reason, due_at, updated_at FROM work_items WHERE project_id = ? AND archived_at IS NULL
|
|
37
|
+
ORDER BY CASE status WHEN 'blocked' THEN 0 ELSE 1 END, updated_at DESC LIMIT 100`).all(String(project.project_id))
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var AutomationStateContextProvider = class {
|
|
42
|
+
id = "automation_state";
|
|
43
|
+
supports(scenarioKey) {
|
|
44
|
+
return scenarioKey === "automation_failure_impact";
|
|
45
|
+
}
|
|
46
|
+
async collect(input) {
|
|
47
|
+
if (!input.eventIds.length) return {};
|
|
48
|
+
const run = getSqliteDatabase().prepare(`SELECT r.* FROM proactive_events e JOIN automation_runs r ON r.run_id = e.subject_id
|
|
49
|
+
WHERE e.event_id IN (${input.eventIds.map(() => "?").join(",")}) ORDER BY e.occurred_at DESC LIMIT 1`).get(...input.eventIds);
|
|
50
|
+
if (!run) return {};
|
|
51
|
+
return {
|
|
52
|
+
automation: getSqliteDatabase().prepare(`SELECT automation_id, name, description, enabled, reliability_json, state_json, project_id
|
|
53
|
+
FROM automations WHERE automation_id = ?`).get(String(run.automation_id)),
|
|
54
|
+
failedRun: run
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
var ContextProviderRegistry = class {
|
|
59
|
+
constructor(providers = [
|
|
60
|
+
new EventBatchContextProvider(),
|
|
61
|
+
new ProjectStateContextProvider(),
|
|
62
|
+
new AutomationStateContextProvider()
|
|
63
|
+
]) {
|
|
64
|
+
this.providers = providers;
|
|
65
|
+
}
|
|
66
|
+
async collect(scenarioKey, input) {
|
|
67
|
+
const entries = await Promise.all(this.providers.filter((provider) => provider.supports(scenarioKey)).map(async (provider) => [provider.id, await provider.collect(input)]));
|
|
68
|
+
return Object.fromEntries(entries);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
72
|
+
export { AutomationStateContextProvider, ContextProviderRegistry, EventBatchContextProvider, ProjectStateContextProvider };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { InsightCandidate } from './types.js';
|
|
2
|
+
export declare function parseInsightCandidate(raw: string, allowedEvidenceIds: Set<string>): InsightCandidate;
|
|
3
|
+
export declare function scoreInsight(candidate: InsightCandidate): number;
|
|
4
|
+
export declare function isValuableInsight(candidate: InsightCandidate): boolean;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { stripCodeFences } from "../../agent/goals/judge.js";
|
|
2
|
+
//#region src/proactive/execution/insight.ts
|
|
3
|
+
const URGENCY = new Set([
|
|
4
|
+
"low",
|
|
5
|
+
"medium",
|
|
6
|
+
"high",
|
|
7
|
+
"critical"
|
|
8
|
+
]);
|
|
9
|
+
const boundedText = (value, field, max) => {
|
|
10
|
+
if (typeof value !== "string" || !value.trim()) throw new Error(`Insight ${field} is required`);
|
|
11
|
+
return value.trim().slice(0, max);
|
|
12
|
+
};
|
|
13
|
+
function parseDecision(value) {
|
|
14
|
+
if (value == null) return void 0;
|
|
15
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Insight decision is invalid");
|
|
16
|
+
const row = value;
|
|
17
|
+
if (!Array.isArray(row.options) || row.options.length < 2 || row.options.length > 3) throw new Error("Insight decision options are invalid");
|
|
18
|
+
const options = row.options.map((option) => {
|
|
19
|
+
if (!option || typeof option !== "object" || Array.isArray(option)) throw new Error("Insight decision option is invalid");
|
|
20
|
+
const item = option;
|
|
21
|
+
return {
|
|
22
|
+
id: boundedText(item.id, "decision option id", 80),
|
|
23
|
+
label: boundedText(item.label, "decision option label", 120),
|
|
24
|
+
consequence: boundedText(item.consequence, "decision option consequence", 400)
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
if (new Set(options.map((option) => option.id)).size !== options.length) throw new Error("Insight decision option ids must be unique");
|
|
28
|
+
return {
|
|
29
|
+
question: boundedText(row.question, "decision question", 400),
|
|
30
|
+
options
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function parseInsightCandidate(raw, allowedEvidenceIds) {
|
|
34
|
+
let value;
|
|
35
|
+
try {
|
|
36
|
+
value = JSON.parse(stripCodeFences(raw));
|
|
37
|
+
} catch {
|
|
38
|
+
throw new Error("Model output is not valid JSON");
|
|
39
|
+
}
|
|
40
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Insight must be a JSON object");
|
|
41
|
+
const row = value;
|
|
42
|
+
if (!URGENCY.has(String(row.urgency))) throw new Error("Insight urgency is invalid");
|
|
43
|
+
if (typeof row.confidence !== "number" || row.confidence < 0 || row.confidence > 1) throw new Error("Insight confidence is invalid");
|
|
44
|
+
if (!Array.isArray(row.evidenceIds) || row.evidenceIds.length === 0 || row.evidenceIds.length > 20) throw new Error("Insight evidenceIds are invalid");
|
|
45
|
+
const evidenceIds = [...new Set(row.evidenceIds.map(String))];
|
|
46
|
+
if (evidenceIds.some((id) => !allowedEvidenceIds.has(id))) throw new Error("Insight cites unknown evidence");
|
|
47
|
+
const decision = parseDecision(row.decision);
|
|
48
|
+
return {
|
|
49
|
+
title: boundedText(row.title, "title", 160),
|
|
50
|
+
summary: boundedText(row.summary, "summary", 1200),
|
|
51
|
+
whyNow: boundedText(row.whyNow, "whyNow", 800),
|
|
52
|
+
impact: boundedText(row.impact, "impact", 800),
|
|
53
|
+
recommendation: boundedText(row.recommendation, "recommendation", 800),
|
|
54
|
+
workDone: boundedText(row.workDone, "workDone", 800),
|
|
55
|
+
...decision ? { decision } : {},
|
|
56
|
+
urgency: String(row.urgency),
|
|
57
|
+
confidence: row.confidence,
|
|
58
|
+
evidenceIds
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function scoreInsight(candidate) {
|
|
62
|
+
const urgencyWeight = {
|
|
63
|
+
low: .15,
|
|
64
|
+
medium: .45,
|
|
65
|
+
high: .75,
|
|
66
|
+
critical: 1
|
|
67
|
+
}[candidate.urgency];
|
|
68
|
+
return Math.round((candidate.confidence * .7 + urgencyWeight * .3) * 1e3) / 1e3;
|
|
69
|
+
}
|
|
70
|
+
function isValuableInsight(candidate) {
|
|
71
|
+
return candidate.confidence >= .65 && candidate.urgency !== "low" && scoreInsight(candidate) >= .6;
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { isValuableInsight, parseInsightCandidate, scoreInsight };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ContextSnapshot, InsightCandidate, ProactiveInsight } from './types.js';
|
|
2
|
+
export interface ClaimedRun {
|
|
3
|
+
id: string;
|
|
4
|
+
batchId: string;
|
|
5
|
+
subscriptionId: string;
|
|
6
|
+
scenarioKey: string;
|
|
7
|
+
scenarioVersion: number;
|
|
8
|
+
promptRevisionId?: string;
|
|
9
|
+
attempt: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function claimNextRun(owner: string, now?: Date, leaseSeconds?: number): ClaimedRun | null;
|
|
12
|
+
export declare function eventIdsForBatch(batchId: string): string[];
|
|
13
|
+
export declare function saveSnapshot(batchId: string, content: Record<string, unknown>, evidenceIds: string[], now?: Date): ContextSnapshot;
|
|
14
|
+
export declare function attachSnapshot(runId: string, snapshotId: string): void;
|
|
15
|
+
export declare function finishRun(input: {
|
|
16
|
+
run: ClaimedRun;
|
|
17
|
+
candidate?: InsightCandidate;
|
|
18
|
+
valueScore?: number;
|
|
19
|
+
rawOutput: string;
|
|
20
|
+
modelRef?: string;
|
|
21
|
+
}, now?: Date): ProactiveInsight | null;
|
|
22
|
+
export declare function failRun(run: ClaimedRun, error: unknown, retryable: boolean, now?: Date): void;
|
|
23
|
+
export declare function listInsights(limit?: number): ProactiveInsight[];
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { getSqliteDatabase, runSqliteWriteTransaction } from "../../storage/sqlite/transaction.js";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
//#region src/proactive/execution/repository.ts
|
|
4
|
+
const str = (row, key) => String(row[key]);
|
|
5
|
+
function claimNextRun(owner, now = /* @__PURE__ */ new Date(), leaseSeconds = 120) {
|
|
6
|
+
return runSqliteWriteTransaction((db) => {
|
|
7
|
+
const nowIso = now.toISOString();
|
|
8
|
+
db.prepare(`UPDATE proactive_signal_batches SET status = 'ignored', updated_at = ? WHERE status = 'ready'
|
|
9
|
+
AND subscription_id IN (SELECT subscription_id FROM proactive_scenario_subscriptions WHERE enabled = 0)`).run(nowIso);
|
|
10
|
+
db.prepare(`UPDATE proactive_signal_batches SET status = 'failed_permanent', updated_at = ?
|
|
11
|
+
WHERE status = 'processing' AND batch_id IN (
|
|
12
|
+
SELECT batch_id FROM proactive_runs WHERE status = 'running' AND lease_expires_at <= ? AND attempt >= 3
|
|
13
|
+
)`).run(nowIso, nowIso);
|
|
14
|
+
db.prepare(`UPDATE proactive_runs SET status = 'failed', error_message = 'Lease expired after maximum attempts',
|
|
15
|
+
lease_owner = NULL, lease_expires_at = NULL, updated_at = ?
|
|
16
|
+
WHERE status = 'running' AND lease_expires_at <= ? AND attempt >= 3`).run(nowIso, nowIso);
|
|
17
|
+
const retry = db.prepare(`SELECT * FROM proactive_runs WHERE status = 'retryable' AND next_attempt_at <= ? ORDER BY next_attempt_at LIMIT 1`).get(nowIso);
|
|
18
|
+
if (retry) {
|
|
19
|
+
db.prepare(`UPDATE proactive_runs SET status = 'running', attempt = attempt + 1, lease_owner = ?, lease_expires_at = ?, next_attempt_at = NULL, updated_at = ? WHERE run_id = ?`).run(owner, new Date(now.getTime() + leaseSeconds * 1e3).toISOString(), nowIso, str(retry, "run_id"));
|
|
20
|
+
db.prepare("UPDATE proactive_signal_batches SET status = 'processing', updated_at = ? WHERE batch_id = ?").run(nowIso, str(retry, "batch_id"));
|
|
21
|
+
return runFromRow(db.prepare("SELECT * FROM proactive_runs WHERE run_id = ?").get(str(retry, "run_id")));
|
|
22
|
+
}
|
|
23
|
+
const expired = db.prepare(`SELECT * FROM proactive_runs WHERE status = 'running' AND lease_expires_at <= ? AND attempt < 3 ORDER BY updated_at LIMIT 1`).get(nowIso);
|
|
24
|
+
if (expired) {
|
|
25
|
+
db.prepare(`UPDATE proactive_runs SET attempt = attempt + 1, lease_owner = ?, lease_expires_at = ?, updated_at = ? WHERE run_id = ?`).run(owner, new Date(now.getTime() + leaseSeconds * 1e3).toISOString(), nowIso, str(expired, "run_id"));
|
|
26
|
+
return runFromRow(db.prepare("SELECT * FROM proactive_runs WHERE run_id = ?").get(str(expired, "run_id")));
|
|
27
|
+
}
|
|
28
|
+
const batch = db.prepare(`SELECT b.*, s.active_prompt_revision_id FROM proactive_signal_batches b
|
|
29
|
+
JOIN proactive_scenario_subscriptions s ON s.subscription_id = b.subscription_id
|
|
30
|
+
WHERE b.status = 'ready' ORDER BY b.ready_at LIMIT 1`).get();
|
|
31
|
+
if (!batch) return null;
|
|
32
|
+
const id = randomUUID();
|
|
33
|
+
db.prepare(`INSERT INTO proactive_runs (run_id, batch_id, subscription_id, scenario_key, scenario_version,
|
|
34
|
+
prompt_revision_id, status, attempt, lease_owner, lease_expires_at, started_at, updated_at)
|
|
35
|
+
VALUES (?, ?, ?, ?, ?, ?, 'running', 1, ?, ?, ?, ?)`).run(id, str(batch, "batch_id"), str(batch, "subscription_id"), str(batch, "scenario_key"), Number(batch.scenario_version), batch.active_prompt_revision_id ? String(batch.active_prompt_revision_id) : null, owner, new Date(now.getTime() + leaseSeconds * 1e3).toISOString(), nowIso, nowIso);
|
|
36
|
+
db.prepare("UPDATE proactive_signal_batches SET status = 'processing', updated_at = ? WHERE batch_id = ?").run(nowIso, str(batch, "batch_id"));
|
|
37
|
+
return runFromRow(db.prepare("SELECT * FROM proactive_runs WHERE run_id = ?").get(id));
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function runFromRow(row) {
|
|
41
|
+
return {
|
|
42
|
+
id: str(row, "run_id"),
|
|
43
|
+
batchId: str(row, "batch_id"),
|
|
44
|
+
subscriptionId: str(row, "subscription_id"),
|
|
45
|
+
scenarioKey: str(row, "scenario_key"),
|
|
46
|
+
scenarioVersion: Number(row.scenario_version),
|
|
47
|
+
attempt: Number(row.attempt),
|
|
48
|
+
...row.prompt_revision_id ? { promptRevisionId: str(row, "prompt_revision_id") } : {}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function eventIdsForBatch(batchId) {
|
|
52
|
+
return getSqliteDatabase().prepare("SELECT event_id FROM proactive_batch_events WHERE batch_id = ? ORDER BY added_at DESC LIMIT 100").all(batchId).map((row) => row.event_id);
|
|
53
|
+
}
|
|
54
|
+
function saveSnapshot(batchId, content, evidenceIds, now = /* @__PURE__ */ new Date()) {
|
|
55
|
+
const snapshot = {
|
|
56
|
+
id: randomUUID(),
|
|
57
|
+
batchId,
|
|
58
|
+
content,
|
|
59
|
+
evidenceIds,
|
|
60
|
+
createdAt: now.toISOString()
|
|
61
|
+
};
|
|
62
|
+
runSqliteWriteTransaction((db) => db.prepare(`INSERT INTO proactive_context_snapshots
|
|
63
|
+
(snapshot_id, batch_id, content_json, evidence_ids_json, created_at) VALUES (?, ?, ?, ?, ?)`).run(snapshot.id, batchId, JSON.stringify(content), JSON.stringify(evidenceIds), snapshot.createdAt));
|
|
64
|
+
return snapshot;
|
|
65
|
+
}
|
|
66
|
+
function attachSnapshot(runId, snapshotId) {
|
|
67
|
+
runSqliteWriteTransaction((db) => db.prepare("UPDATE proactive_runs SET context_snapshot_id = ?, updated_at = ? WHERE run_id = ?").run(snapshotId, (/* @__PURE__ */ new Date()).toISOString(), runId));
|
|
68
|
+
}
|
|
69
|
+
function finishRun(input, now = /* @__PURE__ */ new Date()) {
|
|
70
|
+
return runSqliteWriteTransaction((db) => {
|
|
71
|
+
const nowIso = now.toISOString();
|
|
72
|
+
const valuable = Boolean(input.candidate);
|
|
73
|
+
db.prepare(`UPDATE proactive_runs SET status = ?, raw_output = ?, model_ref = ?, lease_owner = NULL,
|
|
74
|
+
lease_expires_at = NULL, completed_at = ?, updated_at = ? WHERE run_id = ?`).run(valuable ? "completed" : "discarded", input.rawOutput.slice(0, 2e4), input.modelRef ?? null, nowIso, nowIso, input.run.id);
|
|
75
|
+
db.prepare(`UPDATE proactive_signal_batches SET status = ?, updated_at = ? WHERE batch_id = ?`).run(valuable ? "processed" : "ignored", nowIso, input.run.batchId);
|
|
76
|
+
if (!input.candidate) return null;
|
|
77
|
+
const insight = {
|
|
78
|
+
...input.candidate,
|
|
79
|
+
id: randomUUID(),
|
|
80
|
+
runId: input.run.id,
|
|
81
|
+
subscriptionId: input.run.subscriptionId,
|
|
82
|
+
scenarioKey: input.run.scenarioKey,
|
|
83
|
+
valueScore: input.valueScore ?? 0,
|
|
84
|
+
createdAt: nowIso
|
|
85
|
+
};
|
|
86
|
+
db.prepare(`INSERT INTO proactive_insights (insight_id, run_id, subscription_id, scenario_key, title, summary,
|
|
87
|
+
why_now, impact, recommendation, work_done, decision_json, urgency, confidence, value_score, evidence_ids_json, created_at)
|
|
88
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(insight.id, insight.runId, insight.subscriptionId, insight.scenarioKey, insight.title, insight.summary, insight.whyNow, insight.impact, insight.recommendation, insight.workDone, insight.decision ? JSON.stringify(insight.decision) : null, insight.urgency, insight.confidence, insight.valueScore, JSON.stringify(insight.evidenceIds), insight.createdAt);
|
|
89
|
+
return insight;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function failRun(run, error, retryable, now = /* @__PURE__ */ new Date()) {
|
|
93
|
+
runSqliteWriteTransaction((db) => {
|
|
94
|
+
const finalRetryable = retryable && run.attempt < 3;
|
|
95
|
+
const nowIso = now.toISOString();
|
|
96
|
+
const nextAttemptAt = finalRetryable ? new Date(now.getTime() + 15e3 * run.attempt).toISOString() : null;
|
|
97
|
+
db.prepare(`UPDATE proactive_runs SET status = ?, error_message = ?, lease_owner = NULL, lease_expires_at = NULL, next_attempt_at = ?, updated_at = ? WHERE run_id = ?`).run(finalRetryable ? "retryable" : "failed", String(error instanceof Error ? error.message : error).slice(0, 2e3), nextAttemptAt, nowIso, run.id);
|
|
98
|
+
db.prepare(`UPDATE proactive_signal_batches SET status = ?, updated_at = ? WHERE batch_id = ?`).run(finalRetryable ? "failed_retryable" : "failed_permanent", nowIso, run.batchId);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function listInsights(limit = 50) {
|
|
102
|
+
return getSqliteDatabase().prepare("SELECT * FROM proactive_insights ORDER BY created_at DESC LIMIT ?").all(Math.min(200, Math.max(1, limit))).map((row) => ({
|
|
103
|
+
id: str(row, "insight_id"),
|
|
104
|
+
runId: str(row, "run_id"),
|
|
105
|
+
subscriptionId: str(row, "subscription_id"),
|
|
106
|
+
scenarioKey: str(row, "scenario_key"),
|
|
107
|
+
title: str(row, "title"),
|
|
108
|
+
summary: str(row, "summary"),
|
|
109
|
+
whyNow: str(row, "why_now"),
|
|
110
|
+
impact: str(row, "impact"),
|
|
111
|
+
recommendation: str(row, "recommendation"),
|
|
112
|
+
workDone: str(row, "work_done"),
|
|
113
|
+
...row.decision_json ? { decision: JSON.parse(str(row, "decision_json")) } : {},
|
|
114
|
+
urgency: str(row, "urgency"),
|
|
115
|
+
confidence: Number(row.confidence),
|
|
116
|
+
valueScore: Number(row.value_score),
|
|
117
|
+
evidenceIds: JSON.parse(str(row, "evidence_ids_json")),
|
|
118
|
+
createdAt: str(row, "created_at")
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
export { attachSnapshot, claimNextRun, eventIdsForBatch, failRun, finishRun, listInsights, saveSnapshot };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export interface ContextSnapshot {
|
|
2
|
+
id: string;
|
|
3
|
+
batchId: string;
|
|
4
|
+
content: Record<string, unknown>;
|
|
5
|
+
evidenceIds: string[];
|
|
6
|
+
createdAt: string;
|
|
7
|
+
}
|
|
8
|
+
export interface InsightCandidate {
|
|
9
|
+
title: string;
|
|
10
|
+
summary: string;
|
|
11
|
+
whyNow: string;
|
|
12
|
+
impact: string;
|
|
13
|
+
recommendation: string;
|
|
14
|
+
workDone: string;
|
|
15
|
+
decision?: {
|
|
16
|
+
question: string;
|
|
17
|
+
options: Array<{
|
|
18
|
+
id: string;
|
|
19
|
+
label: string;
|
|
20
|
+
consequence: string;
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
23
|
+
urgency: 'low' | 'medium' | 'high' | 'critical';
|
|
24
|
+
confidence: number;
|
|
25
|
+
evidenceIds: string[];
|
|
26
|
+
}
|
|
27
|
+
export interface ProactiveInsight extends InsightCandidate {
|
|
28
|
+
id: string;
|
|
29
|
+
runId: string;
|
|
30
|
+
subscriptionId: string;
|
|
31
|
+
scenarioKey: string;
|
|
32
|
+
valueScore: number;
|
|
33
|
+
createdAt: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ProactiveAgentExecutor {
|
|
36
|
+
execute(input: {
|
|
37
|
+
systemPrompt: string;
|
|
38
|
+
userPrompt: string;
|
|
39
|
+
authorizedContext: Record<string, unknown>;
|
|
40
|
+
signal?: AbortSignal;
|
|
41
|
+
}): Promise<{
|
|
42
|
+
text: string;
|
|
43
|
+
modelRef?: string;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
export interface ContextProvider {
|
|
47
|
+
id: string;
|
|
48
|
+
supports(scenarioKey: string): boolean;
|
|
49
|
+
collect(input: {
|
|
50
|
+
batchId: string;
|
|
51
|
+
eventIds: string[];
|
|
52
|
+
subscriptionId: string;
|
|
53
|
+
}): Promise<Record<string, unknown>>;
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ContextProviderRegistry } from './context.js';
|
|
2
|
+
import type { ProactiveAgentExecutor } from './types.js';
|
|
3
|
+
export declare class ProactiveWorker {
|
|
4
|
+
private readonly executor;
|
|
5
|
+
private readonly contexts;
|
|
6
|
+
private readonly intervalMs;
|
|
7
|
+
private readonly owner;
|
|
8
|
+
private timer?;
|
|
9
|
+
private running;
|
|
10
|
+
private stoppedWaiters;
|
|
11
|
+
constructor(executor: ProactiveAgentExecutor, contexts?: ContextProviderRegistry, intervalMs?: number);
|
|
12
|
+
start(): void;
|
|
13
|
+
stop(): Promise<void>;
|
|
14
|
+
tick(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { createLogger } from "../../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../../utils/logger.js";
|
|
3
|
+
import { markReadyBatches } from "../routing/batch-repository.js";
|
|
4
|
+
import { getPromptRevision, getScenario } from "../scenarios/repository.js";
|
|
5
|
+
import { composeScenarioPrompt } from "../scenarios/prompt-composer.js";
|
|
6
|
+
import { ContextProviderRegistry } from "./context.js";
|
|
7
|
+
import { attachSnapshot, claimNextRun, eventIdsForBatch, failRun, finishRun, saveSnapshot } from "./repository.js";
|
|
8
|
+
import { isValuableInsight, parseInsightCandidate, scoreInsight } from "./insight.js";
|
|
9
|
+
import { randomUUID } from "node:crypto";
|
|
10
|
+
//#region src/proactive/execution/worker.ts
|
|
11
|
+
init_logger();
|
|
12
|
+
const log = createLogger("ProactiveWorker");
|
|
13
|
+
var ProactiveWorker = class {
|
|
14
|
+
owner = randomUUID();
|
|
15
|
+
timer;
|
|
16
|
+
running = false;
|
|
17
|
+
stoppedWaiters = [];
|
|
18
|
+
constructor(executor, contexts = new ContextProviderRegistry(), intervalMs = 5e3) {
|
|
19
|
+
this.executor = executor;
|
|
20
|
+
this.contexts = contexts;
|
|
21
|
+
this.intervalMs = intervalMs;
|
|
22
|
+
}
|
|
23
|
+
start() {
|
|
24
|
+
if (this.timer) return;
|
|
25
|
+
this.timer = setInterval(() => void this.tick(), this.intervalMs);
|
|
26
|
+
this.timer.unref();
|
|
27
|
+
this.tick();
|
|
28
|
+
}
|
|
29
|
+
async stop() {
|
|
30
|
+
if (this.timer) clearInterval(this.timer);
|
|
31
|
+
this.timer = void 0;
|
|
32
|
+
if (!this.running) return;
|
|
33
|
+
await new Promise((resolve) => this.stoppedWaiters.push(resolve));
|
|
34
|
+
}
|
|
35
|
+
async tick() {
|
|
36
|
+
if (this.running) return;
|
|
37
|
+
this.running = true;
|
|
38
|
+
try {
|
|
39
|
+
markReadyBatches();
|
|
40
|
+
const run = claimNextRun(this.owner);
|
|
41
|
+
if (!run) return;
|
|
42
|
+
try {
|
|
43
|
+
const scenario = getScenario(run.scenarioKey);
|
|
44
|
+
if (!scenario || scenario.version !== run.scenarioVersion) throw new Error("Pinned scenario version is unavailable");
|
|
45
|
+
const revision = run.promptRevisionId ? getPromptRevision(run.promptRevisionId) ?? void 0 : void 0;
|
|
46
|
+
const eventIds = eventIdsForBatch(run.batchId);
|
|
47
|
+
const context = await this.contexts.collect(run.scenarioKey, {
|
|
48
|
+
batchId: run.batchId,
|
|
49
|
+
eventIds,
|
|
50
|
+
subscriptionId: run.subscriptionId
|
|
51
|
+
});
|
|
52
|
+
const snapshot = saveSnapshot(run.batchId, context, eventIds);
|
|
53
|
+
attachSnapshot(run.id, snapshot.id);
|
|
54
|
+
const prompt = composeScenarioPrompt({
|
|
55
|
+
scenario,
|
|
56
|
+
...revision ? { revision } : {},
|
|
57
|
+
runtimeContext: "Use the read-only inspection tool to examine the authorized evidence."
|
|
58
|
+
});
|
|
59
|
+
const output = await this.executor.execute({
|
|
60
|
+
systemPrompt: prompt.platformSafety,
|
|
61
|
+
userPrompt: prompt.text,
|
|
62
|
+
authorizedContext: context
|
|
63
|
+
});
|
|
64
|
+
const candidate = parseInsightCandidate(output.text, new Set(eventIds));
|
|
65
|
+
const valuable = isValuableInsight(candidate);
|
|
66
|
+
const insight = finishRun({
|
|
67
|
+
run,
|
|
68
|
+
...valuable ? {
|
|
69
|
+
candidate,
|
|
70
|
+
valueScore: scoreInsight(candidate)
|
|
71
|
+
} : {},
|
|
72
|
+
rawOutput: output.text,
|
|
73
|
+
modelRef: output.modelRef
|
|
74
|
+
});
|
|
75
|
+
log.info({
|
|
76
|
+
runId: run.id,
|
|
77
|
+
scenarioKey: run.scenarioKey,
|
|
78
|
+
valuable,
|
|
79
|
+
insightId: insight?.id
|
|
80
|
+
}, valuable ? "Proactive insight created" : "Proactive output discarded by value gate");
|
|
81
|
+
} catch (error) {
|
|
82
|
+
failRun(run, error, !(error instanceof Error && /Pinned scenario/.test(error.message)));
|
|
83
|
+
log.warn({
|
|
84
|
+
err: error,
|
|
85
|
+
runId: run.id,
|
|
86
|
+
scenarioKey: run.scenarioKey
|
|
87
|
+
}, "Proactive run failed");
|
|
88
|
+
}
|
|
89
|
+
} finally {
|
|
90
|
+
this.running = false;
|
|
91
|
+
for (const resolve of this.stoppedWaiters.splice(0)) resolve();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
//#endregion
|
|
96
|
+
export { ProactiveWorker };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { InboxItem, InboxStatus } from './types.js';
|
|
2
|
+
export declare function projectInsightsToInbox(now?: Date): number;
|
|
3
|
+
export declare function wakeSnoozedItems(now?: Date): number;
|
|
4
|
+
export declare function listInbox(input?: {
|
|
5
|
+
status?: InboxStatus;
|
|
6
|
+
limit?: number;
|
|
7
|
+
}): InboxItem[];
|
|
8
|
+
export declare function getInboxItem(id: string): InboxItem | null;
|
|
9
|
+
export declare function transitionInboxItem(id: string, input: {
|
|
10
|
+
status: InboxStatus;
|
|
11
|
+
snoozedUntil?: string;
|
|
12
|
+
resolution?: string;
|
|
13
|
+
}, now?: Date): InboxItem;
|
|
14
|
+
export declare function recordDecision(id: string, choice: string, note?: string, now?: Date): InboxItem;
|
|
15
|
+
export declare function recordFeedback(id: string, rating: 'useful' | 'not_useful', note?: string, now?: Date): void;
|
|
16
|
+
export declare function getInboxSubscriptionId(id: string): string;
|
|
17
|
+
export interface OutboxClaim {
|
|
18
|
+
id: string;
|
|
19
|
+
item: InboxItem;
|
|
20
|
+
attempt: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function claimDelivery(now?: Date): OutboxClaim | null;
|
|
23
|
+
export declare function finishDelivery(id: string, error?: unknown, attempt?: number, now?: Date): void;
|
|
24
|
+
export declare function recoverExpiredDeliveries(now?: Date): number;
|