@xopcai/xopc 0.0.159 → 0.0.161
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{action-boundary-settings-panel-CaRK2vIy.js → action-boundary-settings-panel-BIVBUiNT.js} +1 -1
- package/dist/gateway/static/root/assets/activity-store-o7GRljfD.js +1 -0
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BeVf03hx.js → agent-avatar-dicebear-9R3szLMc.js} +1 -1
- package/dist/gateway/static/root/assets/agents-_qbUaYbs.js +2 -0
- package/dist/gateway/static/root/assets/{api-BjcBIV2O.js → api-jSTqFMq9.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-BG2ulMYw.js → app-management-settings-panel-D81cyE76.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-0Kjenr2T.js → appearance-settings-Ct6bZQCV.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-0sPqD0FK.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-ChiNnH1e.js → archive-plugin-CfD7AZKn.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-pbBlQR-g.js → attachment-load-CP_Gaxt3.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-CTZDOyDy.js → automation-suggestion-card-BGKM_NDZ.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-BxWBuBsE.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-BeZGzpGF.js → binary-plugins-IPr2EVnS.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-BnDmhN5Z.js → block-editor-B6aHEGA6.js} +3 -3
- package/dist/gateway/static/root/assets/browser-settings-page-C499b9AH.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-Ii36j_7v.js → capability-presets-api-CCX9-LjU.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-settings-panel-Cl3dw4yh.js → capability-presets-settings-panel-Dva9SiPK.js} +2 -2
- package/dist/gateway/static/root/assets/{channel-recipient-api-CFqrSi5K.js → channel-recipient-api-MmLMLDWf.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-DNAU7u9o.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-CIOH7tgn.js +1 -0
- package/dist/gateway/static/root/assets/check-DUBLcKHA.js +1 -0
- package/dist/gateway/static/root/assets/circle-alert-BEjgurr2.js +1 -0
- package/dist/gateway/static/root/assets/cn-JdwAhgNB.js +1 -0
- package/dist/gateway/static/root/assets/{connector-logo-CiLxuZ8S.js → connector-logo-BbjzIs87.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-TwiBp8vY.js +2 -0
- package/dist/gateway/static/root/assets/createLucideIcon-DPHK1VkS.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-CYH5ZSXy.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-settings-DQLqGLc0.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-sprite-DTqXSpqf.js +1 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-CC2VL5Mc.js → directory-picker-path-field-ClsiXhmz.js} +1 -1
- package/dist/gateway/static/root/assets/{dreaming-settings-KY7ZfVsy.js → dreaming-settings-CjMPPdzg.js} +3 -3
- package/dist/gateway/static/root/assets/{extension-debug-page-C8KBeucs.js → extension-debug-page-57eizjhB.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CV-5Kcyk.js → extension-page-CsbSb3u1.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DvUtDrAu.js → extension-settings-page-DKA9-lyP.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-DXfVeJLc.js → gateway-config-api-DIPJuzBZ.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-CDLqAQY7.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-cxQ94qMm.js → gateway-startup-retry-DrfSJtvT.js} +1 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-Q3B0E-0s.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-C5VAK04G.js +4 -0
- package/dist/gateway/static/root/assets/{goals-config-api-P8VhLqYF.js → goals-config-api-lGC10UkU.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-BJb-5IYw.js +2 -0
- package/dist/gateway/static/root/assets/{goals-settings-DHXyaH_O.js → goals-settings-BWq0pdrA.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-CNckP_Dv.js → heartbeat-settings-D4o8BJVv.js} +1 -1
- package/dist/gateway/static/root/assets/index-CkUHH170.css +1 -0
- package/dist/gateway/static/root/assets/index-vq_cJZJC.js +199 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-jXmv1RVX.js → keyboard-shortcuts-settings-6T2QYehS.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-euH17Kvb.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-Cs5LKjtc.js → local-apps-page-DCjg2K9f.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-C_M71MK9.js → locale-store-Dar-xqQr.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-CyYCBqym.js +7 -0
- package/dist/gateway/static/root/assets/{markdown-split-BFqeSNte.js → markdown-split-rt1XO43g.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-Cc8379gq.js → media-plugins-Bwo8CJuh.js} +1 -1
- package/dist/gateway/static/root/assets/messages-CxySDn9d.js +5 -0
- package/dist/gateway/static/root/assets/models-hub-panel-DObxxjc9.js +2 -0
- package/dist/gateway/static/root/assets/{notes-page-DlGU4ipo.js → notes-page-Dp-pkX7d.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-CCb4RXcZ.js +4 -0
- package/dist/gateway/static/root/assets/{page-tabs-kFOswGlG.js → page-tabs-CzZ4iKOb.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-CvjuvABg.js → preview-open-alternatives-3E_Qa-58.js} +1 -1
- package/dist/gateway/static/root/assets/product-delivery-BTRT9kCS.js +1 -0
- package/dist/gateway/static/root/assets/product-open-page-B2T48Sbt.js +1 -0
- package/dist/gateway/static/root/assets/project-detail-page-BvEcriRC.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-B_e79MRJ.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-DGcqnYfR.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-DwrD7ODx.js → save-bar-controls-CkeKasGm.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-Ba4WzTmE.js → schedule-field-TkqUL3wm.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-DvydL8eF.js → schema-form-DC-iU5IG.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DQZhrDJ8.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BkdeN99Y.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-LaQqGGej.js → settings-collapsible-section-Dpqth3oA.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-DruI__qO.js → settings-form-section-CNqF6IwY.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-UvA-eDhF.js → settings-loading-skeleton-DkvwitK_.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-DG-XMIPu.js → settings-page-layout-DWvNgISx.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-5oCGSebm.js → settings-page-qPG7I2zd.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-lwRAh9aw.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-FLVeTrRt.js → share-preview-page-DGpSPW-n.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-BGj9IVUr.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-B2UBAs5U.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-DdjzFZ8I.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-CeK93xVi.js → system-settings-panel-8TxxSbUw.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-C8ohIHhw.js → theme-store-B5FzMWOx.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-D1Lr2gG0.js → typed-models-lib-DEGtSA5h.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-BWzou5ar.js → use-channel-catalog-BDU6FneG.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-BTTZpAGV.js +1 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-DuDQd-WU.js +1 -0
- package/dist/gateway/static/root/assets/work-discovery-page-BUVkVCsW.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-DeEPPu4n.js +1 -0
- package/dist/gateway/static/root/assets/work-page-DQNUyTUq.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Bynqe4kV.js → workflow-run-setup-panel-Cwi8Pz7n.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-BIy4wrzO.js +11 -0
- package/dist/gateway/static/root/index.html +12 -10
- package/dist/package.js +1 -1
- package/dist/src/automations/service/action-executor.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/chat-stream/mapper.d.ts +1 -0
- package/dist/src/gateway/chat-stream/mapper.js +43 -7
- package/dist/src/gateway/hono/routes/home.js +15 -2
- package/dist/src/gateway/hono/routes/work-discovery.js +451 -0
- package/dist/src/gateway/hono/sse.d.ts +2 -0
- package/dist/src/gateway/hono/sse.js +10 -3
- package/dist/src/gateway/run-loop.js +17 -7
- package/dist/src/gateway/server.d.ts +1 -0
- package/dist/src/gateway/server.js +9 -0
- package/dist/src/gateway/service.d.ts +2 -0
- package/dist/src/gateway/service.js +27 -2
- package/dist/src/local-apps/preview-runtime-bridge.d.ts +1 -1
- package/dist/src/local-apps/preview-runtime-bridge.js +14 -11
- package/dist/src/mobile/notification-service.js +1 -0
- package/dist/src/pets/factory.js +23 -26
- package/dist/src/pets/manifest.d.ts +2 -1
- package/dist/src/pets/manifest.js +15 -8
- package/dist/src/pets/validator.js +8 -3
- package/dist/src/proactive/calendar-signals.d.ts +4 -0
- package/dist/src/proactive/calendar-signals.js +120 -0
- package/dist/src/proactive/focus-service.d.ts +62 -0
- package/dist/src/proactive/focus-service.js +173 -0
- package/dist/src/proactive/index.d.ts +7 -0
- package/dist/src/proactive/index.js +7 -0
- package/dist/src/proactive/insight-repository.d.ts +19 -0
- package/dist/src/proactive/insight-repository.js +106 -0
- package/dist/src/proactive/run-processor.d.ts +19 -0
- package/dist/src/proactive/run-processor.js +125 -0
- package/dist/src/proactive/trial-expiry-reconciler.d.ts +5 -0
- package/dist/src/proactive/trial-expiry-reconciler.js +30 -0
- package/dist/src/proactive/types.d.ts +64 -0
- package/dist/src/proactive/types.js +1 -0
- package/dist/src/proactive/watch-repository.d.ts +28 -0
- package/dist/src/proactive/watch-repository.js +129 -0
- package/dist/src/storage/sqlite/knowledge-repository.d.ts +1 -0
- package/dist/src/storage/sqlite/knowledge-repository.js +4 -0
- package/dist/src/storage/sqlite/migrations/047_work_discovery_sources.sql +16 -0
- package/dist/src/storage/sqlite/migrations/048_work_understanding_investigations.sql +40 -0
- package/dist/src/storage/sqlite/migrations/049_work_understanding_threads.sql +52 -0
- package/dist/src/storage/sqlite/migrations/050_work_discovery_source_refreshes.sql +15 -0
- package/dist/src/storage/sqlite/migrations/051_focus_watches.sql +26 -0
- package/dist/src/storage/sqlite/migrations/052_proactive_insights.sql +25 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/work-discovery/analyzer.d.ts +12 -1
- package/dist/src/work-discovery/analyzer.js +126 -7
- package/dist/src/work-discovery/candidate-discovery.d.ts +17 -0
- package/dist/src/work-discovery/candidate-discovery.js +279 -0
- package/dist/src/work-discovery/evaluation.d.ts +11 -0
- package/dist/src/work-discovery/evaluation.js +17 -0
- package/dist/src/work-discovery/governance.d.ts +25 -0
- package/dist/src/work-discovery/governance.js +96 -0
- package/dist/src/work-discovery/incremental-repository.d.ts +13 -0
- package/dist/src/work-discovery/incremental-repository.js +55 -0
- package/dist/src/work-discovery/incremental.d.ts +2 -0
- package/dist/src/work-discovery/incremental.js +7 -0
- package/dist/src/work-discovery/index.d.ts +10 -0
- package/dist/src/work-discovery/index.js +13 -3
- package/dist/src/work-discovery/investigation-repository.d.ts +16 -0
- package/dist/src/work-discovery/investigation-repository.js +122 -0
- package/dist/src/work-discovery/investigator.d.ts +55 -0
- package/dist/src/work-discovery/investigator.js +358 -0
- package/dist/src/work-discovery/probe.d.ts +22 -0
- package/dist/src/work-discovery/probe.js +88 -1
- package/dist/src/work-discovery/service.d.ts +110 -1
- package/dist/src/work-discovery/service.js +578 -8
- package/dist/src/work-discovery/source-repository.d.ts +13 -0
- package/dist/src/work-discovery/source-repository.js +72 -0
- package/dist/src/work-discovery/thread-repository.d.ts +34 -0
- package/dist/src/work-discovery/thread-repository.js +112 -0
- package/dist/src/work-discovery/thread-service.d.ts +8 -0
- package/dist/src/work-discovery/thread-service.js +104 -0
- package/dist/src/work-discovery/types.d.ts +145 -0
- package/package.json +1 -1
- package/skills/creative/hatch-pet/SKILL.md +2 -2
- package/dist/gateway/static/root/assets/activity-store-C-57rvkX.js +0 -1
- package/dist/gateway/static/root/assets/agents-P05C4A-3.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-DANGrBpN.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-BXfGL_cm.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-534luV0B.js +0 -8
- package/dist/gateway/static/root/assets/channels-settings-M91zFJVi.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-vooEF4lG.js +0 -1
- package/dist/gateway/static/root/assets/check-BODqo66o.js +0 -1
- package/dist/gateway/static/root/assets/circle-alert-RzVebuX2.js +0 -1
- package/dist/gateway/static/root/assets/cn-BQ_8x0bT.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-CV5UulW6.js +0 -2
- package/dist/gateway/static/root/assets/desktop-pet-Cil2eSpW.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-settings-DwsC6l5F.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-sprite-DqZ6fbIj.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-BpJ0v56l.js +0 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-lMHD-2CX.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-yPmF-G7b.js +0 -4
- package/dist/gateway/static/root/assets/goals-page-BHTCKobG.js +0 -2
- package/dist/gateway/static/root/assets/index-BgZhEOkN.css +0 -1
- package/dist/gateway/static/root/assets/index-BuqGh2SW.js +0 -193
- package/dist/gateway/static/root/assets/local-app-workbench-page-zpp_wciE.js +0 -2
- package/dist/gateway/static/root/assets/logs-page-B4CRYqYR.js +0 -7
- package/dist/gateway/static/root/assets/messages-BuT0SR1R.js +0 -5
- package/dist/gateway/static/root/assets/models-hub-panel-1hdoAoB_.js +0 -2
- package/dist/gateway/static/root/assets/notes-workbench-DQEAZTA5.js +0 -4
- package/dist/gateway/static/root/assets/project-detail-page-Bp9bC7A6.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-RNorFXTw.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BqRTcM_6.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-B2EfW1cm.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-CklL85T4.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-D3my1HgB.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-DVxgjrsP.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-Bcu58ZaF.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-CDd-84FE.js +0 -2
- package/dist/gateway/static/root/assets/sliding-segmented-uWmrxE3P.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-1OyJnTyR.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-B2ncRwHS.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-page-BZlY4PZL.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-DPXMrneY.js +0 -1
- package/dist/gateway/static/root/assets/work-page-CTERoqyv.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-CLgsnqFQ.js +0 -11
|
@@ -48,6 +48,10 @@ import { startConnectorMemorySyncCoordinator } from "../connectors/memory-sync-c
|
|
|
48
48
|
import { startConnectorSupervisor } from "../connectors/supervisor.js";
|
|
49
49
|
import { assertGatewayAuthConfigured, extractToken, resolveGatewayAuth, validateToken } from "./auth.js";
|
|
50
50
|
import { reconcileDreamingAutomations } from "./dreaming-automation-reconciler.js";
|
|
51
|
+
import { FocusService } from "../proactive/focus-service.js";
|
|
52
|
+
import { processFocusAutomationRun } from "../proactive/run-processor.js";
|
|
53
|
+
import { startTrialExpiryReconciler } from "../proactive/trial-expiry-reconciler.js";
|
|
54
|
+
import "../proactive/index.js";
|
|
51
55
|
import { NotesStore } from "../notes/store.js";
|
|
52
56
|
import { buildNoteAgentContext } from "../notes/agent-context.js";
|
|
53
57
|
import { NotesService } from "../notes/service.js";
|
|
@@ -141,6 +145,7 @@ var GatewayService = class {
|
|
|
141
145
|
connectedKnowledgeCoordinator = null;
|
|
142
146
|
stopAutomationProductEventBridge = null;
|
|
143
147
|
stopSessionTranscriptAutomationEvents = null;
|
|
148
|
+
stopFocusTrialExpiryReconciler = null;
|
|
144
149
|
/**
|
|
145
150
|
* Webchat agent invocation surface (`runAgent`, `abortAgentRun`, `steer*`,
|
|
146
151
|
* `submitClarifyResponse`, clarify-bridge dispatch). Owns the
|
|
@@ -356,7 +361,7 @@ var GatewayService = class {
|
|
|
356
361
|
getDefaultAgentId: () => getDefaultAgentId(this.config),
|
|
357
362
|
getProjectWorkspaceRoot: (projectId) => this.projects.get(projectId)?.workspaceRoot,
|
|
358
363
|
workflowRunService: this.createWorkflowRunService(),
|
|
359
|
-
onRunCompleted: (run) => this.
|
|
364
|
+
onRunCompleted: (run) => this.handleAutomationRunCompleted(run)
|
|
360
365
|
});
|
|
361
366
|
this._agentService.persistentGoals.setWebchatContinuationScheduler((sessionKey, message) => {
|
|
362
367
|
const scheduleWhenIdle = () => {
|
|
@@ -666,7 +671,7 @@ var GatewayService = class {
|
|
|
666
671
|
agentService: this.agentService,
|
|
667
672
|
getDefaultAgentId: () => getDefaultAgentId(this.config),
|
|
668
673
|
workflowRunService: this.createWorkflowRunService(),
|
|
669
|
-
onRunCompleted: (run) => this.
|
|
674
|
+
onRunCompleted: (run) => this.handleAutomationRunCompleted(run)
|
|
670
675
|
});
|
|
671
676
|
this.startAutomationProductEventBridge();
|
|
672
677
|
await trace.measure("workflows.reconcile", () => this.reconcileInterruptedWorkflowRuns());
|
|
@@ -679,6 +684,7 @@ var GatewayService = class {
|
|
|
679
684
|
});
|
|
680
685
|
await trace.measure("automations.initialize", () => this.automationService.initialize());
|
|
681
686
|
await trace.measure("dreaming.reconcile", () => this.reconcileDreamingAutomations());
|
|
687
|
+
this.stopFocusTrialExpiryReconciler = startTrialExpiryReconciler(new FocusService(this.automationService));
|
|
682
688
|
await this.notesService.initialize();
|
|
683
689
|
this.ensureHeartbeatService().start(heartbeatRunnerConfigFromConfig(this.config));
|
|
684
690
|
this.connectorSupervisor = startConnectorSupervisor({
|
|
@@ -834,6 +840,8 @@ var GatewayService = class {
|
|
|
834
840
|
this.lastChannelConnectDeferMode = "auto";
|
|
835
841
|
this.lastChannelConnectDeferSource = "off";
|
|
836
842
|
await this.channelManager.stop();
|
|
843
|
+
await this.stopFocusTrialExpiryReconciler?.();
|
|
844
|
+
this.stopFocusTrialExpiryReconciler = null;
|
|
837
845
|
await this.automationService.stop();
|
|
838
846
|
this.stopAutomationProductEventBridge?.();
|
|
839
847
|
this.stopAutomationProductEventBridge = null;
|
|
@@ -1234,6 +1242,23 @@ var GatewayService = class {
|
|
|
1234
1242
|
this.createGoalNotificationService().handleGatewayEvent(type, payload);
|
|
1235
1243
|
this.createMobileNotificationService().handleGatewayEvent(type, payload);
|
|
1236
1244
|
}
|
|
1245
|
+
handleAutomationRunCompleted(run) {
|
|
1246
|
+
try {
|
|
1247
|
+
const proactive = processFocusAutomationRun(run);
|
|
1248
|
+
this.emit("automation.run.completed", {
|
|
1249
|
+
run,
|
|
1250
|
+
silent: proactive.handled
|
|
1251
|
+
});
|
|
1252
|
+
if (proactive.insight) this.emit("proactive.insight.created", { insight: proactive.insight });
|
|
1253
|
+
} catch (err) {
|
|
1254
|
+
log.warn({
|
|
1255
|
+
err,
|
|
1256
|
+
automationId: run.automationId,
|
|
1257
|
+
runId: run.id
|
|
1258
|
+
}, "Proactive result processing failed");
|
|
1259
|
+
this.emit("automation.run.completed", { run });
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1237
1262
|
startAutomationProductEventBridge() {
|
|
1238
1263
|
this.stopAutomationProductEventBridge?.();
|
|
1239
1264
|
this.stopSessionTranscriptAutomationEvents?.();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { LocalAppAcceptanceConfig } from './acceptance.js';
|
|
2
|
-
export declare const LOCAL_APP_RUNTIME_BRIDGE_SCRIPT = "(() => {\n let hostPort = null;\n const
|
|
2
|
+
export declare const LOCAL_APP_RUNTIME_BRIDGE_SCRIPT = "(() => {\n let hostPort = null;\n const latest = new Map();\n const send = (type, detail) => {\n const message = { source: 'xopc-local-app-preview', version: 1, type, detail };\n latest.set(type, message);\n hostPort?.postMessage(message);\n };\n const connect = (event) => {\n const message = event.data;\n if (event.source !== parent || message?.source !== 'xopc-local-app-host' || message?.version !== 1 || message?.type !== 'connect' || !event.ports[0]) return;\n event.stopImmediatePropagation();\n hostPort?.close();\n hostPort = event.ports[0];\n hostPort.start();\n for (const message of latest.values()) hostPort.postMessage(message);\n };\n addEventListener('message', connect, true);\n const text = (value) => String(value ?? '').slice(0, 500);\n let runtimeFailure = '';\n const visible = (element) => {\n const style = getComputedStyle(element);\n const rect = element.getBoundingClientRect();\n return style.display !== 'none' && style.visibility !== 'hidden' && Number(style.opacity) !== 0 && rect.width > 0 && rect.height > 0;\n };\n const accessibleName = (element) => {\n const labelledBy = text(element.getAttribute('aria-labelledby'));\n const labelledText = labelledBy.split(/\\s+/).map((id) => document.getElementById(id)?.textContent).filter(Boolean).join(' ');\n return text(element.getAttribute('aria-label') || labelledText || element.labels?.[0]?.textContent || element.textContent || element.getAttribute('alt') || element.getAttribute('title') || element.getAttribute('placeholder') || element.getAttribute('name')).trim();\n };\n const target = (id) => Array.from(document.querySelectorAll('[data-xopc-test-id]'))\n .find((element) => element.getAttribute('data-xopc-test-id') === id);\n const settle = async () => {\n await Promise.resolve();\n await Promise.resolve();\n };\n const readCriteria = () => {\n const encoded = document.querySelector('meta[name=\"xopc-local-app-acceptance\"]')?.content;\n if (!encoded) return { schemaVersion: 1, scenarios: [] };\n const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n const bytes = Uint8Array.from(atob(padded), (char) => char.charCodeAt(0));\n return JSON.parse(new TextDecoder().decode(bytes));\n };\n addEventListener('error', (event) => {\n runtimeFailure = text(event.message || 'A preview resource failed to load');\n send('error', {\n kind: 'script_error',\n message: runtimeFailure,\n filename: text(event.filename),\n line: Number(event.lineno) || undefined,\n column: Number(event.colno) || undefined,\n });\n }, true);\n addEventListener('unhandledrejection', (event) => {\n runtimeFailure = text(event.reason instanceof Error ? event.reason.message : event.reason);\n send('error', { kind: 'unhandled_rejection', message: runtimeFailure });\n });\n const runAcceptance = () => {\n const checks = [];\n const bodyReady = Boolean(document.body);\n checks.push({ id: 'document', status: bodyReady ? 'passed' : 'failed', message: bodyReady ? 'Preview document loaded' : 'Preview document has no body' });\n const contentNodes = bodyReady ? Array.from(document.body.querySelectorAll('*')).slice(0, 300) : [];\n const hasVisibleContent = contentNodes.some((element) => visible(element) && (text(element.textContent).trim() || /^(IMG|SVG|CANVAS|VIDEO)$/.test(element.tagName)));\n checks.push({ id: 'content', status: hasVisibleContent ? 'passed' : 'failed', message: hasVisibleContent ? 'Visible content rendered' : 'No visible content was rendered' });\n const controls = bodyReady ? Array.from(document.body.querySelectorAll('a[href], button, input, select, textarea, [role=\"button\"], [tabindex]')).filter((element) => visible(element) && !element.disabled && element.getAttribute('aria-disabled') !== 'true') : [];\n const unnamed = controls.filter((element) => !accessibleName(element));\n let interactionStatus = controls.length ? 'passed' : 'skipped';\n let interactionMessage = controls.length ? controls.length + ' interactive control(s) are discoverable' : 'No interactive controls to exercise';\n if (unnamed.length) {\n interactionStatus = 'failed';\n interactionMessage = unnamed.length + ' interactive control(s) need an accessible name';\n } else if (controls.length) {\n const previous = document.activeElement;\n controls[0].focus({ preventScroll: true });\n if (document.activeElement !== controls[0]) {\n interactionStatus = 'failed';\n interactionMessage = 'The first interactive control could not receive focus';\n }\n if (previous && typeof previous.focus === 'function') previous.focus({ preventScroll: true });\n else controls[0].blur();\n }\n checks.push({ id: 'interaction', status: interactionStatus, message: interactionMessage });\n send('acceptance', {\n status: checks.some((check) => check.status === 'failed') ? 'failed' : 'passed',\n checks,\n interactiveCount: controls.length,\n });\n };\n const runCriteria = async () => {\n const params = new URLSearchParams(location.search);\n if (params.get('xopcAcceptance') !== '1') return;\n let config;\n try {\n config = readCriteria();\n } catch (error) {\n send('criteria', { status: 'failed', scenarioCount: 0, scenarios: [{ id: 'config', name: 'Acceptance config', status: 'failed', message: text(error instanceof Error ? error.message : error) }] });\n return;\n }\n const requestedScenario = params.get('xopcScenario');\n const selectedScenarios = requestedScenario\n ? (config.scenarios || []).filter((scenario) => scenario.id === requestedScenario)\n : config.scenarios || [];\n if (requestedScenario && !selectedScenarios.length) {\n send('criteria', { status: 'failed', scenarioCount: 1, scenarios: [{ id: requestedScenario, name: requestedScenario, status: 'failed', message: 'Requested scenario was not found' }] });\n return;\n }\n const scenarios = [];\n for (const scenario of selectedScenarios) {\n let failure = '';\n for (let index = 0; index < scenario.steps.length; index += 1) {\n const step = scenario.steps[index];\n try {\n if (step.action === 'click') {\n const element = target(step.target);\n if (!(element instanceof HTMLElement)) throw new Error('Target \"' + step.target + '\" was not found');\n if ('disabled' in element && element.disabled || element.getAttribute('aria-disabled') === 'true') throw new Error('Target \"' + step.target + '\" is disabled');\n element.click();\n await settle();\n } else if (step.action === 'fill') {\n const element = target(step.target);\n if (!(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)) throw new Error('Target \"' + step.target + '\" cannot be filled');\n const prototype = element instanceof HTMLInputElement ? HTMLInputElement.prototype : element instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLSelectElement.prototype;\n const setter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set;\n if (setter) setter.call(element, step.value);\n else element.value = step.value;\n element.dispatchEvent(new Event('input', { bubbles: true }));\n element.dispatchEvent(new Event('change', { bubbles: true }));\n await settle();\n } else if (step.assert === 'text_visible') {\n const found = Array.from(document.body.querySelectorAll('*')).some((element) => visible(element) && text(element.textContent).includes(step.text));\n if (!found) throw new Error('Visible text was not found: \"' + step.text + '\"');\n } else if (step.assert === 'element_exists') {\n if (!target(step.target)) throw new Error('Target \"' + step.target + '\" was not found');\n } else if (step.assert === 'value_equals') {\n const element = target(step.target);\n if (!element || !('value' in element) || element.value !== step.value) throw new Error('Target \"' + step.target + '\" does not have the expected value');\n }\n if (runtimeFailure) throw new Error(runtimeFailure);\n } catch (error) {\n failure = 'Step ' + (index + 1) + ': ' + text(error instanceof Error ? error.message : error);\n break;\n }\n }\n scenarios.push({ id: scenario.id, name: scenario.name, status: failure ? 'failed' : 'passed', message: failure || 'Scenario passed' });\n }\n send('criteria', {\n status: scenarios.some((scenario) => scenario.status === 'failed') ? 'failed' : 'passed',\n scenarioCount: scenarios.length,\n scenarios,\n });\n };\n const loaded = () => {\n send('ready', { readyState: document.readyState });\n void runCriteria();\n if (typeof requestAnimationFrame === 'function') requestAnimationFrame(() => requestAnimationFrame(runAcceptance));\n else setTimeout(runAcceptance, 0);\n };\n if (document.readyState === 'complete') queueMicrotask(loaded);\n else addEventListener('load', loaded, { once: true });\n})();";
|
|
3
3
|
export declare const LOCAL_APP_RUNTIME_BRIDGE_HASH: string;
|
|
4
4
|
export declare function injectLocalAppRuntimeBridge(html: string, acceptance?: LocalAppAcceptanceConfig): string;
|
|
@@ -3,20 +3,20 @@ import { createHash } from "node:crypto";
|
|
|
3
3
|
//#region src/local-apps/preview-runtime-bridge.ts
|
|
4
4
|
const LOCAL_APP_RUNTIME_BRIDGE_SCRIPT = `(() => {
|
|
5
5
|
let hostPort = null;
|
|
6
|
-
const
|
|
6
|
+
const latest = new Map();
|
|
7
7
|
const send = (type, detail) => {
|
|
8
8
|
const message = { source: 'xopc-local-app-preview', version: 1, type, detail };
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
latest.set(type, message);
|
|
10
|
+
hostPort?.postMessage(message);
|
|
11
11
|
};
|
|
12
12
|
const connect = (event) => {
|
|
13
13
|
const message = event.data;
|
|
14
14
|
if (event.source !== parent || message?.source !== 'xopc-local-app-host' || message?.version !== 1 || message?.type !== 'connect' || !event.ports[0]) return;
|
|
15
15
|
event.stopImmediatePropagation();
|
|
16
|
-
|
|
16
|
+
hostPort?.close();
|
|
17
17
|
hostPort = event.ports[0];
|
|
18
18
|
hostPort.start();
|
|
19
|
-
for (const
|
|
19
|
+
for (const message of latest.values()) hostPort.postMessage(message);
|
|
20
20
|
};
|
|
21
21
|
addEventListener('message', connect, true);
|
|
22
22
|
const text = (value) => String(value ?? '').slice(0, 500);
|
|
@@ -33,7 +33,10 @@ const LOCAL_APP_RUNTIME_BRIDGE_SCRIPT = `(() => {
|
|
|
33
33
|
};
|
|
34
34
|
const target = (id) => Array.from(document.querySelectorAll('[data-xopc-test-id]'))
|
|
35
35
|
.find((element) => element.getAttribute('data-xopc-test-id') === id);
|
|
36
|
-
const
|
|
36
|
+
const settle = async () => {
|
|
37
|
+
await Promise.resolve();
|
|
38
|
+
await Promise.resolve();
|
|
39
|
+
};
|
|
37
40
|
const readCriteria = () => {
|
|
38
41
|
const encoded = document.querySelector('meta[name="xopc-local-app-acceptance"]')?.content;
|
|
39
42
|
if (!encoded) return { schemaVersion: 1, scenarios: [] };
|
|
@@ -116,7 +119,7 @@ const LOCAL_APP_RUNTIME_BRIDGE_SCRIPT = `(() => {
|
|
|
116
119
|
if (!(element instanceof HTMLElement)) throw new Error('Target "' + step.target + '" was not found');
|
|
117
120
|
if ('disabled' in element && element.disabled || element.getAttribute('aria-disabled') === 'true') throw new Error('Target "' + step.target + '" is disabled');
|
|
118
121
|
element.click();
|
|
119
|
-
await
|
|
122
|
+
await settle();
|
|
120
123
|
} else if (step.action === 'fill') {
|
|
121
124
|
const element = target(step.target);
|
|
122
125
|
if (!(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)) throw new Error('Target "' + step.target + '" cannot be filled');
|
|
@@ -126,7 +129,7 @@ const LOCAL_APP_RUNTIME_BRIDGE_SCRIPT = `(() => {
|
|
|
126
129
|
else element.value = step.value;
|
|
127
130
|
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
128
131
|
element.dispatchEvent(new Event('change', { bubbles: true }));
|
|
129
|
-
await
|
|
132
|
+
await settle();
|
|
130
133
|
} else if (step.assert === 'text_visible') {
|
|
131
134
|
const found = Array.from(document.body.querySelectorAll('*')).some((element) => visible(element) && text(element.textContent).includes(step.text));
|
|
132
135
|
if (!found) throw new Error('Visible text was not found: "' + step.text + '"');
|
|
@@ -152,9 +155,9 @@ const LOCAL_APP_RUNTIME_BRIDGE_SCRIPT = `(() => {
|
|
|
152
155
|
};
|
|
153
156
|
const loaded = () => {
|
|
154
157
|
send('ready', { readyState: document.readyState });
|
|
155
|
-
|
|
156
|
-
if (typeof requestAnimationFrame === 'function') requestAnimationFrame(() => requestAnimationFrame(
|
|
157
|
-
else setTimeout(
|
|
158
|
+
void runCriteria();
|
|
159
|
+
if (typeof requestAnimationFrame === 'function') requestAnimationFrame(() => requestAnimationFrame(runAcceptance));
|
|
160
|
+
else setTimeout(runAcceptance, 0);
|
|
158
161
|
};
|
|
159
162
|
if (document.readyState === 'complete') queueMicrotask(loaded);
|
|
160
163
|
else addEventListener('load', loaded, { once: true });
|
|
@@ -44,6 +44,7 @@ function goalEvent(payload) {
|
|
|
44
44
|
}
|
|
45
45
|
function automationEvent(payload) {
|
|
46
46
|
if (!payload || typeof payload !== "object") return null;
|
|
47
|
+
if (payload.silent === true) return null;
|
|
47
48
|
const run = payload.run;
|
|
48
49
|
if (!run || run.status !== "failed" && run.status !== "timeout" && run.status !== "succeeded") return null;
|
|
49
50
|
const type = run.status === "succeeded" ? "automation.completed" : "automation.failed";
|
package/dist/src/pets/factory.js
CHANGED
|
@@ -75,23 +75,26 @@ function esc(value) {
|
|
|
75
75
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
76
76
|
}
|
|
77
77
|
function desktopPetActionFrameCount(action) {
|
|
78
|
-
if (action === "idle") return 6;
|
|
79
|
-
if (action === "success" || action === "
|
|
78
|
+
if (action === "idle" || action === "sleep") return 6;
|
|
79
|
+
if (action === "success" || action === "concern") return 8;
|
|
80
80
|
return 10;
|
|
81
81
|
}
|
|
82
82
|
function desktopPetActionFps(action) {
|
|
83
|
-
if (action === "idle") return 6;
|
|
84
|
-
if (action === "success" || action === "
|
|
83
|
+
if (action === "idle" || action === "sleep") return 6;
|
|
84
|
+
if (action === "success" || action === "concern") return 12;
|
|
85
85
|
return 10;
|
|
86
86
|
}
|
|
87
87
|
function desktopPetActionLoops(action) {
|
|
88
|
-
return action
|
|
88
|
+
return action === "idle" || action === "sleep" || action === "research" || action === "read" || action === "create" || action === "execute" || action === "wait";
|
|
89
89
|
}
|
|
90
90
|
function renderAccessory(action, frame, style) {
|
|
91
91
|
const t = frame / Math.max(1, desktopPetActionFrameCount(action) - 1);
|
|
92
92
|
const bob = Math.sin(t * Math.PI * 2) * 2;
|
|
93
93
|
const { palette } = style;
|
|
94
|
-
if (action === "
|
|
94
|
+
if (action === "sleep") return `<g fill="${palette.screen}" font-family="Arial, sans-serif" font-weight="800"><text x="70" y="28" font-size="13" opacity="${frame % 2 ? .45 : .9}">z</text><text x="80" y="17" font-size="10" opacity="${frame % 2 ? .9 : .45}">z</text></g>`;
|
|
95
|
+
if (action === "greet" || action === "wake" || action === "pet") return `<path d="M76 23c-7-9-19 1-7 13l7 7 7-7c12-12 0-22-7-13z" fill="${palette.cheek}" stroke="#101827" stroke-width="2"/>`;
|
|
96
|
+
if (action === "wait") return `<g fill="${palette.screen}" transform="translate(62 18)"><circle cx="0" cy="0" r="3" opacity="${frame % 3 === 0 ? 1 : .35}"/><circle cx="10" cy="0" r="3" opacity="${frame % 3 === 1 ? 1 : .35}"/><circle cx="20" cy="0" r="3" opacity="${frame % 3 === 2 ? 1 : .35}"/></g>`;
|
|
97
|
+
if (action === "create") {
|
|
95
98
|
const glow = frame % 2 === 0 ? 1 : .45;
|
|
96
99
|
return `
|
|
97
100
|
<g transform="translate(55 ${59 + bob})">
|
|
@@ -103,7 +106,7 @@ function renderAccessory(action, frame, style) {
|
|
|
103
106
|
<rect x="29" y="28" width="5" height="2" rx="1" fill="#e0f2fe" opacity="${glow}"/>
|
|
104
107
|
</g>`;
|
|
105
108
|
}
|
|
106
|
-
if (action === "
|
|
109
|
+
if (action === "prepare") {
|
|
107
110
|
const open = frame % 2 === 0 ? -11 : -3;
|
|
108
111
|
return `
|
|
109
112
|
<g transform="translate(6 ${61 + bob})">
|
|
@@ -114,38 +117,30 @@ function renderAccessory(action, frame, style) {
|
|
|
114
117
|
<rect x="29" y="${8 + open / 2}" width="5" height="17" rx="2" fill="#e5e7eb" stroke="#101827" stroke-width="2" transform="rotate(25 31 24)"/>
|
|
115
118
|
</g>`;
|
|
116
119
|
}
|
|
117
|
-
if (action === "
|
|
120
|
+
if (action === "research") return `
|
|
118
121
|
<g transform="translate(58 ${58 + bob}) rotate(${frame % 2 === 0 ? -7 : 4} 18 18)">
|
|
119
122
|
<circle cx="15" cy="14" r="11" fill="#e0f2fe" stroke="${palette.accent}" stroke-width="5"/>
|
|
120
123
|
<rect x="24" y="24" width="17" height="7" rx="3" fill="${palette.accent}" stroke="#101827" stroke-width="3" transform="rotate(43 24 24)"/>
|
|
121
124
|
</g>`;
|
|
122
|
-
if (action === "
|
|
125
|
+
if (action === "read") return `
|
|
123
126
|
<g transform="translate(59 ${55 + bob}) rotate(${frame % 2 === 0 ? -5 : 5} 16 20)">
|
|
124
127
|
<path d="M5 1h22l9 9v31H5z" fill="#eff6ff" stroke="#101827" stroke-width="3" stroke-linejoin="round"/>
|
|
125
128
|
<path d="M27 1v10h9" fill="${palette.accent}" stroke="#101827" stroke-width="3" stroke-linejoin="round"/>
|
|
126
129
|
<rect x="12" y="20" width="16" height="3" rx="1.5" fill="${palette.body}"/>
|
|
127
130
|
<rect x="12" y="28" width="12" height="3" rx="1.5" fill="${palette.body}"/>
|
|
128
131
|
</g>`;
|
|
129
|
-
if (action === "
|
|
132
|
+
if (action === "execute") return `
|
|
130
133
|
<g transform="translate(55 ${61 + bob})">
|
|
131
134
|
<rect x="0" y="0" width="39" height="29" rx="7" fill="#111827" stroke="#101827" stroke-width="3"/>
|
|
132
135
|
<path d="M10 10l6 5-6 5" fill="none" stroke="#86efac" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" opacity="${frame % 2 === 0 ? 1 : .4}"/>
|
|
133
136
|
<path d="M20 20h9" stroke="#86efac" stroke-width="3" stroke-linecap="round"/>
|
|
134
|
-
</g>`;
|
|
135
|
-
if (action === "browser") return `
|
|
136
|
-
<g transform="translate(55 ${58 + bob})">
|
|
137
|
-
<rect x="0" y="0" width="40" height="31" rx="7" fill="#e0f2fe" stroke="#101827" stroke-width="3"/>
|
|
138
|
-
<path d="M1 11h38" stroke="${palette.accent}" stroke-width="8"/>
|
|
139
|
-
<circle cx="10" cy="10" r="2" fill="#101827"/>
|
|
140
|
-
<circle cx="17" cy="10" r="2" fill="#101827"/>
|
|
141
|
-
<rect x="11" y="21" width="20" height="4" rx="2" fill="${palette.body}"/>
|
|
142
137
|
</g>`;
|
|
143
138
|
if (action === "success") return `
|
|
144
139
|
<g fill="#facc15" transform="translate(61 ${12 + bob})">
|
|
145
140
|
<path d="M12 0l3 8 8 3-8 3-3 8-3-8-8-3 8-3z"/>
|
|
146
141
|
<path d="M-12 18l2 5 5 2-5 2-2 5-2-5-5-2 5-2z" opacity="0.78"/>
|
|
147
142
|
</g>`;
|
|
148
|
-
if (action === "
|
|
143
|
+
if (action === "concern") return `
|
|
149
144
|
<g transform="translate(61 ${56 + bob})">
|
|
150
145
|
<path d="M18 0l18 32H0z" fill="#facc15" stroke="#101827" stroke-width="3" stroke-linejoin="round"/>
|
|
151
146
|
<text x="18" y="26" text-anchor="middle" font-family="Arial, sans-serif" font-size="22" font-weight="900" fill="#101827">!</text>
|
|
@@ -170,14 +165,14 @@ function renderHead(style) {
|
|
|
170
165
|
}
|
|
171
166
|
function renderFrame(style, action, frame) {
|
|
172
167
|
const t = frame / Math.max(1, desktopPetActionFrameCount(action) - 1);
|
|
173
|
-
const bob = Math.sin(t * Math.PI * 2) * (action === "idle" ? 2.5 : 1.5);
|
|
174
|
-
const tilt = action === "
|
|
168
|
+
const bob = action === "sleep" ? 7 : action === "pickedUp" ? -6 : action === "released" ? 4 : Math.sin(t * Math.PI * 2) * (action === "idle" ? 2.5 : 1.5);
|
|
169
|
+
const tilt = action === "prepare" ? Math.sin(t * Math.PI * 2) * 2.5 : action === "pet" ? Math.sin(t * Math.PI) * 5 : 0;
|
|
175
170
|
const { palette } = style;
|
|
176
|
-
const face = action === "
|
|
177
|
-
const eye = action === "
|
|
178
|
-
const mouth = action === "success" ? "M39 45q5 5 10 0" : "M42 46h9";
|
|
179
|
-
const leftArm = action === "
|
|
180
|
-
const rightArm = action === "
|
|
171
|
+
const face = action === "concern" ? "#3f0f18" : palette.face;
|
|
172
|
+
const eye = action === "concern" ? "#fca5a5" : palette.screen;
|
|
173
|
+
const mouth = action === "success" || action === "greet" || action === "pet" ? "M39 45q5 5 10 0" : "M42 46h9";
|
|
174
|
+
const leftArm = action === "create" || action === "execute" ? 23 + frame % 2 * 13 : action === "prepare" || action === "read" ? 39 + frame % 2 * 10 : action === "pickedUp" ? 58 : 9;
|
|
175
|
+
const rightArm = action === "create" || action === "execute" ? -23 - frame % 2 * 13 : action === "prepare" || action === "read" ? -39 - frame % 2 * 10 : action === "greet" || action === "wake" ? -48 - frame % 2 * 12 : action === "pickedUp" ? -58 : -9;
|
|
181
176
|
return `
|
|
182
177
|
<g transform="translate(0 ${bob}) rotate(${tilt} 48 76)">
|
|
183
178
|
${renderHead(style)}
|
|
@@ -287,6 +282,7 @@ async function createDesktopPetPackage(input) {
|
|
|
287
282
|
await mkdir(targetRoot, { recursive: true });
|
|
288
283
|
const { id, dir } = await resolvePackageDir(targetRoot, (input.id?.trim() ? normalizePackageId(input.id) : null) || slugify(input.name?.trim() || titleFromPrompt(prompt), `pet-${hashString(prompt).toString(36)}`), input.overwrite === true);
|
|
289
284
|
const existingManifest = input.overwrite === true ? await readExistingManifest(dir) : null;
|
|
285
|
+
if (existingManifest && existingManifest.schemaVersion !== 2) throw new Error(`Existing pet package must use schemaVersion 2`);
|
|
290
286
|
const existingName = typeof existingManifest?.name === "string" ? existingManifest.name.trim() : "";
|
|
291
287
|
const persona = normalizePersona(input.persona ?? existingManifest?.persona);
|
|
292
288
|
const baseName = clampText(input.name?.trim() || existingName || titleFromPrompt(prompt), 36);
|
|
@@ -296,6 +292,7 @@ async function createDesktopPetPackage(input) {
|
|
|
296
292
|
const thumbnailPath = join(tempDir, "thumbnail.svg");
|
|
297
293
|
const manifestPath = join(tempDir, "manifest.json");
|
|
298
294
|
const manifest = {
|
|
295
|
+
schemaVersion: 2,
|
|
299
296
|
id,
|
|
300
297
|
name: baseName,
|
|
301
298
|
description,
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const DESKTOP_PET_SCHEMA_VERSION: 2;
|
|
2
|
+
export declare const DESKTOP_PET_ACTIONS: readonly ["idle", "sleep", "wake", "greet", "prepare", "research", "read", "create", "execute", "wait", "success", "concern", "pet", "pickedUp", "released"];
|
|
2
3
|
export type DesktopPetPackageAction = (typeof DESKTOP_PET_ACTIONS)[number];
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
//#region src/pets/manifest.ts
|
|
2
|
+
const DESKTOP_PET_SCHEMA_VERSION = 2;
|
|
2
3
|
const DESKTOP_PET_ACTIONS = [
|
|
3
4
|
"idle",
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
5
|
+
"sleep",
|
|
6
|
+
"wake",
|
|
7
|
+
"greet",
|
|
8
|
+
"prepare",
|
|
9
|
+
"research",
|
|
10
|
+
"read",
|
|
11
|
+
"create",
|
|
12
|
+
"execute",
|
|
13
|
+
"wait",
|
|
10
14
|
"success",
|
|
11
|
-
"
|
|
15
|
+
"concern",
|
|
16
|
+
"pet",
|
|
17
|
+
"pickedUp",
|
|
18
|
+
"released"
|
|
12
19
|
];
|
|
13
20
|
//#endregion
|
|
14
|
-
export { DESKTOP_PET_ACTIONS };
|
|
21
|
+
export { DESKTOP_PET_ACTIONS, DESKTOP_PET_SCHEMA_VERSION };
|
|
@@ -135,6 +135,7 @@ async function validateDesktopPetPackage(dir) {
|
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
137
|
if (typeof manifest.id !== "string" || !manifest.id.trim()) details.push("id is required");
|
|
138
|
+
if (manifest.schemaVersion !== 2) details.push(`schemaVersion must be 2`);
|
|
138
139
|
if (typeof manifest.name !== "string" || !manifest.name.trim()) details.push("name is required");
|
|
139
140
|
if (typeof manifest.description !== "string" || !manifest.description.trim()) details.push("description is required");
|
|
140
141
|
if (positiveNumber(manifest.canvasWidth) === null || positiveNumber(manifest.canvasHeight) === null) details.push("canvasWidth and canvasHeight must be positive numbers");
|
|
@@ -143,9 +144,13 @@ async function validateDesktopPetPackage(dir) {
|
|
|
143
144
|
const rawAnimations = manifest.animations && typeof manifest.animations === "object" ? manifest.animations : null;
|
|
144
145
|
if (!rawAnimations) details.push("animations is required");
|
|
145
146
|
const animations = [];
|
|
146
|
-
if (rawAnimations)
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
147
|
+
if (rawAnimations) {
|
|
148
|
+
const supportedActions = new Set(DESKTOP_PET_ACTIONS);
|
|
149
|
+
for (const action of Object.keys(rawAnimations)) if (!supportedActions.has(action)) details.push(`${action}: unsupported animation action`);
|
|
150
|
+
for (const action of DESKTOP_PET_ACTIONS) {
|
|
151
|
+
const animation = parseAnimation(action, rawAnimations[action], details);
|
|
152
|
+
if (animation) animations.push(animation);
|
|
153
|
+
}
|
|
149
154
|
}
|
|
150
155
|
for (const animation of animations) {
|
|
151
156
|
const sourcePath = safeResolve(dir, animation.src);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { KnowledgeSourceItem } from '../knowledge/types.js';
|
|
2
|
+
import type { FocusCalendarSignal, FocusView } from './types.js';
|
|
3
|
+
export declare function buildFocusCalendarSignals(items: KnowledgeSourceItem[], focuses: FocusView[], nowMs?: number): FocusCalendarSignal[];
|
|
4
|
+
export declare function listFocusCalendarSignals(focuses: FocusView[], nowMs?: number): FocusCalendarSignal[];
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { listKnowledgeSourceItems } from "../storage/sqlite/knowledge-repository.js";
|
|
2
|
+
//#region src/proactive/calendar-signals.ts
|
|
3
|
+
const LOOKAHEAD_MS = 7 * 864e5;
|
|
4
|
+
const FIELD_LIMIT = 8;
|
|
5
|
+
const TITLE_KEYS = new Set([
|
|
6
|
+
"summary",
|
|
7
|
+
"title",
|
|
8
|
+
"name",
|
|
9
|
+
"subject"
|
|
10
|
+
]);
|
|
11
|
+
const DESCRIPTION_KEYS = new Set([
|
|
12
|
+
"description",
|
|
13
|
+
"notes",
|
|
14
|
+
"body",
|
|
15
|
+
"agenda"
|
|
16
|
+
]);
|
|
17
|
+
const START_KEYS = new Set([
|
|
18
|
+
"start",
|
|
19
|
+
"starttime",
|
|
20
|
+
"start_time",
|
|
21
|
+
"startat",
|
|
22
|
+
"start_at",
|
|
23
|
+
"datetime"
|
|
24
|
+
]);
|
|
25
|
+
const END_KEYS = new Set([
|
|
26
|
+
"end",
|
|
27
|
+
"endtime",
|
|
28
|
+
"end_time",
|
|
29
|
+
"endat",
|
|
30
|
+
"end_at"
|
|
31
|
+
]);
|
|
32
|
+
const STOP_WORDS = new Set([
|
|
33
|
+
"about",
|
|
34
|
+
"after",
|
|
35
|
+
"before",
|
|
36
|
+
"from",
|
|
37
|
+
"have",
|
|
38
|
+
"meeting",
|
|
39
|
+
"project",
|
|
40
|
+
"review",
|
|
41
|
+
"the",
|
|
42
|
+
"this",
|
|
43
|
+
"with"
|
|
44
|
+
]);
|
|
45
|
+
function parsePayload(item) {
|
|
46
|
+
if (!item.normalizedText) return null;
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(item.normalizedText);
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function findStrings(value, keys, depth = 0) {
|
|
54
|
+
if (depth > 6 || value == null) return [];
|
|
55
|
+
if (Array.isArray(value)) return value.flatMap((item) => findStrings(item, keys, depth + 1)).slice(0, FIELD_LIMIT);
|
|
56
|
+
if (typeof value !== "object") return [];
|
|
57
|
+
const record = value;
|
|
58
|
+
const direct = Object.entries(record).flatMap(([key, nested]) => {
|
|
59
|
+
if (!keys.has(key.toLowerCase().replaceAll("-", "_"))) return [];
|
|
60
|
+
if (typeof nested === "string") return [nested];
|
|
61
|
+
return findStrings(nested, new Set([
|
|
62
|
+
"datetime",
|
|
63
|
+
"date",
|
|
64
|
+
"value"
|
|
65
|
+
]), depth + 1);
|
|
66
|
+
});
|
|
67
|
+
if (direct.length > 0) return direct.slice(0, FIELD_LIMIT);
|
|
68
|
+
return Object.values(record).flatMap((nested) => findStrings(nested, keys, depth + 1)).slice(0, FIELD_LIMIT);
|
|
69
|
+
}
|
|
70
|
+
function firstDate(payload, keys) {
|
|
71
|
+
for (const value of findStrings(payload, keys)) {
|
|
72
|
+
const parsed = Date.parse(value);
|
|
73
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function terms(value) {
|
|
77
|
+
const normalized = value.toLocaleLowerCase();
|
|
78
|
+
const result = new Set(normalized.match(/[a-z0-9][a-z0-9_-]{2,}/g)?.filter((word) => !STOP_WORDS.has(word)) ?? []);
|
|
79
|
+
for (const sequence of normalized.match(/[\p{Script=Han}]{2,}/gu) ?? []) for (let index = 0; index < sequence.length - 1; index += 1) result.add(sequence.slice(index, index + 2));
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
function relatedFocus(focuses, text) {
|
|
83
|
+
const eventTerms = terms(text);
|
|
84
|
+
if (eventTerms.size === 0) return void 0;
|
|
85
|
+
return focuses.map((focus) => ({
|
|
86
|
+
focus,
|
|
87
|
+
score: [...terms(`${focus.title} ${focus.summary}`)].filter((term) => eventTerms.has(term)).length
|
|
88
|
+
})).filter((candidate) => candidate.score >= 2).toSorted((left, right) => right.score - left.score || right.focus.focusScore - left.focus.focusScore)[0]?.focus;
|
|
89
|
+
}
|
|
90
|
+
function buildFocusCalendarSignals(items, focuses, nowMs = Date.now()) {
|
|
91
|
+
const end = nowMs + LOOKAHEAD_MS;
|
|
92
|
+
const signals = items.flatMap((item) => {
|
|
93
|
+
if (item.itemType !== "calendar_event") return [];
|
|
94
|
+
const payload = parsePayload(item);
|
|
95
|
+
const title = findStrings(payload, TITLE_KEYS)[0]?.trim();
|
|
96
|
+
const startsAt = firstDate(payload, START_KEYS);
|
|
97
|
+
if (!title || startsAt == null || startsAt < nowMs || startsAt > end) return [];
|
|
98
|
+
const focus = relatedFocus(focuses, `${title} ${findStrings(payload, DESCRIPTION_KEYS).join(" ")}`);
|
|
99
|
+
if (!focus) return [];
|
|
100
|
+
const endsAt = firstDate(payload, END_KEYS);
|
|
101
|
+
return [{
|
|
102
|
+
id: item.id,
|
|
103
|
+
focusId: focus.id,
|
|
104
|
+
focusTitle: focus.title,
|
|
105
|
+
title,
|
|
106
|
+
startsAt,
|
|
107
|
+
...endsAt != null ? { endsAt } : {},
|
|
108
|
+
sourceInstanceId: item.sourceInstanceId
|
|
109
|
+
}];
|
|
110
|
+
}).toSorted((left, right) => left.startsAt - right.startsAt);
|
|
111
|
+
return signals.filter((signal, index) => signals.findIndex((candidate) => candidate.focusId === signal.focusId && candidate.startsAt === signal.startsAt && candidate.title.toLocaleLowerCase() === signal.title.toLocaleLowerCase()) === index).slice(0, 20);
|
|
112
|
+
}
|
|
113
|
+
function listFocusCalendarSignals(focuses, nowMs = Date.now()) {
|
|
114
|
+
return buildFocusCalendarSignals(listKnowledgeSourceItems({
|
|
115
|
+
itemType: "calendar_event",
|
|
116
|
+
limit: 500
|
|
117
|
+
}), focuses, nowMs);
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
export { buildFocusCalendarSignals, listFocusCalendarSignals };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Automation, AutomationRun } from '../automations/index.js';
|
|
2
|
+
import type { FocusView, FocusWatch, FocusWatchKind } from './types.js';
|
|
3
|
+
interface AutomationPort {
|
|
4
|
+
create(input: {
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
projectId?: string;
|
|
8
|
+
trigger: {
|
|
9
|
+
kind: 'schedule';
|
|
10
|
+
schedule: {
|
|
11
|
+
kind: 'interval';
|
|
12
|
+
everyMs: number;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
action: {
|
|
16
|
+
kind: 'agent';
|
|
17
|
+
instruction: string;
|
|
18
|
+
timeoutSeconds: number;
|
|
19
|
+
};
|
|
20
|
+
safety: {
|
|
21
|
+
mode: 'suggest_only';
|
|
22
|
+
};
|
|
23
|
+
afterRun: {
|
|
24
|
+
kind: 'none';
|
|
25
|
+
};
|
|
26
|
+
reliability: {
|
|
27
|
+
timeoutSeconds: number;
|
|
28
|
+
disableAfterConsecutiveFailures: number;
|
|
29
|
+
};
|
|
30
|
+
}): Promise<Automation>;
|
|
31
|
+
pause(id: string): Promise<Automation | null>;
|
|
32
|
+
resume(id: string): Promise<Automation | null>;
|
|
33
|
+
update(id: string, patch: {
|
|
34
|
+
action: {
|
|
35
|
+
kind: 'agent';
|
|
36
|
+
instruction: string;
|
|
37
|
+
timeoutSeconds: number;
|
|
38
|
+
};
|
|
39
|
+
}): Promise<Automation | null>;
|
|
40
|
+
runNow(id: string): Promise<AutomationRun>;
|
|
41
|
+
}
|
|
42
|
+
export declare class FocusService {
|
|
43
|
+
private readonly automations;
|
|
44
|
+
private readonly goals;
|
|
45
|
+
constructor(automations: AutomationPort);
|
|
46
|
+
list(options?: {
|
|
47
|
+
includeUnreviewed?: boolean;
|
|
48
|
+
}): FocusView[];
|
|
49
|
+
confirm(id: string): FocusView | null;
|
|
50
|
+
activateTrial(input: {
|
|
51
|
+
threadId: string;
|
|
52
|
+
kind?: FocusWatchKind;
|
|
53
|
+
eventContext?: string;
|
|
54
|
+
}): Promise<{
|
|
55
|
+
focus: FocusView;
|
|
56
|
+
watch: FocusWatch;
|
|
57
|
+
}>;
|
|
58
|
+
pauseWatch(id: string): Promise<FocusWatch | null>;
|
|
59
|
+
reconcileExpiredTrials(nowMs?: number): Promise<number>;
|
|
60
|
+
recordInsightFeedback(watchId: string, useful: boolean): Promise<FocusWatch | null>;
|
|
61
|
+
}
|
|
62
|
+
export {};
|