@xopcai/xopc 0.0.283 → 0.0.285
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-iPtG94qr.js → activity-store-B0BTIk9j.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DQ74xqLi.js → agent-avatar-dicebear-Dc-0QZTQ.js} +1 -1
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-CcmqK1Sc.js +1 -0
- package/dist/gateway/static/root/assets/agents-B_7PVt7S.js +1 -0
- package/dist/gateway/static/root/assets/api-COG_Tzpv.js +1 -0
- package/dist/gateway/static/root/assets/{api-KMJzMqvZ.js → api-Coly9ab6.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-B42TpqIb.js → app-management-settings-panel-TWBO-msg.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-D9Lfe7Sk.js → appearance-settings-CpTZ2KuT.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-6qxxKFsK.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-BdmQwj4x.js → archive-plugin-z_u6alob.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-B4S0_nji.js → attachment-load-C2nS04TC.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-B3TmHNjQ.js → automations-page-aIBsw9zR.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-BRQlU4qk.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-B3cXF_DP.js → binary-plugins-CGmXoZnl.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-05zyBXBe.js → block-editor-CtvzCZhC.js} +3 -3
- package/dist/gateway/static/root/assets/{browser-automation-inputs-BTIufVbM.js → browser-automation-inputs-BOTU1isH.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-DRBA9nwe.js → browser-automations-page-BwU5V8J0.js} +2 -2
- package/dist/gateway/static/root/assets/browser-settings-page-DDjbDeHQ.js +8 -0
- package/dist/gateway/static/root/assets/capabilities-settings-panel-DM1Bcf1x.js +8 -0
- package/dist/gateway/static/root/assets/channels-settings-Bq36CUxL.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-DMYQNs-J.js → channels-status-swr-Dfi0Desh.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-Dx5KQyo5.js → chat-terminal-dock-B0MvcO6j.js} +2 -2
- package/dist/gateway/static/root/assets/connectors-page-L8JehrxF.js +2 -0
- package/dist/gateway/static/root/assets/{date-picker-CkDROZoe.js → date-picker-DXOvbHCr.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-Bl8HcvzL.js → dependency-picker-BEnC-eS0.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-BcknAjbW.js → desktop-pet-DtPNvznL.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-BuCpXQPm.js → desktop-pet-settings-CYyjcSn6.js} +1 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard-BPMGvAKE.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-qRpb2FFa.js → extension-debug-page-H_aPMGXW.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-C7gs9wu4.js → extension-page-C1JnpeMt.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-CyJP1CMZ.js → extension-settings-page-C75kCOqe.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-Cpmyrr6j.js → gateway-config-swr-G2lQt7Ak.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-BCF732TU.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-DoiPeb65.js → gateway-startup-retry-GP31PUrG.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-CV6Dolcn.js → heartbeat-settings-C7fjiowt.js} +1 -1
- package/dist/gateway/static/root/assets/home-page-DZXeXdUL.js +1 -0
- package/dist/gateway/static/root/assets/{image-generation-api-CqjHWl2T.js → image-generation-api-CKFhfHSw.js} +1 -1
- package/dist/gateway/static/root/assets/imports-page-GROumhj8.js +1 -0
- package/dist/gateway/static/root/assets/{index-CHgOG1aR.js → index-ChIrvv3t.js} +47 -47
- package/dist/gateway/static/root/assets/index-R1VNlHjj.css +2 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-B3XqA41D.js → keyboard-shortcuts-settings-zcI822m_.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-CAEKGTAg.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-BcIVYp0J.js → local-apps-page-BtgDJ92Q.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-1urrBBCQ.js → locale-store-DlOv7896.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-DHZ4BEkG.js +2 -0
- package/dist/gateway/static/root/assets/{management-settings-DNFCdgzR.js → management-settings-DasZxCyD.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-view-BZmcFIWa.js → markdown-view-6oYdBahT.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-BDxNhoBC.js → media-plugins-CxthbBvk.js} +1 -1
- package/dist/gateway/static/root/assets/messages-Ls1Aaiqd.js +3 -0
- package/dist/gateway/static/root/assets/note-detail-panel-BcWq3ddw.js +5 -0
- package/dist/gateway/static/root/assets/notes-page-DfZ8QWyv.js +8 -0
- package/dist/gateway/static/root/assets/{page-tabs-agH3a2PI.js → page-tabs-BG39KidE.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-XEZ7zDOP.js → preview-open-alternatives-Bevpbq9f.js} +1 -1
- package/dist/gateway/static/root/assets/proactive-page-D2U_xQaS.js +1 -0
- package/dist/gateway/static/root/assets/{product-open-page-lXAbj85Z.js → product-open-page-Bi1seZpM.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-CzlGTW4F.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-mQsM-TsP.js +1 -0
- package/dist/gateway/static/root/assets/{registry-api-CxQstD00.js → registry-api-D0ddwBFj.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-Cb56JcO1.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DErKZlVJ.js → runtime-tools-settings-panel-Wkru8_My.js} +2 -2
- package/dist/gateway/static/root/assets/{schema-form-DhCmJQVJ.js → schema-form-CSMRzj9Y.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-Hj9uFcIO.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-CV4-_EVA.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-Dg6chotg.js → settings-form-section-wcAdCkK8.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-LyuN5N3M.js → settings-page-BcQGAnCs.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-DzxmsaQG.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-CZvmf4Ob.js → share-preview-page-DQocUl-1.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-xlJh1ccG.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-wSXJRSxX.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-AmqtBJo7.js +2 -0
- package/dist/gateway/static/root/assets/{skills-page.utils-Eu0WVHTS.js → skills-page.utils-DtlfCgJp.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-DQKkM7B2.js → system-settings-panel-Ct698Nf-.js} +1 -1
- package/dist/gateway/static/root/assets/task-detail-page-sQpwq5-c.js +1 -0
- package/dist/gateway/static/root/assets/{theme-store-Dc5s20V3.js → theme-store-ng8KJsAT.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-TKQpPVd6.js → tunnel-api-Boajol0M.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-CkVlrFk1.js → use-autosave-BSyxRHt2.js} +1 -1
- package/dist/gateway/static/root/assets/user-model-page-DIEHnAER.js +2 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-B12fFN-L.js → voice-api-key-field-SE3OQI1s.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-Mk78BrpP.js → voice-config-api-VNuCmdFe.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-CT9Zy36i.js → work-discovery-overlay-Dn1vy2oq.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-BwefQliE.js → workflow-run-setup-panel-BJEfGY-k.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-DgRNIyr7.js +6 -0
- package/dist/gateway/static/root/index.html +10 -10
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/proactive.js +13 -4
- package/dist/src/agent/coding/repository-instructions.js +1 -1
- package/dist/src/agent/coding/workspace-revision.js +1 -1
- package/dist/src/agent/commands/command-isolation.js +1 -1
- package/dist/src/agent/tools/factory.js +7 -3
- package/dist/src/agent/tools/review-workspace.js +1 -1
- package/dist/src/browser/providers/browser-ext-install.js +10 -2
- package/dist/src/cli/command-loaders.js +1 -0
- package/dist/src/cli/command-manifest.js +4 -0
- 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/gateway/run-foreground.js +3 -0
- package/dist/src/cli/commands/import.d.ts +2 -0
- package/dist/src/cli/commands/import.js +78 -0
- package/dist/src/discussions/action-tasks.js +1 -1
- package/dist/src/discussions/edits.js +1 -1
- package/dist/src/discussions/index.js +1 -1
- package/dist/src/discussions/organizer.js +1 -1
- package/dist/src/discussions/recording.js +1 -1
- package/dist/src/discussions/sealer.js +1 -1
- package/dist/src/discussions/service.js +2 -2
- package/dist/src/endpoint-tools/invocation-service.d.ts +2 -0
- package/dist/src/endpoint-tools/invocation-service.js +48 -19
- package/dist/src/gateway/heartbeat/check-store.d.ts +18 -0
- package/dist/src/gateway/heartbeat/check-store.js +73 -0
- package/dist/src/gateway/heartbeat/service.d.ts +12 -3
- package/dist/src/gateway/heartbeat/service.js +61 -44
- package/dist/src/gateway/hono/routes/discussions.js +2 -2
- package/dist/src/gateway/hono/routes/imports.d.ts +3 -0
- package/dist/src/gateway/hono/routes/imports.js +56 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/hono/routes/proactive-control.js +2 -1
- package/dist/src/gateway/service.d.ts +7 -0
- package/dist/src/gateway/service.js +26 -9
- package/dist/src/imports/compatibility.d.ts +7 -0
- package/dist/src/imports/compatibility.js +174 -0
- package/dist/src/imports/contextImport.d.ts +3 -0
- package/dist/src/imports/contextImport.js +56 -0
- package/dist/src/imports/files.d.ts +13 -0
- package/dist/src/imports/files.js +77 -0
- package/dist/src/imports/importService.d.ts +50 -0
- package/dist/src/imports/importService.js +414 -0
- package/dist/src/imports/inventory.d.ts +11 -0
- package/dist/src/imports/inventory.js +202 -0
- package/dist/src/imports/planner.d.ts +11 -0
- package/dist/src/imports/planner.js +98 -0
- package/dist/src/imports/productImport.d.ts +10 -0
- package/dist/src/imports/productImport.js +241 -0
- package/dist/src/imports/runtime.d.ts +5 -0
- package/dist/src/imports/runtime.js +61 -0
- package/dist/src/imports/scanner.d.ts +14 -0
- package/dist/src/imports/scanner.js +159 -0
- package/dist/src/imports/secrets.d.ts +2 -0
- package/dist/src/imports/secrets.js +9 -0
- package/dist/src/imports/sources.d.ts +37 -0
- package/dist/src/imports/sources.js +75 -0
- package/dist/src/imports/types.d.ts +144 -0
- package/dist/src/imports/types.js +11 -0
- package/dist/src/infra/background-task.d.ts +2 -0
- package/dist/src/infra/background-task.js +25 -0
- package/dist/src/infra/process-diagnostics.d.ts +5 -0
- package/dist/src/infra/process-diagnostics.js +97 -0
- package/dist/src/infra/unhandled-rejections.js +6 -3
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/knowledge/ingestion-service.js +1 -0
- package/dist/src/knowledge-memory/repository.js +4 -2
- package/dist/src/media-shared/http/ssrf-guard.js +1 -1
- package/dist/src/notifications/proactive-policy.js +7 -5
- package/dist/src/proactive/execution/authorization.d.ts +2 -0
- package/dist/src/proactive/execution/authorization.js +10 -1
- package/dist/src/proactive/execution/context.js +1 -0
- package/dist/src/proactive/execution/repository.d.ts +2 -0
- package/dist/src/proactive/execution/repository.js +19 -2
- package/dist/src/proactive/execution/worker.js +14 -4
- package/dist/src/proactive/experience.d.ts +4 -1
- package/dist/src/proactive/experience.js +3 -0
- package/dist/src/proactive/follow-ups.d.ts +5 -0
- package/dist/src/proactive/follow-ups.js +18 -7
- package/dist/src/proactive/inbox/cards.js +2 -1
- package/dist/src/proactive/inbox/delivery.js +6 -7
- package/dist/src/proactive/inbox/digest.d.ts +1 -1
- package/dist/src/proactive/inbox/digest.js +23 -13
- package/dist/src/proactive/inbox/notification-policy.d.ts +1 -1
- package/dist/src/proactive/inbox/notification-policy.js +9 -6
- package/dist/src/proactive/inbox/repository.js +3 -2
- package/dist/src/proactive/inbox/service.d.ts +6 -0
- package/dist/src/proactive/inbox/service.js +18 -0
- package/dist/src/proactive/inbox/types.d.ts +1 -0
- package/dist/src/proactive/policy/presence.d.ts +1 -1
- package/dist/src/proactive/policy/presence.js +7 -5
- package/dist/src/proactive/policy/service.d.ts +8 -5
- package/dist/src/proactive/policy/service.js +12 -12
- package/dist/src/proactive/scenarios/control.d.ts +3 -3
- package/dist/src/proactive/scenarios/control.js +12 -2
- package/dist/src/proactive/source-freshness.d.ts +8 -0
- package/dist/src/proactive/source-freshness.js +27 -0
- package/dist/src/proactive/status.d.ts +2 -0
- package/dist/src/proactive/status.js +21 -0
- package/dist/src/proactive/temporal/worker.js +2 -1
- package/dist/src/realtime/runtime.js +31 -5
- package/dist/src/realtime/writer.js +16 -9
- package/dist/src/storage/sqlite/import-repository.d.ts +18 -0
- package/dist/src/storage/sqlite/import-repository.js +35 -0
- package/dist/src/storage/sqlite/import-selection-repository.d.ts +15 -0
- package/dist/src/storage/sqlite/import-selection-repository.js +28 -0
- package/dist/src/storage/sqlite/knowledge-repository.d.ts +1 -0
- package/dist/src/storage/sqlite/knowledge-repository.js +2 -2
- package/dist/src/storage/sqlite/migrations/174_capability_imports.sql +11 -0
- package/dist/src/storage/sqlite/migrations/175_import_selections.sql +8 -0
- package/dist/src/storage/sqlite/migrations/176_proactive_quality.sql +5 -0
- package/dist/src/storage/sqlite/migrations/177_proactive_check_controls.sql +25 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/work-discovery/candidate-discovery.js +1 -1
- package/dist/src/work-discovery/probe.js +1 -1
- package/package.json +4 -3
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-SElFkQd6.js +0 -1
- package/dist/gateway/static/root/assets/agents-DYazUaZ6.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-BzrodN8C.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace--A4I0jMu.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-DDTXUNWK.js +0 -8
- package/dist/gateway/static/root/assets/capabilities-settings-panel-Ll5UeXq5.js +0 -8
- package/dist/gateway/static/root/assets/channels-settings-BzL15bkU.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-RPw1AOMb.js +0 -2
- package/dist/gateway/static/root/assets/device-pairing-wizard-D0_tCX49.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-DOeeTdWE.js +0 -1
- package/dist/gateway/static/root/assets/home-page-Djw1H_Go.js +0 -1
- package/dist/gateway/static/root/assets/index-B_Sg5BKz.css +0 -2
- package/dist/gateway/static/root/assets/local-app-workbench-page-BPqgyYJq.js +0 -2
- package/dist/gateway/static/root/assets/logs-page-BHax3XlS.js +0 -2
- package/dist/gateway/static/root/assets/messages-DYFcjtmG.js +0 -3
- package/dist/gateway/static/root/assets/note-detail-panel-Cs46LFXO.js +0 -5
- package/dist/gateway/static/root/assets/notes-page-BUkcn_0t.js +0 -8
- package/dist/gateway/static/root/assets/proactive-page-Bn484J9v.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-LcH21ixD.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-DelBiwyB.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-3BjFYXCf.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-CVFirgVd.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DyfBpqkY.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-CrXhTzHr.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-DyM_D8hM.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-Bys58-15.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-sWNvFo6D.js +0 -2
- package/dist/gateway/static/root/assets/task-detail-page-B18NcZQJ.js +0 -1
- package/dist/gateway/static/root/assets/user-model-page-ul-zr58b.js +0 -2
- package/dist/gateway/static/root/assets/workflows-page-BY_AWs7S.js +0 -6
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
3
|
import { shouldSilence, stripHeartbeatToken } from "../../heartbeat/tokens.js";
|
|
4
|
+
import { proactiveChecksAllowed, proactivePreferences } from "../../proactive/policy/service.js";
|
|
5
|
+
import { beginHeartbeatCheck, completeHeartbeatCheck, deliverHeartbeatChecks, recentHeartbeatChecks, recoverHeartbeatChecks } from "./check-store.js";
|
|
4
6
|
import { appendCronEventLines } from "../../heartbeat/event-prompt.js";
|
|
5
7
|
import { isWithinActiveHours } from "../../heartbeat/active-hours.js";
|
|
6
8
|
import { isHeartbeatContentEmpty } from "../../heartbeat/content-check.js";
|
|
@@ -31,22 +33,41 @@ function heartbeatRunnerConfigFromConfig(cfg) {
|
|
|
31
33
|
var HeartbeatService = class {
|
|
32
34
|
intervalId = null;
|
|
33
35
|
wake;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
nextCheckAt = null;
|
|
37
|
+
deliveryTimer = null;
|
|
38
|
+
delivering = false;
|
|
39
|
+
recovered = false;
|
|
36
40
|
runnerConfig = null;
|
|
37
41
|
constructor(deps) {
|
|
38
42
|
this.deps = deps;
|
|
39
|
-
this.wake = createHeartbeatWake((reasons) =>
|
|
43
|
+
this.wake = createHeartbeatWake(async (reasons) => {
|
|
44
|
+
try {
|
|
45
|
+
await this.runHeartbeatOnce(reasons);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
log.error({ err }, "Heartbeat check failed unexpectedly");
|
|
48
|
+
}
|
|
49
|
+
});
|
|
40
50
|
}
|
|
41
51
|
start(config) {
|
|
52
|
+
if (this.runnerConfig) this.stop();
|
|
42
53
|
if (!config.enabled) {
|
|
43
54
|
log.info("Heartbeat disabled");
|
|
44
55
|
this.runnerConfig = null;
|
|
45
56
|
return;
|
|
46
57
|
}
|
|
47
58
|
this.runnerConfig = config;
|
|
59
|
+
if (!this.recovered) {
|
|
60
|
+
recoverHeartbeatChecks(this.deps.getWorkspace());
|
|
61
|
+
this.recovered = true;
|
|
62
|
+
}
|
|
63
|
+
this.deliveryTimer = setInterval(() => {
|
|
64
|
+
this.drainDeliveries().catch((err) => log.warn({ err }, "Heartbeat delivery check failed"));
|
|
65
|
+
}, 1e4);
|
|
66
|
+
this.deliveryTimer.unref?.();
|
|
67
|
+
this.nextCheckAt = new Date(Date.now() + config.intervalMs).toISOString();
|
|
48
68
|
log.info({ intervalMs: config.intervalMs }, "Heartbeat timer started (interval wake)");
|
|
49
69
|
this.intervalId = setInterval(() => {
|
|
70
|
+
this.nextCheckAt = new Date(Date.now() + config.intervalMs).toISOString();
|
|
50
71
|
this.wake.request({ reason: "interval" });
|
|
51
72
|
}, config.intervalMs);
|
|
52
73
|
this.intervalId.unref?.();
|
|
@@ -56,6 +77,9 @@ var HeartbeatService = class {
|
|
|
56
77
|
this.wake.request({ reason: opts?.reason ?? "manual" });
|
|
57
78
|
}
|
|
58
79
|
stop() {
|
|
80
|
+
if (this.deliveryTimer) clearInterval(this.deliveryTimer);
|
|
81
|
+
this.deliveryTimer = null;
|
|
82
|
+
this.nextCheckAt = null;
|
|
59
83
|
if (this.intervalId) {
|
|
60
84
|
clearInterval(this.intervalId);
|
|
61
85
|
this.intervalId = null;
|
|
@@ -73,6 +97,24 @@ var HeartbeatService = class {
|
|
|
73
97
|
isRunning() {
|
|
74
98
|
return this.intervalId !== null;
|
|
75
99
|
}
|
|
100
|
+
status() {
|
|
101
|
+
const preferences = proactivePreferences(this.deps.getWorkspace());
|
|
102
|
+
return {
|
|
103
|
+
enabled: Boolean(this.runnerConfig?.enabled),
|
|
104
|
+
checksAllowed: proactiveChecksAllowed(preferences),
|
|
105
|
+
nextCheckAt: proactiveChecksAllowed(preferences) ? this.nextCheckAt : null,
|
|
106
|
+
recent: recentHeartbeatChecks(this.deps.getWorkspace())
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
async drainDeliveries() {
|
|
110
|
+
if (this.delivering || !this.runnerConfig?.enabled) return;
|
|
111
|
+
this.delivering = true;
|
|
112
|
+
try {
|
|
113
|
+
await deliverHeartbeatChecks(this.deps.getWorkspace(), this.deps.messageBus, this.runnerConfig.target?.trim(), this.runnerConfig.targetChatId?.trim());
|
|
114
|
+
} finally {
|
|
115
|
+
this.delivering = false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
76
118
|
async runHeartbeatOnce(reasons) {
|
|
77
119
|
const reasonSummary = [...new Set(reasons)].join(", ") || "unknown";
|
|
78
120
|
const cfg = this.runnerConfig;
|
|
@@ -80,36 +122,34 @@ var HeartbeatService = class {
|
|
|
80
122
|
log.debug({ reasons: reasonSummary }, "Heartbeat: skip (disabled)");
|
|
81
123
|
return;
|
|
82
124
|
}
|
|
125
|
+
if (!proactiveChecksAllowed(proactivePreferences(this.deps.getWorkspace()))) return;
|
|
83
126
|
if (cfg.activeHours && !isWithinActiveHours(cfg.activeHours)) {
|
|
84
127
|
log.debug({ reasons: reasonSummary }, "Heartbeat: skip (outside active hours)");
|
|
85
128
|
return;
|
|
86
129
|
}
|
|
130
|
+
const checkId = beginHeartbeatCheck(this.deps.getWorkspace());
|
|
87
131
|
const heartbeatPath = resolveHeartbeatMdPath(this.deps.getConfig());
|
|
88
132
|
if (!heartbeatPath) {
|
|
89
|
-
|
|
133
|
+
completeHeartbeatCheck(checkId, "blocked", "No workspace checklist path");
|
|
90
134
|
return;
|
|
91
135
|
}
|
|
92
136
|
let heartbeatContent;
|
|
93
137
|
try {
|
|
94
138
|
const raw = await readFile(heartbeatPath, "utf-8");
|
|
95
139
|
if (isHeartbeatContentEmpty(raw)) {
|
|
96
|
-
|
|
97
|
-
path: heartbeatPath,
|
|
98
|
-
reasons: reasonSummary
|
|
99
|
-
}, "Heartbeat: skip (HEARTBEAT.md empty)");
|
|
140
|
+
completeHeartbeatCheck(checkId, "empty_checklist");
|
|
100
141
|
return;
|
|
101
142
|
}
|
|
102
143
|
heartbeatContent = raw.trim();
|
|
103
144
|
} catch {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
reasons: reasonSummary
|
|
107
|
-
}, "Heartbeat: HEARTBEAT.md missing; continuing");
|
|
145
|
+
completeHeartbeatCheck(checkId, "blocked", "Checklist could not be read");
|
|
146
|
+
return;
|
|
108
147
|
}
|
|
109
148
|
const sessionKey = cfg.isolatedSession ? `heartbeat:isolated:${Date.now()}` : "heartbeat:main";
|
|
110
149
|
let basePrompt = (cfg.prompt?.trim() || DEFAULT_PROMPT).trim();
|
|
111
150
|
if (heartbeatContent) basePrompt = `${basePrompt}\n\n---\nHEARTBEAT.md:\n${heartbeatContent}\n---`;
|
|
112
151
|
basePrompt = appendCronEventLines(basePrompt, reasons);
|
|
152
|
+
basePrompt += "\nReturn notifications in your final response. Do not send messages, media or voice directly; delivery follows the user notification policy.";
|
|
113
153
|
const prompt = `${basePrompt}\n\nCurrent time: ${(/* @__PURE__ */ new Date()).toISOString()}`;
|
|
114
154
|
const ackMax = cfg.ackMaxChars ?? 300;
|
|
115
155
|
log.debug({
|
|
@@ -143,14 +183,21 @@ var HeartbeatService = class {
|
|
|
143
183
|
source: "heartbeat"
|
|
144
184
|
});
|
|
145
185
|
} catch (error) {
|
|
186
|
+
completeHeartbeatCheck(checkId, "failed", "Agent check failed; see gateway logs");
|
|
146
187
|
log.error({ err: error }, "Heartbeat: agent call failed");
|
|
147
188
|
return;
|
|
148
189
|
}
|
|
190
|
+
if (cfg !== this.runnerConfig || !proactiveChecksAllowed(proactivePreferences(this.deps.getWorkspace()))) {
|
|
191
|
+
completeHeartbeatCheck(checkId, "cancelled", "Checks paused or configuration changed");
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
149
194
|
if (!reply?.trim()) {
|
|
195
|
+
completeHeartbeatCheck(checkId, "no_change");
|
|
150
196
|
log.debug({ reasons: reasonSummary }, "Heartbeat: skip (empty model reply)");
|
|
151
197
|
return;
|
|
152
198
|
}
|
|
153
199
|
if (shouldSilence(reply, ackMax)) {
|
|
200
|
+
completeHeartbeatCheck(checkId, "no_change");
|
|
154
201
|
log.info({
|
|
155
202
|
ackMax,
|
|
156
203
|
replyChars: reply.length,
|
|
@@ -159,38 +206,8 @@ var HeartbeatService = class {
|
|
|
159
206
|
return;
|
|
160
207
|
}
|
|
161
208
|
const { stripped } = stripHeartbeatToken(reply);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
log.info({
|
|
165
|
-
finalTextChars: finalText.length,
|
|
166
|
-
reasons: reasonSummary
|
|
167
|
-
}, "Heartbeat: not sent — duplicate within 24h");
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
const target = cfg.target?.trim();
|
|
171
|
-
const targetChatId = cfg.targetChatId?.trim();
|
|
172
|
-
if (Boolean(target && targetChatId)) {
|
|
173
|
-
await this.deps.messageBus.publishOutbound({
|
|
174
|
-
channel: target,
|
|
175
|
-
chat_id: targetChatId,
|
|
176
|
-
content: finalText,
|
|
177
|
-
type: "message"
|
|
178
|
-
});
|
|
179
|
-
log.info({
|
|
180
|
-
reasons: reasonSummary,
|
|
181
|
-
channel: target,
|
|
182
|
-
chatId: targetChatId,
|
|
183
|
-
contentChars: finalText.length
|
|
184
|
-
}, "Heartbeat: sent — outbound queued");
|
|
185
|
-
} else log.info({
|
|
186
|
-
reasons: reasonSummary,
|
|
187
|
-
finalTextChars: finalText.length
|
|
188
|
-
}, "Heartbeat: not sent — no delivery target (set gateway.heartbeat.target + targetChatId)");
|
|
189
|
-
this.lastHeartbeatText = finalText;
|
|
190
|
-
this.lastHeartbeatAt = Date.now();
|
|
191
|
-
}
|
|
192
|
-
isDuplicate(text) {
|
|
193
|
-
return text.trim() === this.lastHeartbeatText.trim() && Date.now() - this.lastHeartbeatAt < 1440 * 60 * 1e3;
|
|
209
|
+
completeHeartbeatCheck(checkId, "prepared", void 0, stripped || reply.trim(), cfg.target?.trim(), cfg.targetChatId?.trim());
|
|
210
|
+
await this.drainDeliveries();
|
|
194
211
|
}
|
|
195
212
|
};
|
|
196
213
|
//#endregion
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { DISCUSSION_STATUSES } from "../../../../packages/gateway-contract/src/discussions.js";
|
|
2
2
|
import { hasGatewayScope } from "../../security/gateway-scopes.js";
|
|
3
3
|
import { DiscussionServiceError } from "../../../discussions/errors.js";
|
|
4
|
-
import { convertMeetingAction, meetingActionTasks } from "../../../discussions/action-tasks.js";
|
|
5
4
|
import { MeetingEditSchema } from "../../../discussions/edits.js";
|
|
6
|
-
import { exportDiscussion } from "../../../discussions/export.js";
|
|
7
5
|
import "../../../discussions/index.js";
|
|
8
6
|
import { getGatewayPrincipal } from "../../security/gateway-principal.js";
|
|
7
|
+
import { convertMeetingAction, meetingActionTasks } from "../../../discussions/action-tasks.js";
|
|
8
|
+
import { exportDiscussion } from "../../../discussions/export.js";
|
|
9
9
|
import { createReadStream } from "node:fs";
|
|
10
10
|
import { stat } from "node:fs/promises";
|
|
11
11
|
import { Readable } from "node:stream";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { getGatewayPrincipal } from "../../security/gateway-principal.js";
|
|
2
|
+
import { ImportError } from "../../../imports/types.js";
|
|
3
|
+
import { isImportSource } from "../../../imports/sources.js";
|
|
4
|
+
import { ImportSelectionRepository } from "../../../storage/sqlite/import-selection-repository.js";
|
|
5
|
+
import { getLiveInventory, previewInventoryItem, publicInventory, scanProduct } from "../../../imports/inventory.js";
|
|
6
|
+
import { importSelection, listImportSources } from "../../../imports/productImport.js";
|
|
7
|
+
import { createRuntimeImportService } from "../../../imports/runtime.js";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
//#region src/gateway/hono/routes/imports.ts
|
|
10
|
+
const selectionSchema = z.object({
|
|
11
|
+
inventoryId: z.string().uuid(),
|
|
12
|
+
candidateIds: z.array(z.string().uuid()).min(1).max(5e3),
|
|
13
|
+
requestId: z.string().uuid(),
|
|
14
|
+
retryOf: z.string().uuid().optional()
|
|
15
|
+
}).strict();
|
|
16
|
+
function registerImportRoutes(app, deps) {
|
|
17
|
+
const service = () => createRuntimeImportService("gateway-owner", () => deps.service.agentService.refreshSkillsAfterDiskChange(), (root) => deps.service.agentService.getWorkspaceTrust(root).trusted);
|
|
18
|
+
const respond = async (c, run) => {
|
|
19
|
+
try {
|
|
20
|
+
if (getGatewayPrincipal(c).kind !== "owner") throw new ImportError("owner_required", "Only the Gateway owner can import local capabilities", 403);
|
|
21
|
+
return c.json({
|
|
22
|
+
ok: true,
|
|
23
|
+
data: await run()
|
|
24
|
+
});
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (error instanceof z.ZodError) return c.json({
|
|
27
|
+
ok: false,
|
|
28
|
+
code: "invalid_input",
|
|
29
|
+
error: "Invalid import request"
|
|
30
|
+
}, 400);
|
|
31
|
+
if (error instanceof ImportError) return c.json({
|
|
32
|
+
ok: false,
|
|
33
|
+
code: error.code,
|
|
34
|
+
error: error.message
|
|
35
|
+
}, error.status);
|
|
36
|
+
return c.json({
|
|
37
|
+
ok: false,
|
|
38
|
+
code: "import_failed",
|
|
39
|
+
error: "Import could not finish. Existing content is preserved."
|
|
40
|
+
}, 400);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
app.get("/api/imports/sources", (c) => respond(c, () => ({ sources: listImportSources("gateway-owner") })));
|
|
44
|
+
app.post("/api/imports/sources/:source/scan", deps.strictRateLimitMiddleware, (c) => respond(c, async () => {
|
|
45
|
+
const source = c.req.param("source");
|
|
46
|
+
if (!isImportSource(source)) throw new ImportError("unsupported_source", "Unsupported source");
|
|
47
|
+
z.object({}).strict().parse(await c.req.json());
|
|
48
|
+
return scanProduct(service(), source);
|
|
49
|
+
}));
|
|
50
|
+
app.get("/api/imports/inventories/:id", (c) => respond(c, () => publicInventory(getLiveInventory("gateway-owner", c.req.param("id")))));
|
|
51
|
+
app.get("/api/imports/inventories/:id/items/:itemId/preview", (c) => respond(c, () => previewInventoryItem(service(), "gateway-owner", c.req.param("id"), c.req.param("itemId"))));
|
|
52
|
+
app.post("/api/imports/runs", deps.strictRateLimitMiddleware, (c) => respond(c, async () => importSelection(service(), selectionSchema.parse(await c.req.json()))));
|
|
53
|
+
app.get("/api/imports/runs/:id", (c) => respond(c, () => new ImportSelectionRepository("gateway-owner").get("run", c.req.param("id"))));
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { registerImportRoutes };
|
|
@@ -3,6 +3,14 @@ function startsWithAny(path, prefixes) {
|
|
|
3
3
|
return prefixes.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));
|
|
4
4
|
}
|
|
5
5
|
const AUTHENTICATED_LAZY_ROUTE_BUNDLES = [
|
|
6
|
+
{
|
|
7
|
+
id: "imports",
|
|
8
|
+
match: (path) => startsWithAny(path, ["/api/imports"]),
|
|
9
|
+
load: async () => {
|
|
10
|
+
const { registerImportRoutes } = await import("./imports.js");
|
|
11
|
+
return { register: registerImportRoutes };
|
|
12
|
+
}
|
|
13
|
+
},
|
|
6
14
|
{
|
|
7
15
|
id: "project-understanding",
|
|
8
16
|
match: (path) => /^\/api\/projects\/[^/]+\/understanding$/.test(path),
|
|
@@ -33,7 +33,8 @@ function registerProactiveControlRoutes(app, deps) {
|
|
|
33
33
|
}));
|
|
34
34
|
app.get("/api/proactive/overview", (c) => c.json({
|
|
35
35
|
ok: true,
|
|
36
|
-
...delegationOverview(workspace())
|
|
36
|
+
...delegationOverview(workspace()),
|
|
37
|
+
heartbeat: deps.service.heartbeatStatus()
|
|
37
38
|
}));
|
|
38
39
|
app.post("/api/proactive/delegations", deps.strictRateLimitMiddleware, async (c) => c.json({
|
|
39
40
|
ok: true,
|
|
@@ -124,6 +124,7 @@ export declare class GatewayService {
|
|
|
124
124
|
private ensureHeartbeatService;
|
|
125
125
|
private createTaskRunDispatcher;
|
|
126
126
|
private createNotificationService;
|
|
127
|
+
private readonly taskRunDispatch;
|
|
127
128
|
dispatchTaskRuns(): void;
|
|
128
129
|
ensureTaskConversation(taskId: string, options?: {
|
|
129
130
|
runId?: string;
|
|
@@ -255,6 +256,12 @@ export declare class GatewayService {
|
|
|
255
256
|
manageable: boolean;
|
|
256
257
|
order: number;
|
|
257
258
|
}>;
|
|
259
|
+
heartbeatStatus(): {
|
|
260
|
+
enabled: boolean;
|
|
261
|
+
checksAllowed: boolean;
|
|
262
|
+
nextCheckAt: string;
|
|
263
|
+
recent: import("./heartbeat/check-store.js").HeartbeatCheck[];
|
|
264
|
+
};
|
|
258
265
|
/**
|
|
259
266
|
* Request an immediate heartbeat run (coalesced like interval/cron wakes).
|
|
260
267
|
*/
|
|
@@ -63,23 +63,24 @@ import { ConnectionRecoveryService } from "../connectors/connection-recovery-ser
|
|
|
63
63
|
import { startConnectorLearningCoordinator } from "../connectors/learning-coordinator.js";
|
|
64
64
|
import { ConnectedSourceChangePublisher } from "../connectors/source-change-publisher.js";
|
|
65
65
|
import { startConnectorSupervisor } from "../connectors/supervisor.js";
|
|
66
|
-
import { DiscussionService } from "../discussions/service.js";
|
|
67
|
-
import { DiscussionOrganizer } from "../discussions/organizer.js";
|
|
68
|
-
import { DiscussionLiveWorker } from "../discussions/live-worker.js";
|
|
69
|
-
import { DiscussionOrganizerWorker } from "../discussions/organizer-worker.js";
|
|
70
|
-
import { DiscussionSealer } from "../discussions/sealer.js";
|
|
71
|
-
import "../discussions/index.js";
|
|
72
66
|
import { EndpointToolRuntime } from "../endpoint-tools/runtime.js";
|
|
73
67
|
import "../endpoint-tools/index.js";
|
|
74
68
|
import { assertGatewayAuthConfigured, extractToken, resolveGatewayAuth, validateToken } from "./auth.js";
|
|
75
69
|
import { prepareAutomationAgentSession } from "./automation-agent-session.js";
|
|
76
70
|
import { stopTailscaleExposure } from "./tailscale-lifecycle.js";
|
|
71
|
+
import { createBackgroundTask } from "../infra/background-task.js";
|
|
77
72
|
import { buildTaskAgentContext } from "../agent/source-context/task-context.js";
|
|
78
73
|
import { deliverProactiveCard } from "../proactive/inbox/delivery.js";
|
|
79
74
|
import { WorkDiscoveryService } from "../work-discovery/service.js";
|
|
80
75
|
import { buildVoiceMemoryContext } from "../voice/realtime/memory-context.js";
|
|
81
76
|
import { buildVoicePersonaContext } from "../voice/realtime/persona-context.js";
|
|
82
77
|
import { voiceMemoryBudget, voicePersonaBudget } from "../voice/realtime/conversation-context.js";
|
|
78
|
+
import { DiscussionService } from "../discussions/service.js";
|
|
79
|
+
import { DiscussionOrganizer } from "../discussions/organizer.js";
|
|
80
|
+
import { DiscussionLiveWorker } from "../discussions/live-worker.js";
|
|
81
|
+
import { DiscussionOrganizerWorker } from "../discussions/organizer-worker.js";
|
|
82
|
+
import { DiscussionSealer } from "../discussions/sealer.js";
|
|
83
|
+
import "../discussions/index.js";
|
|
83
84
|
import { NotesStore } from "../notes/store.js";
|
|
84
85
|
import { buildNoteAgentContext } from "../notes/agent-context.js";
|
|
85
86
|
import { NotesService } from "../notes/service.js";
|
|
@@ -665,7 +666,8 @@ var GatewayService = class {
|
|
|
665
666
|
agentService: this.ensureAgentService(),
|
|
666
667
|
messageBus: this.bus,
|
|
667
668
|
sessionStore: this.sessionIndex.getStore(),
|
|
668
|
-
getConfig: () => this.config
|
|
669
|
+
getConfig: () => this.config,
|
|
670
|
+
getWorkspace: () => this.workspacePath
|
|
669
671
|
});
|
|
670
672
|
return this.heartbeatService;
|
|
671
673
|
}
|
|
@@ -721,11 +723,21 @@ var GatewayService = class {
|
|
|
721
723
|
});
|
|
722
724
|
return this.notificationService;
|
|
723
725
|
}
|
|
724
|
-
|
|
726
|
+
taskRunDispatch = createBackgroundTask(async () => {
|
|
725
727
|
new TaskSignalService().tick();
|
|
726
728
|
this.dispatchTaskEvents();
|
|
727
729
|
this.createTaskRunDispatcher().dispatch();
|
|
728
|
-
this.createWorkflowRunService().dispatchTaskRuns();
|
|
730
|
+
await this.createWorkflowRunService().dispatchTaskRuns();
|
|
731
|
+
}, (err) => {
|
|
732
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
733
|
+
log.error({
|
|
734
|
+
err,
|
|
735
|
+
errorMessage,
|
|
736
|
+
phase: "task_run_dispatch"
|
|
737
|
+
}, `Task dispatch failed: ${errorMessage}`);
|
|
738
|
+
});
|
|
739
|
+
dispatchTaskRuns() {
|
|
740
|
+
this.taskRunDispatch();
|
|
729
741
|
}
|
|
730
742
|
async ensureTaskConversation(taskId, options = {}) {
|
|
731
743
|
const task = new TaskRepository().require(taskId);
|
|
@@ -955,6 +967,8 @@ var GatewayService = class {
|
|
|
955
967
|
});
|
|
956
968
|
log.debug("Starting gateway service...");
|
|
957
969
|
openXopcDatabase();
|
|
970
|
+
const { recoverCapabilityImports } = await import("../imports/runtime.js");
|
|
971
|
+
await recoverCapabilityImports();
|
|
958
972
|
this.createNotificationService().start();
|
|
959
973
|
this.startTime = Date.now();
|
|
960
974
|
this.running = true;
|
|
@@ -1429,6 +1443,9 @@ var GatewayService = class {
|
|
|
1429
1443
|
return a.id.localeCompare(b.id);
|
|
1430
1444
|
});
|
|
1431
1445
|
}
|
|
1446
|
+
heartbeatStatus() {
|
|
1447
|
+
return this.heartbeatService?.status() ?? null;
|
|
1448
|
+
}
|
|
1432
1449
|
/**
|
|
1433
1450
|
* Request an immediate heartbeat run (coalesced like interval/cron wakes).
|
|
1434
1451
|
*/
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ImportCandidate, ImportFile } from './types.js';
|
|
2
|
+
export declare function parseSkill(raw: string): {
|
|
3
|
+
metadata: Record<string, unknown>;
|
|
4
|
+
body: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function inspectSkill(base: Pick<ImportCandidate, 'id' | 'source' | 'scope' | 'location' | 'shared'>, files: ImportFile[]): ImportCandidate;
|
|
7
|
+
export declare function inspectMcp(base: Pick<ImportCandidate, 'id' | 'source' | 'scope' | 'location' | 'shared'>, name: string, raw: unknown): ImportCandidate;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { McpServerSchema, init_schema } from "../config/schema.js";
|
|
2
|
+
import { isValidSkillId } from "../agent/skills/managed-store.js";
|
|
3
|
+
import { containsSecret, isCredentialPath } from "./secrets.js";
|
|
4
|
+
import { digest, fileHash } from "./files.js";
|
|
5
|
+
import yaml from "js-yaml";
|
|
6
|
+
//#region src/imports/compatibility.ts
|
|
7
|
+
init_schema();
|
|
8
|
+
function parseSkill(raw) {
|
|
9
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)([\s\S]*)$/.exec(raw);
|
|
10
|
+
if (!match || match[1].length > 64 * 1024) throw new Error("A valid YAML header is required");
|
|
11
|
+
if (/(?:^|\s)[&*][\w-]+/.test(match[1])) throw new Error("YAML aliases are not supported");
|
|
12
|
+
let metadata;
|
|
13
|
+
try {
|
|
14
|
+
metadata = yaml.load(match[1], { schema: yaml.JSON_SCHEMA });
|
|
15
|
+
} catch {
|
|
16
|
+
throw new Error("Invalid YAML header");
|
|
17
|
+
}
|
|
18
|
+
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) throw new Error("Invalid skill metadata");
|
|
19
|
+
return {
|
|
20
|
+
metadata,
|
|
21
|
+
body: match[2]
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function inspectSkill(base, files) {
|
|
25
|
+
const item = {
|
|
26
|
+
...base,
|
|
27
|
+
kind: "skill",
|
|
28
|
+
name: base.id,
|
|
29
|
+
description: "",
|
|
30
|
+
files,
|
|
31
|
+
hash: fileHash(files),
|
|
32
|
+
compatibility: "compatible",
|
|
33
|
+
findings: [],
|
|
34
|
+
requiredEnv: []
|
|
35
|
+
};
|
|
36
|
+
try {
|
|
37
|
+
if (files.reduce((n, f) => n + Buffer.byteLength(f.data, "base64"), 0) > 15 * 1024 * 1024) throw new Error("Skill exceeds 15 MiB");
|
|
38
|
+
if (files.some((f) => f.path.endsWith("/SKILL.md"))) throw new Error("Nested skill scopes need separate imports");
|
|
39
|
+
const primary = files.find((f) => f.path === "SKILL.md");
|
|
40
|
+
if (!primary || Buffer.byteLength(primary.data, "base64") > 1024 * 1024) throw new Error("SKILL.md is missing or exceeds 1 MiB");
|
|
41
|
+
const { metadata, body } = parseSkill(Buffer.from(primary.data, "base64").toString("utf8"));
|
|
42
|
+
if (typeof metadata.name !== "string" || !isValidSkillId(metadata.name)) throw new Error("Skill name must be a portable identifier");
|
|
43
|
+
item.name = metadata.name;
|
|
44
|
+
if (typeof metadata.description !== "string" || !metadata.description.trim() || metadata.description.length > 1024) throw new Error("Skill description must contain 1–1024 characters");
|
|
45
|
+
item.description = metadata.description;
|
|
46
|
+
const supported = new Set([
|
|
47
|
+
"name",
|
|
48
|
+
"description",
|
|
49
|
+
"license",
|
|
50
|
+
"compatibility",
|
|
51
|
+
"metadata",
|
|
52
|
+
"disable-model-invocation",
|
|
53
|
+
"allowed-tools"
|
|
54
|
+
]);
|
|
55
|
+
for (const key of Object.keys(metadata)) if (!supported.has(key)) item.findings.push(`Unsupported skill field: ${key}`);
|
|
56
|
+
if (metadata["disable-model-invocation"] !== void 0 && typeof metadata["disable-model-invocation"] !== "boolean") item.findings.push("disable-model-invocation must be boolean");
|
|
57
|
+
if (/\$ARGUMENTS|\$\{?(?:CLAUDE_|CODEX_)|!`/.test(body)) item.findings.push("Product-specific variables or command expansion require adaptation");
|
|
58
|
+
const meta = metadata.metadata;
|
|
59
|
+
if (meta && (typeof meta !== "object" || Array.isArray(meta))) item.findings.push("Invalid metadata");
|
|
60
|
+
if (meta && Object.keys(meta).some((k) => ![
|
|
61
|
+
"author",
|
|
62
|
+
"version",
|
|
63
|
+
"short-description",
|
|
64
|
+
"short_description",
|
|
65
|
+
"category",
|
|
66
|
+
"tags",
|
|
67
|
+
"license",
|
|
68
|
+
"homepage"
|
|
69
|
+
].includes(k))) item.findings.push("Extended metadata needs manual compatibility review");
|
|
70
|
+
for (const match of body.matchAll(/\[[^\]]*\]\(([^)\s]+)\)/g)) {
|
|
71
|
+
const ref = match[1].split("#")[0];
|
|
72
|
+
if (ref && !/^(?:[a-z]+:|#)/i.test(ref) && !files.some((f) => f.path === ref.replace(/^\.\//, ""))) item.findings.push("A referenced local file is missing or outside the skill");
|
|
73
|
+
}
|
|
74
|
+
if (item.findings.length) item.compatibility = "blocked";
|
|
75
|
+
if (metadata["allowed-tools"]) item.findings.push("Source tool pre-approvals are not transferred; xopc permissions apply");
|
|
76
|
+
for (const file of files) if (isCredentialPath(file.path) || containsSecret(Buffer.from(file.data, "base64").toString("utf8"))) {
|
|
77
|
+
item.compatibility = "blocked";
|
|
78
|
+
item.findings.push("Possible credentials found; remove them from the source and scan again");
|
|
79
|
+
item.files = [];
|
|
80
|
+
item.description = "";
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
item.compatibility = "blocked";
|
|
85
|
+
item.findings.push(error instanceof Error ? error.message : "Invalid skill");
|
|
86
|
+
}
|
|
87
|
+
if (files.some((f) => containsSecret(Buffer.from(f.data, "base64").toString("utf8")))) {
|
|
88
|
+
item.files = [];
|
|
89
|
+
item.description = "";
|
|
90
|
+
item.compatibility = "blocked";
|
|
91
|
+
item.findings = ["Possible credentials found; remove them and scan again"];
|
|
92
|
+
}
|
|
93
|
+
return item;
|
|
94
|
+
}
|
|
95
|
+
function inspectMcp(base, name, raw) {
|
|
96
|
+
const item = {
|
|
97
|
+
...base,
|
|
98
|
+
kind: "mcp",
|
|
99
|
+
name,
|
|
100
|
+
description: "MCP connection draft",
|
|
101
|
+
files: [],
|
|
102
|
+
hash: "",
|
|
103
|
+
compatibility: "needs_setup",
|
|
104
|
+
findings: [],
|
|
105
|
+
requiredEnv: []
|
|
106
|
+
};
|
|
107
|
+
const value = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
108
|
+
const draft = {};
|
|
109
|
+
const supported = new Set([
|
|
110
|
+
"command",
|
|
111
|
+
"args",
|
|
112
|
+
"cwd",
|
|
113
|
+
"url",
|
|
114
|
+
"type",
|
|
115
|
+
"env",
|
|
116
|
+
"headers",
|
|
117
|
+
"http_headers",
|
|
118
|
+
"startup_timeout_sec",
|
|
119
|
+
"tool_timeout_sec"
|
|
120
|
+
]);
|
|
121
|
+
for (const [key, v] of Object.entries(value)) {
|
|
122
|
+
if (!supported.has(key)) {
|
|
123
|
+
item.compatibility = "blocked";
|
|
124
|
+
item.findings.push(`Unsupported MCP field: ${key}`);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if ([
|
|
128
|
+
"env",
|
|
129
|
+
"headers",
|
|
130
|
+
"http_headers"
|
|
131
|
+
].includes(key)) {
|
|
132
|
+
if (v && typeof v === "object") item.requiredEnv.push(...Object.keys(v));
|
|
133
|
+
item.findings.push(`${key} values omitted; configure them in xopc`);
|
|
134
|
+
} else if (key === "type") {
|
|
135
|
+
if (v === "http") draft.transport = "streamable-http";
|
|
136
|
+
else if (v === "sse") draft.transport = "sse";
|
|
137
|
+
else if (v !== "stdio") {
|
|
138
|
+
item.compatibility = "blocked";
|
|
139
|
+
item.findings.push("Unsupported MCP transport");
|
|
140
|
+
}
|
|
141
|
+
} else if (key === "startup_timeout_sec" || key === "tool_timeout_sec") if (typeof v === "number") draft[key === "startup_timeout_sec" ? "connectionTimeoutMs" : "requestTimeoutMs"] = v * 1e3;
|
|
142
|
+
else {
|
|
143
|
+
item.compatibility = "blocked";
|
|
144
|
+
item.findings.push("Invalid MCP timeout");
|
|
145
|
+
}
|
|
146
|
+
else draft[key] = v;
|
|
147
|
+
}
|
|
148
|
+
if (typeof draft.url === "string") try {
|
|
149
|
+
const url = new URL(draft.url);
|
|
150
|
+
if (url.username || url.password || url.search || url.hash) {
|
|
151
|
+
delete draft.url;
|
|
152
|
+
item.findings.push("URL credentials or query parameters omitted; enter a clean endpoint");
|
|
153
|
+
}
|
|
154
|
+
} catch {
|
|
155
|
+
delete draft.url;
|
|
156
|
+
}
|
|
157
|
+
if (containsSecret(JSON.stringify(draft)) || /--?[a-z-]*(?:token|password|secret|api-key|authorization)(?:[=\s\"]|$)/i.test(JSON.stringify(draft))) {
|
|
158
|
+
Object.keys(draft).forEach((k) => delete draft[k]);
|
|
159
|
+
item.findings.push("Possible credentials omitted from connection fields");
|
|
160
|
+
}
|
|
161
|
+
if (!McpServerSchema.safeParse(draft).success || !draft.command && !draft.url) {
|
|
162
|
+
item.compatibility = "blocked";
|
|
163
|
+
item.findings.push("A valid command or URL is required");
|
|
164
|
+
}
|
|
165
|
+
if (base.scope === "project") {
|
|
166
|
+
item.compatibility = "blocked";
|
|
167
|
+
item.findings.push("Project-scoped MCP activation is not supported");
|
|
168
|
+
}
|
|
169
|
+
item.mcp = draft;
|
|
170
|
+
item.hash = digest(draft);
|
|
171
|
+
return item;
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
export { inspectMcp, inspectSkill, parseSkill };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ImportCandidate } from './types.js';
|
|
2
|
+
/** Small source records fit the existing retrieval budget without injecting entire documents. */
|
|
3
|
+
export declare function importContextDocument(item: ImportCandidate, projectId?: string): 'created' | 'unchanged' | 'kept';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
|
|
2
|
+
import { listKnowledgeItems, writeKnowledgeItem } from "../knowledge-memory/repository.js";
|
|
3
|
+
import "../knowledge-memory/index.js";
|
|
4
|
+
import { digest } from "./files.js";
|
|
5
|
+
//#region src/imports/contextImport.ts
|
|
6
|
+
/** Small source records fit the existing retrieval budget without injecting entire documents. */
|
|
7
|
+
function importContextDocument(item, projectId) {
|
|
8
|
+
const content = item.content?.trim();
|
|
9
|
+
if (!content) throw new Error("Missing context");
|
|
10
|
+
const scope = projectId ? {
|
|
11
|
+
type: "project",
|
|
12
|
+
id: projectId
|
|
13
|
+
} : { type: "global" };
|
|
14
|
+
const canonicalKey = `product-import:${item.source}:${digest(item.location)}`;
|
|
15
|
+
return runSqliteWriteTransaction(() => {
|
|
16
|
+
const existing = listKnowledgeItems({
|
|
17
|
+
canonicalKey,
|
|
18
|
+
scope,
|
|
19
|
+
statuses: [
|
|
20
|
+
"active",
|
|
21
|
+
"candidate",
|
|
22
|
+
"needs_review",
|
|
23
|
+
"stale",
|
|
24
|
+
"archived",
|
|
25
|
+
"rejected"
|
|
26
|
+
]
|
|
27
|
+
})[0];
|
|
28
|
+
if (existing) return existing.source.hash === item.hash ? "unchanged" : "kept";
|
|
29
|
+
for (let offset = 0, part = 0; offset < content.length; offset += 2e3, part++) {
|
|
30
|
+
const chunk = content.slice(offset, offset + 2e3).trim();
|
|
31
|
+
if (!chunk) continue;
|
|
32
|
+
writeKnowledgeItem({
|
|
33
|
+
kind: "note",
|
|
34
|
+
scope,
|
|
35
|
+
content: chunk,
|
|
36
|
+
canonicalKey: part === 0 ? canonicalKey : `${canonicalKey}:part:${part}`,
|
|
37
|
+
recordClass: "source_index",
|
|
38
|
+
status: "active",
|
|
39
|
+
confidence: .7,
|
|
40
|
+
importance: .7,
|
|
41
|
+
originClass: "system",
|
|
42
|
+
source: {
|
|
43
|
+
kind: "product_import",
|
|
44
|
+
product: item.source,
|
|
45
|
+
path: item.location,
|
|
46
|
+
hash: item.hash,
|
|
47
|
+
part,
|
|
48
|
+
importedAt: Date.now()
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return "created";
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { importContextDocument };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ImportFile } from './types.js';
|
|
2
|
+
export declare const IMPORT_LIMITS: {
|
|
3
|
+
file: number;
|
|
4
|
+
total: number;
|
|
5
|
+
count: number;
|
|
6
|
+
depth: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function digest(value: unknown): string;
|
|
9
|
+
export declare function fileHash(files: ImportFile[]): string;
|
|
10
|
+
export declare function inside(root: string, path: string): boolean;
|
|
11
|
+
export declare function safePath(path: string): boolean;
|
|
12
|
+
export declare function readRegularFile(path: string, limit?: number): Buffer;
|
|
13
|
+
export declare function readTree(rootPath: string): ImportFile[];
|