@xopcai/xopc 0.0.125 → 0.0.127
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/README.md +56 -102
- package/README.zh-CN.md +51 -98
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/feishu/src/schema/config-schema.d.ts +6 -6
- package/dist/extensions/feishu/src/workflow-progress.js +1 -1
- package/dist/extensions/telegram/src/adapters/streaming-adapter.js +1 -1
- package/dist/extensions/telegram/src/config-schema.d.ts +8 -8
- package/dist/extensions/telegram/src/inbound-processor.js +1 -1
- package/dist/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/telegram/src/workflow-progress.js +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/api/api.js +1 -1
- package/dist/extensions/weixin/src/config-schema.d.ts +3 -3
- package/dist/extensions/weixin/src/plugin.js +1 -1
- package/dist/extensions/weixin/src/workflow-progress.js +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BKDKICZV.js → agent-avatar-dicebear-CgGgO4OV.js} +1 -1
- package/dist/gateway/static/root/assets/agents-k59l7NMb.js +2 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-DI4BqHKW.js → app-management-settings-panel-DeQsTsyy.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-6_ie3NQ4.js → appearance-settings-BnHQW7Hv.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-BG9b4abG.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-kUHEJ1HK.js → archive-plugin-5NXT7Zqv.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-Dm-9prpU.js → attachment-load-DghwhYe0.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-OTxQTPnC.js → automation-suggestion-card-DYmESgwl.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-De8mVutL.js +4 -0
- package/dist/gateway/static/root/assets/{binary-plugins-DllA_jQ0.js → binary-plugins-DSbPKdWX.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-Cm6CWNj1.js → block-editor-CoZ-_np-.js} +28 -28
- package/dist/gateway/static/root/assets/browser-settings-page-C_ZRa4LF.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-VJ89b6I2.js → capability-presets-api-hny224j-.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-settings-panel-BaPj8wC2.js → capability-presets-settings-panel-Drc0qLsw.js} +1 -1
- package/dist/gateway/static/root/assets/{channel-recipient-api-B5PwBEUx.js → channel-recipient-api-DAlOHxKJ.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-CA20zBV9.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-qMyMB5te.js +2 -0
- package/dist/gateway/static/root/assets/desktop-pet-XG6Vb_fZ.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-settings-npJ0Oh5p.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-sprite-DqZ6fbIj.js +1 -0
- package/dist/gateway/static/root/assets/directory-picker-path-field-BR2ItBuf.js +1 -0
- package/dist/gateway/static/root/assets/dreaming-settings-CkUZ997f.js +3 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-BaM0FpW7.js → extension-debug-page-Tl0f1rNv.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DuZWVOGA.js → extension-page-Ck7QuwE3.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DarVtbmJ.js → extension-settings-page-3RMhQtZ3.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-NFKfkqrd.js → fetch-pg2ErdAk.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-CFEm9rgy.js → gateway-config-api-DbG97ia2.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-B8cyqKk-.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-Dtwh_yGg.js → gateway-startup-retry-Yh8h3hkj.js} +1 -1
- package/dist/gateway/static/root/assets/{goal-create-dialog-BKP7ylcG.js → goal-create-dialog-DmeW7Zz3.js} +1 -1
- package/dist/gateway/static/root/assets/goal-detail-page-DrTyGbVD.js +1 -0
- package/dist/gateway/static/root/assets/{goals-config-api-DTWZNA_Y.js → goals-config-api-CxCWGIBB.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-DV1bJhIh.js +2 -0
- package/dist/gateway/static/root/assets/{goals-settings-Bhct0ZT-.js → goals-settings-9DrfOkP5.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-C-UxSU4a.js → heartbeat-settings-KaDpZrv0.js} +1 -1
- package/dist/gateway/static/root/assets/index-C2F0Clvm.js +256 -0
- package/dist/gateway/static/root/assets/index-COQSX5d_.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-DCjy2y4d.js → keyboard-shortcuts-settings-CfQrt_Rb.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-C9dpiNTF.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-split-BaqxP-GQ.js → markdown-split-BwiMdCf_.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-DQ_EXloK.js → media-plugins-DhoizyZv.js} +1 -1
- package/dist/gateway/static/root/assets/messages-DmnbiNxU.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-zdCdmBmH.js +2 -0
- package/dist/gateway/static/root/assets/notes-page-MpGeFfPt.js +1 -0
- package/dist/gateway/static/root/assets/notes-workbench-DYCM3yd2.js +2 -0
- package/dist/gateway/static/root/assets/{page-tabs-EIZhLppK.js → page-tabs-DDC2t4Z8.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-r_uB4Epr.js → preview-open-alternatives-BTmk-XDM.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-Cu0ElRnS.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-DakNaUjZ.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-DYKjt7L9.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-B132Uf-2.js → save-bar-controls-DqU-yKIh.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-B5qshALm.js → schedule-field-CUttkoSB.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-CvLmkdS9.js → schema-form-CVSQA9nI.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-CcGHDsz1.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-CuFdeKNu.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-pRlvqsAf.js → settings-collapsible-section-CK7ogS-P.js} +1 -1
- package/dist/gateway/static/root/assets/settings-form-section-Bugp38Fa.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-0AJoUvxi.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-layout-DBRWwdhh.js +1 -0
- package/dist/gateway/static/root/assets/setup-status-panel-D6a_hEk7.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-v6-IOp8t.js → share-preview-page-BtsuETKF.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-CcEFT7S9.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-BcN_RuTD.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-BbRZZZA5.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-HUU2FFjM.js → system-settings-panel-DJ6YuLqR.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-AH160QHh.js → typed-models-lib-B0T7N8Km.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-BXzDmjCQ.js → use-channel-catalog-C6vrxdzZ.js} +1 -1
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-DxwnQvQ3.js → user-profile-settings-panel-DZ0nKMgj.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-i5POEtqd.js → utils-CehXcWAY.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-2QKflCx8.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-DfJnuWq1.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-ClVVMgN-.js → workflow-run-setup-panel-c4jeaohQ.js} +2 -2
- package/dist/gateway/static/root/assets/{workflow-start-dialog-Dv4ZA1T0.js → workflow-start-dialog-ENRHjoqa.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-BJwwFTm_.js +18 -0
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/package.js +1 -1
- package/dist/src/activity/context.d.ts +11 -0
- package/dist/src/activity/context.js +36 -0
- package/dist/src/activity/context.js.map +1 -0
- package/dist/src/activity/emitter.d.ts +6 -0
- package/dist/src/activity/emitter.js +43 -0
- package/dist/src/activity/emitter.js.map +1 -0
- package/dist/src/activity/index.d.ts +3 -0
- package/dist/src/activity/index.js +3 -0
- package/dist/src/activity/service.d.ts +12 -0
- package/dist/src/activity/service.js +59 -0
- package/dist/src/activity/service.js.map +1 -0
- package/dist/src/activity/types.d.ts +109 -0
- package/dist/src/activity/types.js +1 -0
- package/dist/src/agent/agent-instance-gateway.d.ts +5 -0
- package/dist/src/agent/agent-manager.d.ts +21 -0
- package/dist/src/agent/agent-manager.js +118 -6
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/capabilities/definitions.d.ts +153 -0
- package/dist/src/agent/capabilities/definitions.js +156 -0
- package/dist/src/agent/capabilities/definitions.js.map +1 -0
- package/dist/src/agent/capabilities/index.d.ts +2 -0
- package/dist/src/agent/capabilities/index.js +3 -0
- package/dist/src/agent/capabilities/registry.d.ts +10 -0
- package/dist/src/agent/capabilities/registry.js +51 -0
- package/dist/src/agent/capabilities/registry.js.map +1 -0
- package/dist/src/agent/context/project-context.js +2 -2
- package/dist/src/agent/context/workspace-seed.js +2 -2
- package/dist/src/agent/embedded/run-for-session.js +1 -1
- package/dist/src/agent/goals/post-turn.js +2 -2
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
- package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
- package/dist/src/agent/mcp/mcp-transport.js +1 -1
- package/dist/src/agent/memory/plugin-discovery.js +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js +1 -1
- package/dist/src/agent/service/process-direct-one-shot.js +5 -3
- package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.js +4 -3
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service.d.ts +2 -0
- package/dist/src/agent/service.js +9 -3
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/service.types.d.ts +7 -0
- package/dist/src/agent/session/session-inspector.js +1 -1
- package/dist/src/agent/skills/hub-pull.js +1 -1
- package/dist/src/agent/skills/parse-skill-metadata.js +9 -1
- package/dist/src/agent/skills/parse-skill-metadata.js.map +1 -1
- package/dist/src/agent/skills/skill-manager.d.ts +4 -0
- package/dist/src/agent/skills/skill-manager.js +17 -0
- package/dist/src/agent/skills/skill-manager.js.map +1 -1
- package/dist/src/agent/skills/types.d.ts +5 -0
- package/dist/src/agent/tool-manuals/index.js +15 -6
- package/dist/src/agent/tool-manuals/index.js.map +1 -1
- package/dist/src/agent/tool-manuals/xopc-use.d.ts +1 -0
- package/dist/src/agent/tool-manuals/xopc-use.js +204 -0
- package/dist/src/agent/tool-manuals/xopc-use.js.map +1 -0
- package/dist/src/agent/tools/desktop-pet-tool.d.ts +24 -0
- package/dist/src/agent/tools/desktop-pet-tool.js +90 -0
- package/dist/src/agent/tools/desktop-pet-tool.js.map +1 -0
- package/dist/src/agent/tools/factory.d.ts +9 -0
- package/dist/src/agent/tools/factory.js +26 -0
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/goal-tool.js +2 -2
- package/dist/src/agent/tools/index.d.ts +2 -0
- package/dist/src/agent/tools/index.js +3 -1
- package/dist/src/agent/tools/workflow-tool.js +1 -1
- package/dist/src/agent/tools/xopc-use-tool.d.ts +34 -0
- package/dist/src/agent/tools/xopc-use-tool.js +636 -0
- package/dist/src/agent/tools/xopc-use-tool.js.map +1 -0
- package/dist/src/agent-manifest/schema.d.ts +27 -27
- package/dist/src/automations/api/routes.js +1 -1
- package/dist/src/automations/domain/validation.d.ts +55 -55
- package/dist/src/automations/draft/automation-draft-service.js +1 -1
- package/dist/src/automations/draft/automation-draft-validator.d.ts +25 -25
- package/dist/src/automations/service/action-executor.js +2 -2
- package/dist/src/browser/cdp-local-launcher.js +1 -1
- package/dist/src/browser/providers/browser-ext-install.js +3 -3
- package/dist/src/browser/providers/cloakbrowser.js +2 -2
- package/dist/src/browser/providers/playwright-doctor.js +1 -1
- package/dist/src/browser/stealth.js +1 -1
- package/dist/src/chat-commands/agent-edit.js +1 -1
- package/dist/src/chat-commands/builtins/config.js +1 -1
- package/dist/src/chat-commands/builtins/goal.js +2 -2
- package/dist/src/chat-commands/context.js +1 -1
- package/dist/src/cli/commands/agent.js +3 -3
- package/dist/src/cli/commands/doctor/checks/database-schema.js +2 -2
- package/dist/src/cli/commands/doctor/checks/session-integrity.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/extension-pack.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/goal.js +3 -3
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/cli/commands/profile.js +1 -1
- package/dist/src/cli/commands/project.js +4 -4
- package/dist/src/cli/commands/tunnel.js +1 -1
- package/dist/src/cli/commands/update.js +1 -1
- package/dist/src/cli/extension-cli-register.js +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/gateway-bind.js +1 -1
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/loader.js +1 -1
- package/dist/src/config/models-json.d.ts +14 -14
- package/dist/src/config/models-json.js +1 -1
- package/dist/src/config/schema.d.ts +60 -60
- package/dist/src/daemon/launchd.js +1 -1
- package/dist/src/daemon/systemd.js +1 -1
- package/dist/src/extensions/discover-extensions.js +1 -1
- package/dist/src/extensions/loader.js +2 -2
- package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
- package/dist/src/gateway/agent-builtin-tools.js +1 -0
- package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +14 -14
- package/dist/src/gateway/hono/lib/static-ui.js +1 -1
- package/dist/src/gateway/hono/middleware/log-context.js +8 -1
- package/dist/src/gateway/hono/middleware/log-context.js.map +1 -1
- package/dist/src/gateway/hono/routes/activity.d.ts +14 -0
- package/dist/src/gateway/hono/routes/activity.js +72 -0
- package/dist/src/gateway/hono/routes/activity.js.map +1 -0
- package/dist/src/gateway/hono/routes/commands-skills.js +7 -0
- package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
- package/dist/src/gateway/hono/routes/goals.js +1 -1
- package/dist/src/gateway/hono/routes/index.js +2 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +2 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/memory.js +2 -2
- package/dist/src/gateway/hono/routes/projects.js +93 -15
- package/dist/src/gateway/hono/routes/projects.js.map +1 -1
- package/dist/src/gateway/hono/routes/sessions.js +101 -2
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/hono/routes/workflows.js +2 -2
- package/dist/src/gateway/lock.js +1 -1
- package/dist/src/gateway/ports.js +1 -1
- package/dist/src/gateway/run-loop.js +1 -1
- package/dist/src/gateway/service/run-gateway-agent.js +6 -2
- package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
- package/dist/src/gateway/service/sessions-api.d.ts +10 -0
- package/dist/src/gateway/service/sessions-api.js +31 -0
- package/dist/src/gateway/service/sessions-api.js.map +1 -1
- package/dist/src/gateway/service.js +12 -9
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/goals/goal-store.js +1 -1
- package/dist/src/goals/goal-store.js.map +1 -1
- package/dist/src/goals/goal-workflow-judge.js +1 -1
- package/dist/src/infra/ports.js +1 -1
- package/dist/src/infra/ssh-tunnel.js +1 -1
- package/dist/src/infra/update-check.js +1 -1
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/migrations/runner.js +1 -1
- package/dist/src/notes/service.js +65 -6
- package/dist/src/notes/service.js.map +1 -1
- package/dist/src/notes/store.js +1 -1
- package/dist/src/pets/factory.d.ts +25 -0
- package/dist/src/pets/factory.js +327 -0
- package/dist/src/pets/factory.js.map +1 -0
- package/dist/src/pets/manifest.d.ts +2 -0
- package/dist/src/pets/manifest.js +16 -0
- package/dist/src/pets/manifest.js.map +1 -0
- package/dist/src/pets/validator.d.ts +12 -0
- package/dist/src/pets/validator.js +175 -0
- package/dist/src/pets/validator.js.map +1 -0
- package/dist/src/projects/project-service.d.ts +4 -1
- package/dist/src/projects/project-service.js +65 -2
- package/dist/src/projects/project-service.js.map +1 -1
- package/dist/src/projects/project-store.d.ts +3 -1
- package/dist/src/projects/project-store.js +56 -6
- package/dist/src/projects/project-store.js.map +1 -1
- package/dist/src/projects/types.d.ts +12 -0
- package/dist/src/projects/workspace.js +1 -1
- package/dist/src/routing/agent-session-key.js +1 -1
- package/dist/src/routing/resolve-route.js +1 -1
- package/dist/src/routing/resolve-tui-session-key.js +1 -1
- package/dist/src/session/client-history.d.ts +3 -0
- package/dist/src/session/client-history.js +24 -9
- package/dist/src/session/client-history.js.map +1 -1
- package/dist/src/session/init-session-turn.js +1 -1
- package/dist/src/session/resolve-session.js +1 -1
- package/dist/src/session/session-title.js +24 -4
- package/dist/src/session/session-title.js.map +1 -1
- package/dist/src/session/store.js +1 -1
- package/dist/src/session/types.d.ts +4 -0
- package/dist/src/session/types.js.map +1 -1
- package/dist/src/storage/sqlite/activity-repository.d.ts +8 -0
- package/dist/src/storage/sqlite/activity-repository.js +208 -0
- package/dist/src/storage/sqlite/activity-repository.js.map +1 -0
- package/dist/src/storage/sqlite/connection.js +1 -1
- package/dist/src/storage/sqlite/index.d.ts +1 -0
- package/dist/src/storage/sqlite/index.js +2 -1
- package/dist/src/storage/sqlite/migrations/024_activity_events.sql +62 -0
- package/dist/src/storage/sqlite/migrations/025_project_pinned_at.sql +5 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/row-mappers.js +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/storage/sqlite/schema.sql +63 -0
- package/dist/src/storage/sqlite/session-repository.js +11 -0
- package/dist/src/storage/sqlite/session-repository.js.map +1 -1
- package/dist/src/tui/backends/embedded-backend.d.ts +13 -0
- package/dist/src/tui/backends/embedded-backend.js +76 -7
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +13 -0
- package/dist/src/tui/backends/gateway-sse-backend.js +53 -0
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/chat-history.js +16 -3
- package/dist/src/tui/chat-history.js.map +1 -1
- package/dist/src/tui/clipboard-image.js +1 -1
- package/dist/src/tui/components/chat-log.d.ts +23 -3
- package/dist/src/tui/components/chat-log.js +68 -9
- package/dist/src/tui/components/chat-log.js.map +1 -1
- package/dist/src/tui/components/tui-bottom-bar.js +9 -5
- package/dist/src/tui/components/tui-bottom-bar.js.map +1 -1
- package/dist/src/tui/tui-backend.d.ts +16 -0
- package/dist/src/tui/tui-command-formatters.js +2 -2
- package/dist/src/tui/tui-commands.d.ts +9 -0
- package/dist/src/tui/tui-commands.js +95 -6
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/tui-picker-overlay.d.ts +1 -0
- package/dist/src/tui/tui-picker-overlay.js +36 -1
- package/dist/src/tui/tui-picker-overlay.js.map +1 -1
- package/dist/src/tui/tui-picker-services.d.ts +13 -0
- package/dist/src/tui/tui-run-state.js +11 -0
- package/dist/src/tui/tui-run-state.js.map +1 -1
- package/dist/src/tui/tui-run-status-format.d.ts +3 -0
- package/dist/src/tui/tui-run-status-format.js +33 -0
- package/dist/src/tui/tui-run-status-format.js.map +1 -0
- package/dist/src/tui/tui-session-actions.d.ts +5 -0
- package/dist/src/tui/tui-session-actions.js +24 -0
- package/dist/src/tui/tui-session-actions.js.map +1 -1
- package/dist/src/tui/tui-timeline-picker.d.ts +19 -0
- package/dist/src/tui/tui-timeline-picker.js +81 -0
- package/dist/src/tui/tui-timeline-picker.js.map +1 -0
- package/dist/src/tui/tui-timeline.d.ts +15 -0
- package/dist/src/tui/tui-timeline.js +65 -0
- package/dist/src/tui/tui-timeline.js.map +1 -0
- package/dist/src/tui/tui-types.d.ts +1 -0
- package/dist/src/tui/tui-types.js +1 -0
- package/dist/src/tui/tui-types.js.map +1 -1
- package/dist/src/tui/tui.js +31 -25
- package/dist/src/tui/tui.js.map +1 -1
- package/dist/src/utils/index.js +1 -1
- package/dist/src/utils/logger/index.js +1 -1
- package/dist/src/utils/logger/stats.d.ts +3 -3
- package/dist/src/utils/logger.js +1 -1
- package/dist/src/work-items/agent-context.d.ts +5 -1
- package/dist/src/work-items/agent-context.js +39 -3
- package/dist/src/work-items/agent-context.js.map +1 -1
- package/dist/src/work-items/work-item-service.d.ts +2 -0
- package/dist/src/work-items/work-item-service.js +97 -0
- package/dist/src/work-items/work-item-service.js.map +1 -1
- package/dist/src/workflows/draft/workflow-draft-service.js +1 -1
- package/dist/src/workflows/service/workflow-session-bridge.js +1 -1
- package/package.json +5 -5
- package/skills/creative/hatch-pet/SKILL.md +63 -0
- package/dist/gateway/static/root/assets/agents-BnTB7-c5.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-B6bG65SW.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-1lVbo0dS.js +0 -1
- package/dist/gateway/static/root/assets/browser-settings-page-CzDBlOK0.js +0 -8
- package/dist/gateway/static/root/assets/channels-settings-Df4m7p1k.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-BMTphmHS.js +0 -2
- package/dist/gateway/static/root/assets/directory-picker-path-field-DySMzOF6.js +0 -1
- package/dist/gateway/static/root/assets/dist-By6Rh2ee.js +0 -1
- package/dist/gateway/static/root/assets/dreaming-settings-D_AjSnV_.js +0 -3
- package/dist/gateway/static/root/assets/gateway-settings-BMk2S5td.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-C2cTxf71.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-CTmA3hWK.js +0 -2
- package/dist/gateway/static/root/assets/index-BNHDd2j1.css +0 -1
- package/dist/gateway/static/root/assets/index-BPXLPMuY.js +0 -193
- package/dist/gateway/static/root/assets/logs-page-CGc3_CQU.js +0 -2
- package/dist/gateway/static/root/assets/messages-CjuIJcZl.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-Drz4UQMb.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-CY1ylng1.js +0 -1
- package/dist/gateway/static/root/assets/notes-workbench-X7IaD--t.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-DE5adRBE.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-CuOb1k6F.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BOxNrUVA.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-CY0IQcGW.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-D4NBNVCB.js +0 -1
- package/dist/gateway/static/root/assets/settings-form-section-BBYUDLVd.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-DaO7zYZM.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-layout-D8g9PAek.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-Bks0CLLT.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-CEZsKm9r.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-oTkHbb9W.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-DigsbZ6K.js +0 -2
- package/dist/gateway/static/root/assets/voice-api-key-field-CEJklEgb.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-CD6FL_Rs.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-OLQiUpoW.js +0 -18
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-history.js","names":[],"sources":["../../../src/session/client-history.ts"],"sourcesContent":["import type { Message } from './types.js';\nimport type { TranscriptStoredRow } from './session-context-for-llm.js';\n\n/** Transcript row for TUI and HTTP clients (flattened from persisted session messages). */\nexport interface ClientHistoryMessage {\n id?: string;\n role: 'user' | 'assistant' | 'system';\n content: string;\n rawContent?: string | unknown[];\n timestamp?: number;\n kind?: 'message' | 'compaction' | 'context' | 'bash' | 'custom' | 'branch';\n tokensBefore?: number;\n tokensAfter?: number;\n bash?: {\n command: string;\n output?: string;\n exitCode?: number | null;\n signal?: string | null;\n excludeFromContext?: boolean;\n truncated?: boolean;\n fullOutputPath?: string;\n };\n custom?: {\n customType: string;\n details?: unknown;\n state?: boolean;\n display?: boolean;\n };\n branch?: {\n summary: string;\n fromId?: string;\n };\n toolCalls?: Array<{ name: string; args?: unknown; result?: string; isError?: boolean }>;\n}\n\nexport function flattenMessageContent(content: string | unknown[]): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block !== 'object' || block === null) continue;\n const b = block as Record<string, unknown>;\n if (b.type === 'text' && typeof b.text === 'string') {\n parts.push(b.text);\n }\n }\n return parts.join('');\n}\n\nfunction parseTimestamp(ts: string | undefined): number | undefined {\n if (!ts) return undefined;\n const ms = Date.parse(ts);\n return Number.isFinite(ms) ? ms : undefined;\n}\n\nfunction safeJsonParseArguments(raw: string): unknown {\n const t = raw.trim();\n if (!t) return undefined;\n try {\n return JSON.parse(t) as unknown;\n } catch {\n return raw;\n }\n}\n\nfunction collectToolResults(messages: Message[]): Map<string, { text: string; isError?: boolean }> {\n const map = new Map<string, { text: string; isError?: boolean }>();\n for (const m of messages) {\n if (m.role !== 'tool' && m.role !== 'toolResult') continue;\n const id = m.tool_call_id;\n if (!id || typeof id !== 'string') continue;\n map.set(id, {\n text: flattenMessageContent(m.content),\n isError: m.isError,\n });\n }\n return map;\n}\n\nfunction toolCallsWithResults(\n tool_calls: Message['tool_calls'],\n results: Map<string, { text: string; isError?: boolean }>,\n): ClientHistoryMessage['toolCalls'] | undefined {\n if (!tool_calls?.length) return undefined;\n const out: NonNullable<ClientHistoryMessage['toolCalls']> = [];\n for (const tc of tool_calls) {\n const res = results.get(tc.id);\n out.push({\n name: tc.function.name,\n args: safeJsonParseArguments(tc.function.arguments),\n result: res?.text,\n isError: res?.isError,\n });\n }\n return out.length ? out : undefined;\n}\n\n/**\n * Maps gateway/session API messages into a linear chat history for clients.\n * Tool / toolResult rows are merged into assistant `toolCalls` when ids match; otherwise skipped.\n */\nexport function messagesToClientHistory(\n messages: Message[],\n opts?: { limit?: number },\n): ClientHistoryMessage[] {\n const slice =\n opts?.limit !== undefined && messages.length > opts.limit\n ? messages.slice(-opts.limit)\n : messages;\n\n const results = collectToolResults(slice);\n const out: ClientHistoryMessage[] = [];\n\n for (const m of slice) {\n if (m.role === 'tool' || m.role === 'toolResult') {\n continue;\n }\n\n if (m.role === 'user' || m.role === 'system') {\n const text = flattenMessageContent(m.content);\n out.push({\n role: m.role,\n content: text,\n timestamp: parseTimestamp(m.timestamp),\n });\n continue;\n }\n\n if (m.role === 'assistant') {\n const text = flattenMessageContent(m.content);\n const toolCalls = toolCallsWithResults(m.tool_calls, results);\n out.push({\n role: 'assistant',\n content: text,\n timestamp: parseTimestamp(m.timestamp),\n toolCalls,\n });\n }\n }\n\n return out;\n}\n\ntype HistoryMessageRow = {\n role: 'system' | 'user' | 'assistant' | 'tool' | 'toolResult';\n content?: string | unknown[];\n timestamp?: string | number;\n tool_call_id?: string;\n toolCallId?: string;\n tool_calls?: Message['tool_calls'];\n toolCalls?: Message['tool_calls'];\n isError?: boolean;\n};\n\nfunction asHistoryMessageRow(row: TranscriptStoredRow): HistoryMessageRow | null {\n const role = (row as { role?: unknown }).role;\n if (\n typeof role !== 'string' ||\n !['system', 'user', 'assistant', 'tool', 'toolResult'].includes(role)\n ) {\n return null;\n }\n return row as unknown as HistoryMessageRow;\n}\n\nfunction parseTimestampValue(ts: string | number | undefined): number | undefined {\n if (typeof ts === 'number') return Number.isFinite(ts) ? ts : undefined;\n return parseTimestamp(ts);\n}\n\nfunction collectHistoryToolResults(messages: HistoryMessageRow[]): Map<string, { text: string; isError?: boolean }> {\n const map = new Map<string, { text: string; isError?: boolean }>();\n for (const m of messages) {\n if (m.role !== 'tool' && m.role !== 'toolResult') continue;\n const id = m.tool_call_id ?? m.toolCallId;\n if (!id || typeof id !== 'string') continue;\n map.set(id, {\n text: flattenMessageContent(m.content ?? ''),\n isError: m.isError,\n });\n }\n return map;\n}\n\nfunction isCompactionRow(row: TranscriptStoredRow): row is TranscriptStoredRow & {\n type: 'compaction';\n summary?: string;\n at?: string;\n tokensBefore?: number;\n tokensAfter?: number;\n} {\n return (row as { type?: unknown }).type === 'compaction';\n}\n\nfunction compactionSummaryRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'compactionSummary') return null;\n const summary = optionalString(r.summary) ?? '';\n return {\n role: 'system',\n kind: 'compaction',\n content: summary,\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n tokensBefore: typeof r.tokensBefore === 'number' ? r.tokensBefore : undefined,\n };\n}\n\nfunction contextRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n if ((row as { kind?: unknown }).kind !== 'context') return null;\n const text = (row as { text?: unknown }).text;\n if (typeof text !== 'string' || !text.trim()) return null;\n const createdAt = (row as { createdAt?: unknown }).createdAt;\n return {\n role: 'system',\n kind: 'context',\n content: text,\n timestamp: typeof createdAt === 'string' ? parseTimestamp(createdAt) : undefined,\n };\n}\n\nfunction optionalString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction bashRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'bashExecution') return null;\n\n const command = optionalString(r.command) ?? optionalString(r.content)?.trim();\n if (!command) return null;\n const output = Array.isArray(r.output) || typeof r.output === 'string'\n ? flattenMessageContent(r.output)\n : optionalString(r.output);\n const exitCode = typeof r.exitCode === 'number' ? r.exitCode : r.exitCode === null ? null : undefined;\n const signal = typeof r.signal === 'string' ? r.signal : r.signal === null ? null : undefined;\n const excludeFromContext =\n typeof r.excludeFromContext === 'boolean'\n ? r.excludeFromContext\n : typeof r.excludedFromContext === 'boolean'\n ? r.excludedFromContext\n : undefined;\n\n return {\n role: 'system',\n kind: 'bash',\n content: output || command,\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n bash: {\n command,\n output,\n exitCode,\n signal,\n excludeFromContext,\n truncated: typeof r.truncated === 'boolean' ? r.truncated : undefined,\n fullOutputPath: optionalString(r.fullOutputPath),\n },\n };\n}\n\nfunction customRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'custom' && r.type !== 'custom_message') return null;\n\n const customType = optionalString(r.customType)?.trim();\n if (!customType) return null;\n const content = Array.isArray(r.content) || typeof r.content === 'string'\n ? flattenMessageContent(r.content)\n : '';\n const timestamp = parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n );\n\n return {\n role: 'system',\n kind: 'custom',\n content,\n rawContent: Array.isArray(r.content) || typeof r.content === 'string' ? r.content : undefined,\n timestamp,\n custom: {\n customType,\n details: r.details,\n display: r.display === false ? false : true,\n },\n };\n}\n\nfunction customStateRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.type !== 'custom') return null;\n\n const customType = optionalString(r.customType)?.trim();\n if (!customType) return null;\n return {\n role: 'system',\n kind: 'custom',\n content: '',\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n custom: {\n customType,\n details: r.data,\n state: true,\n },\n };\n}\n\nfunction branchSummaryRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'branchSummary') return null;\n const summary = optionalString(r.summary)?.trim();\n if (!summary) return null;\n const fromId = optionalString(r.fromId);\n\n return {\n role: 'system',\n kind: 'branch',\n content: summary,\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n branch: {\n summary,\n fromId,\n },\n };\n}\n\n/**\n * Maps persisted transcript rows into linear chat history, preserving non-LLM\n * audit rows that the TUI can render as dedicated components.\n */\nexport function transcriptRowsToClientHistory(\n rows: TranscriptStoredRow[],\n opts?: { limit?: number },\n): ClientHistoryMessage[] {\n const startIndex =\n opts?.limit !== undefined && rows.length > opts.limit\n ? rows.length - opts.limit\n : 0;\n const slice =\n opts?.limit !== undefined && rows.length > opts.limit\n ? rows.slice(-opts.limit)\n : rows;\n const messages = slice\n .map(asHistoryMessageRow)\n .filter((row): row is HistoryMessageRow => row !== null);\n const results = collectHistoryToolResults(messages);\n const out: ClientHistoryMessage[] = [];\n\n for (const [offset, row] of slice.entries()) {\n const id = `row-${startIndex + offset + 1}`;\n if (isCompactionRow(row)) {\n out.push({\n id,\n role: 'system',\n kind: 'compaction',\n content: typeof row.summary === 'string' ? row.summary : '',\n timestamp: parseTimestamp(row.at),\n tokensBefore: row.tokensBefore,\n tokensAfter: row.tokensAfter,\n });\n continue;\n }\n\n const compactionSummaryRow = compactionSummaryRowToClientHistory(row);\n if (compactionSummaryRow) {\n out.push({ id, ...compactionSummaryRow });\n continue;\n }\n\n const contextRow = contextRowToClientHistory(row);\n if (contextRow) {\n out.push({ id, ...contextRow });\n continue;\n }\n\n const bashRow = bashRowToClientHistory(row);\n if (bashRow) {\n out.push({ id, ...bashRow });\n continue;\n }\n\n const customRow = customRowToClientHistory(row);\n if (customRow) {\n out.push({ id, ...customRow });\n continue;\n }\n\n const customStateRow = customStateRowToClientHistory(row);\n if (customStateRow) {\n out.push({ id, ...customStateRow });\n continue;\n }\n\n const branchSummaryRow = branchSummaryRowToClientHistory(row);\n if (branchSummaryRow) {\n out.push({ id, ...branchSummaryRow });\n continue;\n }\n\n const messageRow = asHistoryMessageRow(row);\n if (!messageRow) continue;\n if (messageRow.role === 'tool' || messageRow.role === 'toolResult') {\n continue;\n }\n\n if (messageRow.role === 'user' || messageRow.role === 'system') {\n out.push({\n id,\n role: messageRow.role,\n kind: 'message',\n content: flattenMessageContent(messageRow.content ?? ''),\n timestamp: parseTimestampValue(messageRow.timestamp),\n });\n continue;\n }\n\n out.push({\n id,\n role: 'assistant',\n kind: 'message',\n content: flattenMessageContent(messageRow.content ?? ''),\n timestamp: parseTimestampValue(messageRow.timestamp),\n toolCalls: toolCallsWithResults(messageRow.tool_calls ?? messageRow.toolCalls, results),\n });\n }\n\n return out;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client-history.js","names":[],"sources":["../../../src/session/client-history.ts"],"sourcesContent":["import type { Message } from './types.js';\nimport type { TranscriptStoredRow } from './session-context-for-llm.js';\nimport { buildTranscriptOutline } from './transcript-outline.js';\n\n/** Transcript row for TUI and HTTP clients (flattened from persisted session messages). */\nexport interface ClientHistoryMessage {\n id?: string;\n role: 'user' | 'assistant' | 'system';\n content: string;\n displayIndex?: number;\n rawContent?: string | unknown[];\n timestamp?: number;\n kind?: 'message' | 'compaction' | 'context' | 'bash' | 'custom' | 'branch';\n tokensBefore?: number;\n tokensAfter?: number;\n bash?: {\n command: string;\n output?: string;\n exitCode?: number | null;\n signal?: string | null;\n excludeFromContext?: boolean;\n truncated?: boolean;\n fullOutputPath?: string;\n };\n custom?: {\n customType: string;\n details?: unknown;\n state?: boolean;\n display?: boolean;\n };\n branch?: {\n summary: string;\n fromId?: string;\n };\n toolCalls?: Array<{ name: string; args?: unknown; result?: string; isError?: boolean }>;\n}\n\nexport function flattenMessageContent(content: string | unknown[]): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block !== 'object' || block === null) continue;\n const b = block as Record<string, unknown>;\n if (b.type === 'text' && typeof b.text === 'string') {\n parts.push(b.text);\n }\n }\n return parts.join('');\n}\n\nfunction parseTimestamp(ts: string | undefined): number | undefined {\n if (!ts) return undefined;\n const ms = Date.parse(ts);\n return Number.isFinite(ms) ? ms : undefined;\n}\n\nfunction safeJsonParseArguments(raw: string): unknown {\n const t = raw.trim();\n if (!t) return undefined;\n try {\n return JSON.parse(t) as unknown;\n } catch {\n return raw;\n }\n}\n\nfunction collectToolResults(messages: Message[]): Map<string, { text: string; isError?: boolean }> {\n const map = new Map<string, { text: string; isError?: boolean }>();\n for (const m of messages) {\n if (m.role !== 'tool' && m.role !== 'toolResult') continue;\n const id = m.tool_call_id;\n if (!id || typeof id !== 'string') continue;\n map.set(id, {\n text: flattenMessageContent(m.content),\n isError: m.isError,\n });\n }\n return map;\n}\n\nfunction toolCallsWithResults(\n tool_calls: Message['tool_calls'],\n results: Map<string, { text: string; isError?: boolean }>,\n): ClientHistoryMessage['toolCalls'] | undefined {\n if (!tool_calls?.length) return undefined;\n const out: NonNullable<ClientHistoryMessage['toolCalls']> = [];\n for (const tc of tool_calls) {\n const res = results.get(tc.id);\n out.push({\n name: tc.function.name,\n args: safeJsonParseArguments(tc.function.arguments),\n result: res?.text,\n isError: res?.isError,\n });\n }\n return out.length ? out : undefined;\n}\n\n/**\n * Maps gateway/session API messages into a linear chat history for clients.\n * Tool / toolResult rows are merged into assistant `toolCalls` when ids match; otherwise skipped.\n */\nexport function messagesToClientHistory(\n messages: Message[],\n opts?: { limit?: number },\n): ClientHistoryMessage[] {\n const slice =\n opts?.limit !== undefined && messages.length > opts.limit\n ? messages.slice(-opts.limit)\n : messages;\n\n const results = collectToolResults(slice);\n const out: ClientHistoryMessage[] = [];\n\n for (const m of slice) {\n if (m.role === 'tool' || m.role === 'toolResult') {\n continue;\n }\n\n if (m.role === 'user' || m.role === 'system') {\n const text = flattenMessageContent(m.content);\n out.push({\n role: m.role,\n content: text,\n timestamp: parseTimestamp(m.timestamp),\n });\n continue;\n }\n\n if (m.role === 'assistant') {\n const text = flattenMessageContent(m.content);\n const toolCalls = toolCallsWithResults(m.tool_calls, results);\n out.push({\n role: 'assistant',\n content: text,\n timestamp: parseTimestamp(m.timestamp),\n toolCalls,\n });\n }\n }\n\n return out;\n}\n\ntype HistoryMessageRow = {\n role: 'system' | 'user' | 'assistant' | 'tool' | 'toolResult';\n content?: string | unknown[];\n timestamp?: string | number;\n tool_call_id?: string;\n toolCallId?: string;\n tool_calls?: Message['tool_calls'];\n toolCalls?: Message['tool_calls'];\n isError?: boolean;\n};\n\nfunction asHistoryMessageRow(row: TranscriptStoredRow): HistoryMessageRow | null {\n const role = (row as { role?: unknown }).role;\n if (\n typeof role !== 'string' ||\n !['system', 'user', 'assistant', 'tool', 'toolResult'].includes(role)\n ) {\n return null;\n }\n return row as unknown as HistoryMessageRow;\n}\n\nfunction parseTimestampValue(ts: string | number | undefined): number | undefined {\n if (typeof ts === 'number') return Number.isFinite(ts) ? ts : undefined;\n return parseTimestamp(ts);\n}\n\nfunction collectHistoryToolResults(messages: HistoryMessageRow[]): Map<string, { text: string; isError?: boolean }> {\n const map = new Map<string, { text: string; isError?: boolean }>();\n for (const m of messages) {\n if (m.role !== 'tool' && m.role !== 'toolResult') continue;\n const id = m.tool_call_id ?? m.toolCallId;\n if (!id || typeof id !== 'string') continue;\n map.set(id, {\n text: flattenMessageContent(m.content ?? ''),\n isError: m.isError,\n });\n }\n return map;\n}\n\nfunction isCompactionRow(row: TranscriptStoredRow): row is TranscriptStoredRow & {\n type: 'compaction';\n summary?: string;\n at?: string;\n tokensBefore?: number;\n tokensAfter?: number;\n} {\n return (row as { type?: unknown }).type === 'compaction';\n}\n\nfunction compactionSummaryRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'compactionSummary') return null;\n const summary = optionalString(r.summary) ?? '';\n return {\n role: 'system',\n kind: 'compaction',\n content: summary,\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n tokensBefore: typeof r.tokensBefore === 'number' ? r.tokensBefore : undefined,\n };\n}\n\nfunction contextRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n if ((row as { kind?: unknown }).kind !== 'context') return null;\n const text = (row as { text?: unknown }).text;\n if (typeof text !== 'string' || !text.trim()) return null;\n const createdAt = (row as { createdAt?: unknown }).createdAt;\n return {\n role: 'system',\n kind: 'context',\n content: text,\n timestamp: typeof createdAt === 'string' ? parseTimestamp(createdAt) : undefined,\n };\n}\n\nfunction optionalString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction bashRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'bashExecution') return null;\n\n const command = optionalString(r.command) ?? optionalString(r.content)?.trim();\n if (!command) return null;\n const output = Array.isArray(r.output) || typeof r.output === 'string'\n ? flattenMessageContent(r.output)\n : optionalString(r.output);\n const exitCode = typeof r.exitCode === 'number' ? r.exitCode : r.exitCode === null ? null : undefined;\n const signal = typeof r.signal === 'string' ? r.signal : r.signal === null ? null : undefined;\n const excludeFromContext =\n typeof r.excludeFromContext === 'boolean'\n ? r.excludeFromContext\n : typeof r.excludedFromContext === 'boolean'\n ? r.excludedFromContext\n : undefined;\n\n return {\n role: 'system',\n kind: 'bash',\n content: output || command,\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n bash: {\n command,\n output,\n exitCode,\n signal,\n excludeFromContext,\n truncated: typeof r.truncated === 'boolean' ? r.truncated : undefined,\n fullOutputPath: optionalString(r.fullOutputPath),\n },\n };\n}\n\nfunction customRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'custom' && r.type !== 'custom_message') return null;\n\n const customType = optionalString(r.customType)?.trim();\n if (!customType) return null;\n const content = Array.isArray(r.content) || typeof r.content === 'string'\n ? flattenMessageContent(r.content)\n : '';\n const timestamp = parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n );\n\n return {\n role: 'system',\n kind: 'custom',\n content,\n rawContent: Array.isArray(r.content) || typeof r.content === 'string' ? r.content : undefined,\n timestamp,\n custom: {\n customType,\n details: r.details,\n display: r.display === false ? false : true,\n },\n };\n}\n\nfunction customStateRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.type !== 'custom') return null;\n\n const customType = optionalString(r.customType)?.trim();\n if (!customType) return null;\n return {\n role: 'system',\n kind: 'custom',\n content: '',\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n custom: {\n customType,\n details: r.data,\n state: true,\n },\n };\n}\n\nfunction branchSummaryRowToClientHistory(row: TranscriptStoredRow): ClientHistoryMessage | null {\n const r = row as unknown as Record<string, unknown>;\n if (r.role !== 'branchSummary') return null;\n const summary = optionalString(r.summary)?.trim();\n if (!summary) return null;\n const fromId = optionalString(r.fromId);\n\n return {\n role: 'system',\n kind: 'branch',\n content: summary,\n timestamp: parseTimestampValue(\n typeof r.timestamp === 'string' || typeof r.timestamp === 'number' ? r.timestamp : undefined,\n ),\n branch: {\n summary,\n fromId,\n },\n };\n}\n\n/**\n * Maps persisted transcript rows into linear chat history, preserving non-LLM\n * audit rows that the TUI can render as dedicated components.\n */\nexport function transcriptRowsToClientHistory(\n rows: TranscriptStoredRow[],\n opts?: { limit?: number; startRowNumber?: number; endRowNumber?: number },\n): ClientHistoryMessage[] {\n const displayIndexByRowNumber = new Map(\n buildTranscriptOutline(rows)\n .filter((entry) => entry.displayIndex !== undefined)\n .map((entry) => [entry.rowNumber, entry.displayIndex!] as const),\n );\n const hasRowWindow = opts?.startRowNumber !== undefined || opts?.endRowNumber !== undefined;\n const startIndex = hasRowWindow\n ? Math.max(0, Math.trunc(opts?.startRowNumber ?? 1) - 1)\n : opts?.limit !== undefined && rows.length > opts.limit\n ? rows.length - opts.limit\n : 0;\n const endIndex = hasRowWindow\n ? Math.max(\n startIndex,\n Math.min(rows.length, Math.trunc(opts?.endRowNumber ?? rows.length)),\n )\n : rows.length;\n const slice = hasRowWindow\n ? rows.slice(startIndex, endIndex)\n : opts?.limit !== undefined && rows.length > opts.limit\n ? rows.slice(-opts.limit)\n : rows;\n const messages = slice\n .map(asHistoryMessageRow)\n .filter((row): row is HistoryMessageRow => row !== null);\n const results = collectHistoryToolResults(messages);\n const out: ClientHistoryMessage[] = [];\n\n for (const [offset, row] of slice.entries()) {\n const rowNumber = startIndex + offset + 1;\n const id = `row-${rowNumber}`;\n const displayIndex = displayIndexByRowNumber.get(rowNumber);\n if (isCompactionRow(row)) {\n out.push({\n id,\n role: 'system',\n kind: 'compaction',\n content: typeof row.summary === 'string' ? row.summary : '',\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n timestamp: parseTimestamp(row.at),\n tokensBefore: row.tokensBefore,\n tokensAfter: row.tokensAfter,\n });\n continue;\n }\n\n const compactionSummaryRow = compactionSummaryRowToClientHistory(row);\n if (compactionSummaryRow) {\n out.push({\n id,\n ...compactionSummaryRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const contextRow = contextRowToClientHistory(row);\n if (contextRow) {\n out.push({\n id,\n ...contextRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const bashRow = bashRowToClientHistory(row);\n if (bashRow) {\n out.push({\n id,\n ...bashRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const customRow = customRowToClientHistory(row);\n if (customRow) {\n out.push({\n id,\n ...customRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const customStateRow = customStateRowToClientHistory(row);\n if (customStateRow) {\n out.push({\n id,\n ...customStateRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const branchSummaryRow = branchSummaryRowToClientHistory(row);\n if (branchSummaryRow) {\n out.push({\n id,\n ...branchSummaryRow,\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n });\n continue;\n }\n\n const messageRow = asHistoryMessageRow(row);\n if (!messageRow) continue;\n if (messageRow.role === 'tool' || messageRow.role === 'toolResult') {\n continue;\n }\n\n if (messageRow.role === 'user' || messageRow.role === 'system') {\n out.push({\n id,\n role: messageRow.role,\n kind: 'message',\n content: flattenMessageContent(messageRow.content ?? ''),\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n timestamp: parseTimestampValue(messageRow.timestamp),\n });\n continue;\n }\n\n out.push({\n id,\n role: 'assistant',\n kind: 'message',\n content: flattenMessageContent(messageRow.content ?? ''),\n ...(displayIndex !== undefined ? { displayIndex } : {}),\n timestamp: parseTimestampValue(messageRow.timestamp),\n toolCalls: toolCallsWithResults(messageRow.tool_calls ?? messageRow.toolCalls, results),\n });\n }\n\n return out;\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { SessionConfigSchema, init_schema } from "../config/schema.js";
|
|
2
1
|
import { createLogger } from "../utils/logger/index.js";
|
|
3
2
|
import { init_logger } from "../utils/logger.js";
|
|
4
3
|
import { requireXopcDatabase } from "../storage/sqlite/connection.js";
|
|
5
4
|
import "../storage/sqlite/index.js";
|
|
5
|
+
import { SessionConfigSchema, init_schema } from "../config/schema.js";
|
|
6
6
|
import { evaluateSessionFreshness, resolveSessionResetPolicy } from "./reset-policy.js";
|
|
7
7
|
import { resolveChannelResetConfig, resolveSessionResetType } from "./reset-type.js";
|
|
8
8
|
import { resolveSessionLifecycleTimestamps } from "./lifecycle-timestamps.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { SessionConfigSchema, init_schema } from "../config/schema.js";
|
|
2
1
|
import { createLogger } from "../utils/logger/index.js";
|
|
3
2
|
import { init_logger } from "../utils/logger.js";
|
|
4
3
|
import { requireXopcDatabase } from "../storage/sqlite/connection.js";
|
|
5
4
|
import { findSessionKeyBySessionId, getSessionMetadata, getSessionPersistedLevels } from "../storage/sqlite/session-repository.js";
|
|
6
5
|
import "../storage/sqlite/index.js";
|
|
6
|
+
import { SessionConfigSchema, init_schema } from "../config/schema.js";
|
|
7
7
|
import { normalizeThinkLevel, normalizeVerboseLevel } from "../agent/transcript/thinking-types.js";
|
|
8
8
|
import { evaluateSessionFreshness, resolveSessionResetPolicy } from "./reset-policy.js";
|
|
9
9
|
import { resolveChannelResetConfig, resolveSessionResetType } from "./reset-type.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { isCronSessionKey } from "../routing/session-key-utils.js";
|
|
2
|
-
import { init_session_key, parseSessionKey } from "../routing/session-key.js";
|
|
3
1
|
import { createLogger } from "../utils/logger/index.js";
|
|
4
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { isCronSessionKey } from "../routing/session-key-utils.js";
|
|
4
|
+
import { init_session_key, parseSessionKey } from "../routing/session-key.js";
|
|
5
5
|
import { init_providers, resolveModel } from "../providers/index.js";
|
|
6
6
|
import { readAgentMessageContent } from "../agent/memory/agent-message-access.js";
|
|
7
7
|
import { stripSessionStartupContextFromUserText } from "../agent/reply/startup-context.js";
|
|
@@ -32,12 +32,32 @@ function extractTextFromMessage(m) {
|
|
|
32
32
|
function firstUserText(messages) {
|
|
33
33
|
const u = messages.find((m) => m.role === "user");
|
|
34
34
|
if (!u) return "";
|
|
35
|
-
return
|
|
35
|
+
return normalizeUserTextForTitle(extractTextFromMessage(u));
|
|
36
36
|
}
|
|
37
37
|
function stripMediaClaimChecks(text) {
|
|
38
38
|
if (!text.includes("[media attached:") && !text.includes("xopc-media-uri:")) return text;
|
|
39
39
|
return text.replace(/\s*\[media attached:[^\]]+\]\s*\r?\nxopc-media-uri:[^\r\n]+\r?\n\s*xopc-media-path:[^\r\n]+(?:\r?\n\s*Use the read_media tool[^\r\n]*)?/g, "").replace(/\s*\[media attached:[^\]]+\]\s*/g, " ").replace(/\s*xopc-media-uri:[^\r\n]+/g, "").replace(/\s*xopc-media-path:[^\r\n]+/g, "").replace(/\s*Use the read_media tool[^\r\n]*/g, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
40
40
|
}
|
|
41
|
+
function stripExpandedSkillBlockForTitle(text) {
|
|
42
|
+
const trimmed = text.trimStart();
|
|
43
|
+
if (!/^##\s+Skill:\s*\S+/i.test(trimmed)) return text;
|
|
44
|
+
const markerRe = /(?:^|\r?\n)\*\*Arguments\*\*:\s*/gi;
|
|
45
|
+
let argsStart = -1;
|
|
46
|
+
while (markerRe.exec(trimmed) !== null) argsStart = markerRe.lastIndex;
|
|
47
|
+
if (argsStart !== -1) {
|
|
48
|
+
const args = trimmed.slice(argsStart).trim();
|
|
49
|
+
if (args) return args;
|
|
50
|
+
}
|
|
51
|
+
return (trimmed.split(/\r?\n/)[0]?.trim() ?? "").replace(/^##\s+Skill:\s*/i, "").trim();
|
|
52
|
+
}
|
|
53
|
+
function stripSkillCommandForTitle(text) {
|
|
54
|
+
const match = text.trimStart().match(/^\/skill:([^\s]+)(?:\s+([\s\S]*))?$/i);
|
|
55
|
+
if (!match) return text;
|
|
56
|
+
return match[2]?.trim() || match[1]?.trim() || text;
|
|
57
|
+
}
|
|
58
|
+
function normalizeUserTextForTitle(raw) {
|
|
59
|
+
return stripSkillCommandForTitle(stripExpandedSkillBlockForTitle(stripMediaClaimChecks(stripEnvelopeTimestampPrefix(stripSessionStartupContextFromUserText(raw))))).trim();
|
|
60
|
+
}
|
|
41
61
|
/** First assistant message that has visible text (skips tool-only assistant rows). */
|
|
42
62
|
function firstAssistantText(messages) {
|
|
43
63
|
for (const m of messages) if (m.role === "assistant") {
|
|
@@ -73,7 +93,7 @@ function getSessionTitleSource(meta) {
|
|
|
73
93
|
}
|
|
74
94
|
/** Title from a single user message (first line), for immediate sidebar labels. */
|
|
75
95
|
function provisionalTitleFromUserText(raw) {
|
|
76
|
-
const text =
|
|
96
|
+
const text = normalizeUserTextForTitle((raw ?? "").trim());
|
|
77
97
|
if (!text) return null;
|
|
78
98
|
const line = text.split(/\n/)[0]?.trim();
|
|
79
99
|
if (!line) return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-title.js","names":[],"sources":["../../../src/session/session-title.ts"],"sourcesContent":["/**\n * LLM-generated session titles (webchat and any path using SessionStore).\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport { complete, type UserMessage } from '@earendil-works/pi-ai/compat';\n\nimport { stripSessionStartupContextFromUserText } from '../agent/reply/startup-context.js';\nimport { stripEnvelopeTimestampPrefix } from '../channels/envelope-timestamp.js';\nimport { isCronSessionKey, parseSessionKey } from '../routing/session-key.js';\nimport { resolveModel } from '../providers/index.js';\nimport { createLogger } from '../utils/logger.js';\nimport { readAgentMessageContent } from '../agent/memory/agent-message-access.js';\nimport type { SessionStore } from './store.js';\n\nconst log = createLogger('SessionAutoTitle');\n\nconst MAX_TITLE_LEN = 80;\n\n/** Collect visible text from any content block that exposes `text` (pi-ai / OpenAI / Anthropic shapes). */\nfunction extractTextFromMessage(m: AgentMessage): string {\n const raw = readAgentMessageContent(m);\n if (typeof raw === 'string') return raw.trim();\n if (Array.isArray(raw)) {\n const parts: string[] = [];\n for (const c of raw) {\n if (c && typeof c === 'object') {\n const o = c as unknown as Record<string, unknown>;\n const type = typeof o.type === 'string' ? o.type : '';\n if (type === 'toolCall' || type === 'tool_use' || type === 'tool_result') continue;\n if (typeof o.text === 'string' && o.text.trim()) {\n parts.push(o.text.trim());\n }\n }\n }\n return parts.join(' ').trim();\n }\n return '';\n}\n\nfunction firstUserText(messages: AgentMessage[]): string {\n const u = messages.find((m) => m.role === 'user');\n if (!u) return '';\n const raw = extractTextFromMessage(u);\n return stripMediaClaimChecks(stripEnvelopeTimestampPrefix(stripSessionStartupContextFromUserText(raw)));\n}\n\nfunction stripMediaClaimChecks(text: string): string {\n if (!text.includes('[media attached:') && !text.includes('xopc-media-uri:')) {\n return text;\n }\n return text\n .replace(\n /\\s*\\[media attached:[^\\]]+\\]\\s*\\r?\\nxopc-media-uri:[^\\r\\n]+\\r?\\n\\s*xopc-media-path:[^\\r\\n]+(?:\\r?\\n\\s*Use the read_media tool[^\\r\\n]*)?/g,\n '',\n )\n .replace(/\\s*\\[media attached:[^\\]]+\\]\\s*/g, ' ')\n .replace(/\\s*xopc-media-uri:[^\\r\\n]+/g, '')\n .replace(/\\s*xopc-media-path:[^\\r\\n]+/g, '')\n .replace(/\\s*Use the read_media tool[^\\r\\n]*/g, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n\n/** First assistant message that has visible text (skips tool-only assistant rows). */\nfunction firstAssistantText(messages: AgentMessage[]): string {\n for (const m of messages) {\n if (m.role === 'assistant') {\n const t = extractTextFromMessage(m);\n if (t.length > 0) return t;\n }\n }\n return '';\n}\n\nexport function isWebchatSessionKey(sessionKey: string): boolean {\n const p = parseSessionKey(sessionKey);\n if (p?.source === 'webchat') return true;\n return sessionKey.includes(':webchat:');\n}\n\n/** Whether to run LLM/fallback session naming for this key (excludes cron, heartbeat). */\nexport function shouldAutoTitleSessionKey(sessionKey: string): boolean {\n const raw = (sessionKey ?? '').trim();\n if (!raw) return false;\n if (isCronSessionKey(raw)) return false;\n if (raw.toLowerCase().startsWith('heartbeat:')) return false;\n return true;\n}\n\nexport function sanitizeSessionTitle(raw: string): string {\n let s = raw.trim();\n if ((s.startsWith('\"') && s.endsWith('\"')) || (s.startsWith(\"'\") && s.endsWith(\"'\"))) {\n s = s.slice(1, -1).trim();\n }\n const lineBreak = s.indexOf('\\n');\n if (lineBreak !== -1) s = s.slice(0, lineBreak).trim();\n if (s.length > MAX_TITLE_LEN) s = s.slice(0, MAX_TITLE_LEN - 1).trimEnd() + '…';\n return s;\n}\n\nexport type SessionTitleSource = 'provisional' | 'llm' | 'user';\n\nexport function getSessionTitleSource(\n meta: { customData?: Record<string, unknown> } | null | undefined,\n): SessionTitleSource | null {\n const raw = meta?.customData?.titleSource;\n if (raw === 'provisional' || raw === 'llm' || raw === 'user') return raw;\n return null;\n}\n\n/** Title from a single user message (first line), for immediate sidebar labels. */\nexport function provisionalTitleFromUserText(raw: string): string | null {\n const text = stripEnvelopeTimestampPrefix(\n stripSessionStartupContextFromUserText((raw ?? '').trim()),\n );\n if (!text) return null;\n const line = text.split(/\\n/)[0]?.trim();\n if (!line) return null;\n return sanitizeSessionTitle(line);\n}\n\n/** Non-LLM title: first line of first user text, else first assistant line. */\nexport function fallbackTitleFromMessages(messages: AgentMessage[]): string | null {\n const u = firstUserText(messages);\n if (u) {\n const line = u.split(/\\n/)[0]?.trim();\n if (line) return sanitizeSessionTitle(line);\n }\n const a = firstAssistantText(messages);\n if (a) {\n const line = a.split(/\\n/)[0]?.trim();\n if (line) return sanitizeSessionTitle(line);\n }\n return null;\n}\n\n/**\n * Returns a title string, or null if generation should be skipped or failed.\n */\nexport async function generateSessionTitleFromMessages(\n modelRef: string,\n messages: AgentMessage[],\n signal?: AbortSignal,\n): Promise<string | null> {\n const userText = firstUserText(messages);\n const assistantText = firstAssistantText(messages);\n if (!userText && !assistantText) return null;\n\n let model: ReturnType<typeof resolveModel>;\n try {\n model = resolveModel(modelRef);\n } catch (err) {\n log.warn({ err, modelRef }, 'Cannot resolve model for session title');\n return null;\n }\n\n const prompt =\n userText && assistantText\n ? `You label chat sessions. Given the first user message and the start of the assistant reply, output ONE short title (max 8 words). No quotes. No punctuation at the end. Use the same language as the user when possible.\n\nUser: ${userText.slice(0, 2000)}\n\nAssistant: ${assistantText.slice(0, 2000)}\n\nTitle:`\n : userText\n ? `The assistant reply only used tools (no visible text yet). Output ONE short title (max 8 words) based only on the user's first message. No quotes. No punctuation at the end. Use the same language as the user.\n\nUser: ${userText.slice(0, 2000)}\n\nTitle:`\n : `Output ONE short title (max 8 words) for this assistant reply. No quotes. No punctuation at the end.\n\nAssistant: ${assistantText!.slice(0, 2000)}\n\nTitle:`;\n\n const userMsg: UserMessage = { role: 'user', content: prompt, timestamp: Date.now() };\n\n try {\n const result = await complete(\n model,\n { messages: [userMsg] },\n {\n maxTokens: 64,\n temperature: 0.35,\n signal: signal as AbortSignal,\n },\n );\n\n let text = '';\n if (Array.isArray(result.content)) {\n for (const c of result.content) {\n if (c && typeof c === 'object' && (c as { type?: string }).type === 'text') {\n text += String((c as { text?: string }).text || '');\n }\n }\n }\n\n const cleaned = sanitizeSessionTitle(text);\n return cleaned.length > 0 ? cleaned : null;\n } catch (err) {\n log.warn({ err }, 'Session title LLM call failed');\n return null;\n }\n}\n\nexport type SessionTitleUpdatedHook = (sessionKey: string, name: string) => void | Promise<void>;\n\n/** Whether post-turn LLM refine may run (provisional or still unnamed; not user-locked). */\nexport function shouldRefineSessionTitleWithLlm(\n meta: { name?: string; customData?: Record<string, unknown> } | null | undefined,\n): boolean {\n if (!meta) return false;\n const source = getSessionTitleSource(meta);\n if (source === 'user') return false;\n if (meta.name?.trim()) return source === 'provisional';\n return true;\n}\n\nfunction canAutoWriteTitle(meta: { name?: string; customData?: Record<string, unknown> } | null): boolean {\n if (!meta) return false;\n const source = getSessionTitleSource(meta);\n if (source === 'user') return false;\n return !meta.name?.trim();\n}\n\n/**\n * Set provisional title from first user text when session is still unnamed.\n * Skips cron/heartbeat keys and user-locked titles.\n */\nexport async function maybeSetProvisionalSessionTitle(\n sessionStore: SessionStore,\n sessionKey: string,\n userText?: string,\n onUpdated?: SessionTitleUpdatedHook,\n): Promise<void> {\n if (!shouldAutoTitleSessionKey(sessionKey)) return;\n\n let meta = await sessionStore.getMetadata(sessionKey);\n if (!meta) return;\n if (!canAutoWriteTitle(meta)) return;\n\n let title: string | null = null;\n if (userText?.trim()) {\n title = provisionalTitleFromUserText(userText);\n }\n if (!title) {\n const messages = await sessionStore.load(sessionKey);\n if (!messages.length) return;\n title = fallbackTitleFromMessages(messages);\n }\n if (!title) return;\n\n try {\n await sessionStore.updateMetadata(sessionKey, {\n name: title,\n customData: { ...(meta.customData ?? {}), titleSource: 'provisional' },\n });\n await onUpdated?.(sessionKey, title);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Session title: provisional updateMetadata failed');\n }\n}\n\n/**\n * LLM refine when title is empty or still provisional (not user-locked).\n */\nexport async function maybeRefineSessionTitleWithLlm(\n sessionStore: SessionStore,\n sessionKey: string,\n modelRef: string | undefined,\n onUpdated?: SessionTitleUpdatedHook,\n): Promise<void> {\n if (!shouldAutoTitleSessionKey(sessionKey)) return;\n\n let messages = await sessionStore.load(sessionKey);\n if (!messages.length) return;\n\n let meta = await sessionStore.getMetadata(sessionKey);\n if (!meta) {\n await sessionStore.saveMessages(sessionKey, messages);\n meta = await sessionStore.getMetadata(sessionKey);\n }\n if (!meta) {\n log.warn({ sessionKey }, 'Session title: metadata missing after save');\n return;\n }\n if (!shouldRefineSessionTitleWithLlm(meta)) return;\n\n const source = getSessionTitleSource(meta);\n let title: string | null = null;\n const ref = modelRef?.trim();\n if (ref) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 25_000);\n try {\n title = await generateSessionTitleFromMessages(ref, messages, controller.signal);\n } finally {\n clearTimeout(timeout);\n }\n }\n if (!title) {\n title = fallbackTitleFromMessages(messages);\n }\n if (!title) return;\n\n const existing = meta.name?.trim();\n if (existing === title) {\n if (source !== 'llm') {\n try {\n await sessionStore.updateMetadata(sessionKey, {\n customData: { ...(meta.customData ?? {}), titleSource: 'llm' },\n });\n } catch {\n /* ignore */\n }\n }\n return;\n }\n\n try {\n await sessionStore.updateMetadata(sessionKey, {\n name: title,\n customData: { ...(meta.customData ?? {}), titleSource: 'llm' },\n });\n await onUpdated?.(sessionKey, title);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Session title: refine updateMetadata failed');\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session-title.js","names":[],"sources":["../../../src/session/session-title.ts"],"sourcesContent":["/**\n * LLM-generated session titles (webchat and any path using SessionStore).\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport { complete, type UserMessage } from '@earendil-works/pi-ai/compat';\n\nimport { stripSessionStartupContextFromUserText } from '../agent/reply/startup-context.js';\nimport { stripEnvelopeTimestampPrefix } from '../channels/envelope-timestamp.js';\nimport { isCronSessionKey, parseSessionKey } from '../routing/session-key.js';\nimport { resolveModel } from '../providers/index.js';\nimport { createLogger } from '../utils/logger.js';\nimport { readAgentMessageContent } from '../agent/memory/agent-message-access.js';\nimport type { SessionStore } from './store.js';\n\nconst log = createLogger('SessionAutoTitle');\n\nconst MAX_TITLE_LEN = 80;\n\n/** Collect visible text from any content block that exposes `text` (pi-ai / OpenAI / Anthropic shapes). */\nfunction extractTextFromMessage(m: AgentMessage): string {\n const raw = readAgentMessageContent(m);\n if (typeof raw === 'string') return raw.trim();\n if (Array.isArray(raw)) {\n const parts: string[] = [];\n for (const c of raw) {\n if (c && typeof c === 'object') {\n const o = c as unknown as Record<string, unknown>;\n const type = typeof o.type === 'string' ? o.type : '';\n if (type === 'toolCall' || type === 'tool_use' || type === 'tool_result') continue;\n if (typeof o.text === 'string' && o.text.trim()) {\n parts.push(o.text.trim());\n }\n }\n }\n return parts.join(' ').trim();\n }\n return '';\n}\n\nfunction firstUserText(messages: AgentMessage[]): string {\n const u = messages.find((m) => m.role === 'user');\n if (!u) return '';\n const raw = extractTextFromMessage(u);\n return normalizeUserTextForTitle(raw);\n}\n\nfunction stripMediaClaimChecks(text: string): string {\n if (!text.includes('[media attached:') && !text.includes('xopc-media-uri:')) {\n return text;\n }\n return text\n .replace(\n /\\s*\\[media attached:[^\\]]+\\]\\s*\\r?\\nxopc-media-uri:[^\\r\\n]+\\r?\\n\\s*xopc-media-path:[^\\r\\n]+(?:\\r?\\n\\s*Use the read_media tool[^\\r\\n]*)?/g,\n '',\n )\n .replace(/\\s*\\[media attached:[^\\]]+\\]\\s*/g, ' ')\n .replace(/\\s*xopc-media-uri:[^\\r\\n]+/g, '')\n .replace(/\\s*xopc-media-path:[^\\r\\n]+/g, '')\n .replace(/\\s*Use the read_media tool[^\\r\\n]*/g, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n\nfunction stripExpandedSkillBlockForTitle(text: string): string {\n const trimmed = text.trimStart();\n if (!/^##\\s+Skill:\\s*\\S+/i.test(trimmed)) {\n return text;\n }\n\n const markerRe = /(?:^|\\r?\\n)\\*\\*Arguments\\*\\*:\\s*/gi;\n let argsStart = -1;\n while (markerRe.exec(trimmed) !== null) {\n argsStart = markerRe.lastIndex;\n }\n\n if (argsStart !== -1) {\n const args = trimmed.slice(argsStart).trim();\n if (args) return args;\n }\n\n const firstLine = trimmed.split(/\\r?\\n/)[0]?.trim() ?? '';\n return firstLine.replace(/^##\\s+Skill:\\s*/i, '').trim();\n}\n\nfunction stripSkillCommandForTitle(text: string): string {\n const trimmed = text.trimStart();\n const match = trimmed.match(/^\\/skill:([^\\s]+)(?:\\s+([\\s\\S]*))?$/i);\n if (!match) {\n return text;\n }\n const args = match[2]?.trim();\n return args || match[1]?.trim() || text;\n}\n\nfunction normalizeUserTextForTitle(raw: string): string {\n return stripSkillCommandForTitle(\n stripExpandedSkillBlockForTitle(\n stripMediaClaimChecks(\n stripEnvelopeTimestampPrefix(stripSessionStartupContextFromUserText(raw)),\n ),\n ),\n ).trim();\n}\n\n/** First assistant message that has visible text (skips tool-only assistant rows). */\nfunction firstAssistantText(messages: AgentMessage[]): string {\n for (const m of messages) {\n if (m.role === 'assistant') {\n const t = extractTextFromMessage(m);\n if (t.length > 0) return t;\n }\n }\n return '';\n}\n\nexport function isWebchatSessionKey(sessionKey: string): boolean {\n const p = parseSessionKey(sessionKey);\n if (p?.source === 'webchat') return true;\n return sessionKey.includes(':webchat:');\n}\n\n/** Whether to run LLM/fallback session naming for this key (excludes cron, heartbeat). */\nexport function shouldAutoTitleSessionKey(sessionKey: string): boolean {\n const raw = (sessionKey ?? '').trim();\n if (!raw) return false;\n if (isCronSessionKey(raw)) return false;\n if (raw.toLowerCase().startsWith('heartbeat:')) return false;\n return true;\n}\n\nexport function sanitizeSessionTitle(raw: string): string {\n let s = raw.trim();\n if ((s.startsWith('\"') && s.endsWith('\"')) || (s.startsWith(\"'\") && s.endsWith(\"'\"))) {\n s = s.slice(1, -1).trim();\n }\n const lineBreak = s.indexOf('\\n');\n if (lineBreak !== -1) s = s.slice(0, lineBreak).trim();\n if (s.length > MAX_TITLE_LEN) s = s.slice(0, MAX_TITLE_LEN - 1).trimEnd() + '…';\n return s;\n}\n\nexport type SessionTitleSource = 'provisional' | 'llm' | 'user';\n\nexport function getSessionTitleSource(\n meta: { customData?: Record<string, unknown> } | null | undefined,\n): SessionTitleSource | null {\n const raw = meta?.customData?.titleSource;\n if (raw === 'provisional' || raw === 'llm' || raw === 'user') return raw;\n return null;\n}\n\n/** Title from a single user message (first line), for immediate sidebar labels. */\nexport function provisionalTitleFromUserText(raw: string): string | null {\n const text = normalizeUserTextForTitle((raw ?? '').trim());\n if (!text) return null;\n const line = text.split(/\\n/)[0]?.trim();\n if (!line) return null;\n return sanitizeSessionTitle(line);\n}\n\n/** Non-LLM title: first line of first user text, else first assistant line. */\nexport function fallbackTitleFromMessages(messages: AgentMessage[]): string | null {\n const u = firstUserText(messages);\n if (u) {\n const line = u.split(/\\n/)[0]?.trim();\n if (line) return sanitizeSessionTitle(line);\n }\n const a = firstAssistantText(messages);\n if (a) {\n const line = a.split(/\\n/)[0]?.trim();\n if (line) return sanitizeSessionTitle(line);\n }\n return null;\n}\n\n/**\n * Returns a title string, or null if generation should be skipped or failed.\n */\nexport async function generateSessionTitleFromMessages(\n modelRef: string,\n messages: AgentMessage[],\n signal?: AbortSignal,\n): Promise<string | null> {\n const userText = firstUserText(messages);\n const assistantText = firstAssistantText(messages);\n if (!userText && !assistantText) return null;\n\n let model: ReturnType<typeof resolveModel>;\n try {\n model = resolveModel(modelRef);\n } catch (err) {\n log.warn({ err, modelRef }, 'Cannot resolve model for session title');\n return null;\n }\n\n const prompt =\n userText && assistantText\n ? `You label chat sessions. Given the first user message and the start of the assistant reply, output ONE short title (max 8 words). No quotes. No punctuation at the end. Use the same language as the user when possible.\n\nUser: ${userText.slice(0, 2000)}\n\nAssistant: ${assistantText.slice(0, 2000)}\n\nTitle:`\n : userText\n ? `The assistant reply only used tools (no visible text yet). Output ONE short title (max 8 words) based only on the user's first message. No quotes. No punctuation at the end. Use the same language as the user.\n\nUser: ${userText.slice(0, 2000)}\n\nTitle:`\n : `Output ONE short title (max 8 words) for this assistant reply. No quotes. No punctuation at the end.\n\nAssistant: ${assistantText!.slice(0, 2000)}\n\nTitle:`;\n\n const userMsg: UserMessage = { role: 'user', content: prompt, timestamp: Date.now() };\n\n try {\n const result = await complete(\n model,\n { messages: [userMsg] },\n {\n maxTokens: 64,\n temperature: 0.35,\n signal: signal as AbortSignal,\n },\n );\n\n let text = '';\n if (Array.isArray(result.content)) {\n for (const c of result.content) {\n if (c && typeof c === 'object' && (c as { type?: string }).type === 'text') {\n text += String((c as { text?: string }).text || '');\n }\n }\n }\n\n const cleaned = sanitizeSessionTitle(text);\n return cleaned.length > 0 ? cleaned : null;\n } catch (err) {\n log.warn({ err }, 'Session title LLM call failed');\n return null;\n }\n}\n\nexport type SessionTitleUpdatedHook = (sessionKey: string, name: string) => void | Promise<void>;\n\n/** Whether post-turn LLM refine may run (provisional or still unnamed; not user-locked). */\nexport function shouldRefineSessionTitleWithLlm(\n meta: { name?: string; customData?: Record<string, unknown> } | null | undefined,\n): boolean {\n if (!meta) return false;\n const source = getSessionTitleSource(meta);\n if (source === 'user') return false;\n if (meta.name?.trim()) return source === 'provisional';\n return true;\n}\n\nfunction canAutoWriteTitle(meta: { name?: string; customData?: Record<string, unknown> } | null): boolean {\n if (!meta) return false;\n const source = getSessionTitleSource(meta);\n if (source === 'user') return false;\n return !meta.name?.trim();\n}\n\n/**\n * Set provisional title from first user text when session is still unnamed.\n * Skips cron/heartbeat keys and user-locked titles.\n */\nexport async function maybeSetProvisionalSessionTitle(\n sessionStore: SessionStore,\n sessionKey: string,\n userText?: string,\n onUpdated?: SessionTitleUpdatedHook,\n): Promise<void> {\n if (!shouldAutoTitleSessionKey(sessionKey)) return;\n\n let meta = await sessionStore.getMetadata(sessionKey);\n if (!meta) return;\n if (!canAutoWriteTitle(meta)) return;\n\n let title: string | null = null;\n if (userText?.trim()) {\n title = provisionalTitleFromUserText(userText);\n }\n if (!title) {\n const messages = await sessionStore.load(sessionKey);\n if (!messages.length) return;\n title = fallbackTitleFromMessages(messages);\n }\n if (!title) return;\n\n try {\n await sessionStore.updateMetadata(sessionKey, {\n name: title,\n customData: { ...(meta.customData ?? {}), titleSource: 'provisional' },\n });\n await onUpdated?.(sessionKey, title);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Session title: provisional updateMetadata failed');\n }\n}\n\n/**\n * LLM refine when title is empty or still provisional (not user-locked).\n */\nexport async function maybeRefineSessionTitleWithLlm(\n sessionStore: SessionStore,\n sessionKey: string,\n modelRef: string | undefined,\n onUpdated?: SessionTitleUpdatedHook,\n): Promise<void> {\n if (!shouldAutoTitleSessionKey(sessionKey)) return;\n\n let messages = await sessionStore.load(sessionKey);\n if (!messages.length) return;\n\n let meta = await sessionStore.getMetadata(sessionKey);\n if (!meta) {\n await sessionStore.saveMessages(sessionKey, messages);\n meta = await sessionStore.getMetadata(sessionKey);\n }\n if (!meta) {\n log.warn({ sessionKey }, 'Session title: metadata missing after save');\n return;\n }\n if (!shouldRefineSessionTitleWithLlm(meta)) return;\n\n const source = getSessionTitleSource(meta);\n let title: string | null = null;\n const ref = modelRef?.trim();\n if (ref) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 25_000);\n try {\n title = await generateSessionTitleFromMessages(ref, messages, controller.signal);\n } finally {\n clearTimeout(timeout);\n }\n }\n if (!title) {\n title = fallbackTitleFromMessages(messages);\n }\n if (!title) return;\n\n const existing = meta.name?.trim();\n if (existing === title) {\n if (source !== 'llm') {\n try {\n await sessionStore.updateMetadata(sessionKey, {\n customData: { ...(meta.customData ?? {}), titleSource: 'llm' },\n });\n } catch {\n /* ignore */\n }\n }\n return;\n }\n\n try {\n await sessionStore.updateMetadata(sessionKey, {\n name: title,\n customData: { ...(meta.customData ?? {}), titleSource: 'llm' },\n });\n await onUpdated?.(sessionKey, title);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Session title: refine updateMetadata failed');\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { init_paths_state, resolveStateDir } from "../config/paths-state.js";
|
|
2
|
-
import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
|
|
3
2
|
import { createLogger } from "../utils/logger/index.js";
|
|
4
3
|
import { init_logger } from "../utils/logger.js";
|
|
5
4
|
import { requireXopcDatabase } from "../storage/sqlite/connection.js";
|
|
@@ -9,6 +8,7 @@ import { estimateTokensFromMessages } from "../storage/sqlite/row-mappers.js";
|
|
|
9
8
|
import { deleteSessionRecord, ensureSessionRecord, findSessionKeyBySessionId, getGlobalSessionStats, getSessionMetadata, listSessionMetadata, listSessionsByAgent, patchSessionMetadata, resetSessionRecord } from "../storage/sqlite/session-repository.js";
|
|
10
9
|
import { appendTranscriptEntry, captureCompactionCheckpoint, getCompactionCheckpointDetail, listCompactionCheckpoints, loadCheckpointRows, loadLlmMessagesForSession, loadTranscriptRowsForSession, replaceTranscriptRows, restoreCompactionCheckpoint } from "../storage/sqlite/transcript-repository.js";
|
|
11
10
|
import "../storage/sqlite/index.js";
|
|
11
|
+
import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
|
|
12
12
|
import { readPostCompactionContext } from "../agent/reply/post-compaction-context.js";
|
|
13
13
|
import { SessionCompactor } from "../agent/memory/compaction.js";
|
|
14
14
|
import { SlidingWindow } from "../agent/memory/window.js";
|
|
@@ -150,6 +150,10 @@ export interface SessionListQuery {
|
|
|
150
150
|
sessionTypes?: SessionType[];
|
|
151
151
|
includeHidden?: boolean;
|
|
152
152
|
projectId?: string;
|
|
153
|
+
unassigned?: boolean;
|
|
154
|
+
updatedAfter?: number;
|
|
155
|
+
includePinned?: boolean;
|
|
156
|
+
includeSessionKey?: string;
|
|
153
157
|
sortBy?: 'updatedAt' | 'createdAt' | 'messageCount' | 'lastAccessedAt';
|
|
154
158
|
sortOrder?: 'asc' | 'desc';
|
|
155
159
|
limit?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":[],"sources":["../../../src/session/types.ts"],"sourcesContent":["// Session management types\n\nimport type { TranscriptStoredRow } from './session-context-for-llm.js';\n\nexport interface Message {\n role: 'system' | 'user' | 'assistant' | 'tool' | 'toolResult';\n /** Plain string or structured content blocks (tool calls, multimodal). */\n content: string | unknown[];\n timestamp?: string;\n tool_call_id?: string;\n tool_calls?: Array<{\n id: string;\n type: 'function';\n function: {\n name: string;\n arguments: string;\n };\n }>;\n tool_call_id$?: string;\n toolName?: string;\n isError?: boolean;\n name?: string;\n /** Token usage from the LLM response (assistant messages only). */\n usage?: {\n inputTokens?: number;\n outputTokens?: number;\n cacheReadTokens?: number;\n cacheWriteTokens?: number;\n totalTokens?: number;\n };\n /** Inbound attachment metadata (no base64). */\n media?: Array<{\n id: string;\n bucket: string;\n type: string;\n mimeType: string;\n name: string;\n size: number;\n uri: string;\n path: string;\n }>;\n}\n\n/** Session status enum */\nexport enum SessionStatus {\n ACTIVE = 'active',\n IDLE = 'idle',\n ARCHIVED = 'archived',\n PINNED = 'pinned',\n}\n\n/** Session routing metadata */\nexport interface SessionRoutingMeta {\n agentId: string;\n source: string;\n accountId: string;\n peerKind: string;\n peerId: string;\n threadId?: string;\n scopeId?: string;\n mainSessionKey?: string;\n lastRoutePolicy?: 'main' | 'session';\n}\n\n/** Session-level statistics (per session) */\nexport interface SessionStats {\n messageCount: number;\n tokenCount: number;\n turnCount?: number;\n lastTurnAt?: number;\n}\n\n/** Global session statistics (aggregate) */\nexport interface GlobalSessionStats {\n totalSessions: number;\n activeSessions: number;\n archivedSessions: number;\n pinnedSessions: number;\n totalMessages: number;\n totalTokens: number;\n oldestSession?: string;\n newestSession?: string;\n byChannel: Record<string, number>;\n}\n\nexport type SessionType = 'chat' | 'workflow-run' | 'workflow-subagent' | 'cron' | 'heartbeat';\n\n/** Session metadata (stored in index) */\nexport interface SessionMetadata {\n key: string;\n name?: string;\n status: SessionStatus;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n lastAccessedAt: string;\n messageCount: number;\n estimatedTokens: number;\n compactedCount: number;\n lastFlushedAt?: string;\n flushCount?: number;\n sourceChannel: string;\n sourceChatId: string;\n customData?: Record<string, unknown>;\n /** Optional project grouping for sessions, goals, workflows, and related context. */\n projectId?: string;\n /** Routing metadata */\n routing?: SessionRoutingMeta;\n /** Session statistics */\n stats?: SessionStats;\n /**\n * High-level origin for filtering/UI (e.g. `cron`, `heartbeat`, `webchat`).\n * Distinct from `sourceChannel` (routing namespace).\n */\n sessionType: SessionType;\n hiddenFromSessionList?: boolean;\n parentSessionKey?: string;\n workflowRunId?: string;\n workflowDefinitionId?: string;\n workflowAgentId?: string;\n workflowAgentLabel?: string;\n /** Active OpenClaw-style session instance id. Rotates on reset while `key` stays stable. */\n sessionId?: string;\n /** Workspace/cwd recorded for the active transcript. */\n cwd?: string;\n /** First activity time for this session row (ISO), from transcript header when available. */\n sessionStartedAt?: string;\n /** Last transcript write / interaction (ISO), updated on each persist. */\n lastInteractionAt?: string;\n /**\n * Epoch ms when the last webchat run was aborted (`POST /api/agent/abort`).\n * Used with `clientCreatedAtMs` on the next POST /api/agent to drop stale queued sends.\n */\n abortCutoffTimestamp?: number;\n}\n\n/** Summary of wrapped transcript (no duplicate message bodies). */\nexport interface SessionTranscriptSummary {\n id: string;\n version: number;\n createdAt: string;\n updatedAt: string;\n compactionCount: number;\n}\n\n/** Session detail (metadata + messages) */\nexport interface SessionDetail extends SessionMetadata {\n messages: Message[];\n /** Present when loaded with `includeTranscriptSummary` (gateway `?include=transcript`). */\n transcriptSummary?: SessionTranscriptSummary;\n /** Present when loaded with `include=transcriptRows` (full on-disk rows, LLM + `kind: 'context'`). */\n transcriptRows?: TranscriptStoredRow[];\n}\n\n/** Session list query parameters */\nexport interface SessionListQuery {\n status?: SessionStatus | SessionStatus[];\n /** Single `sourceChannel`, or comma-separated list (e.g. `telegram,weixin`). */\n channel?: string;\n tags?: string[];\n search?: string;\n sessionTypes?: SessionType[];\n includeHidden?: boolean;\n projectId?: string;\n sortBy?: 'updatedAt' | 'createdAt' | 'messageCount' | 'lastAccessedAt';\n sortOrder?: 'asc' | 'desc';\n limit?: number;\n offset?: number;\n}\n\n/** Paginated result */\nexport interface PaginatedResult<T> {\n items: T[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\n/** Export format */\nexport type ExportFormat = 'json' | 'markdown';\n\n/** Session export data */\nexport interface SessionExport {\n version: string;\n exportedAt: string;\n metadata: SessionMetadata;\n messages: Message[];\n /** Full on-disk rows (LLM + `kind: 'context'`) for audit / round-trip. */\n transcriptRows: TranscriptStoredRow[];\n}\n\n/** On-disk pre-compaction snapshot (OpenClaw-style checkpoint list). */\nexport interface CompactionCheckpointSummary {\n id: string;\n sizeBytes: number;\n modifiedAt: string;\n}\n\nexport interface CompactionCheckpointDetail extends CompactionCheckpointSummary {\n messageCount: number;\n}\n"],"mappings":";;AA4CA,IAAY,gBAAL,yBAAA,eAAA;AACL,eAAA,YAAA;AACA,eAAA,UAAA;AACA,eAAA,cAAA;AACA,eAAA,YAAA;;KACD"}
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../../src/session/types.ts"],"sourcesContent":["// Session management types\n\nimport type { TranscriptStoredRow } from './session-context-for-llm.js';\n\nexport interface Message {\n role: 'system' | 'user' | 'assistant' | 'tool' | 'toolResult';\n /** Plain string or structured content blocks (tool calls, multimodal). */\n content: string | unknown[];\n timestamp?: string;\n tool_call_id?: string;\n tool_calls?: Array<{\n id: string;\n type: 'function';\n function: {\n name: string;\n arguments: string;\n };\n }>;\n tool_call_id$?: string;\n toolName?: string;\n isError?: boolean;\n name?: string;\n /** Token usage from the LLM response (assistant messages only). */\n usage?: {\n inputTokens?: number;\n outputTokens?: number;\n cacheReadTokens?: number;\n cacheWriteTokens?: number;\n totalTokens?: number;\n };\n /** Inbound attachment metadata (no base64). */\n media?: Array<{\n id: string;\n bucket: string;\n type: string;\n mimeType: string;\n name: string;\n size: number;\n uri: string;\n path: string;\n }>;\n}\n\n/** Session status enum */\nexport enum SessionStatus {\n ACTIVE = 'active',\n IDLE = 'idle',\n ARCHIVED = 'archived',\n PINNED = 'pinned',\n}\n\n/** Session routing metadata */\nexport interface SessionRoutingMeta {\n agentId: string;\n source: string;\n accountId: string;\n peerKind: string;\n peerId: string;\n threadId?: string;\n scopeId?: string;\n mainSessionKey?: string;\n lastRoutePolicy?: 'main' | 'session';\n}\n\n/** Session-level statistics (per session) */\nexport interface SessionStats {\n messageCount: number;\n tokenCount: number;\n turnCount?: number;\n lastTurnAt?: number;\n}\n\n/** Global session statistics (aggregate) */\nexport interface GlobalSessionStats {\n totalSessions: number;\n activeSessions: number;\n archivedSessions: number;\n pinnedSessions: number;\n totalMessages: number;\n totalTokens: number;\n oldestSession?: string;\n newestSession?: string;\n byChannel: Record<string, number>;\n}\n\nexport type SessionType = 'chat' | 'workflow-run' | 'workflow-subagent' | 'cron' | 'heartbeat';\n\n/** Session metadata (stored in index) */\nexport interface SessionMetadata {\n key: string;\n name?: string;\n status: SessionStatus;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n lastAccessedAt: string;\n messageCount: number;\n estimatedTokens: number;\n compactedCount: number;\n lastFlushedAt?: string;\n flushCount?: number;\n sourceChannel: string;\n sourceChatId: string;\n customData?: Record<string, unknown>;\n /** Optional project grouping for sessions, goals, workflows, and related context. */\n projectId?: string;\n /** Routing metadata */\n routing?: SessionRoutingMeta;\n /** Session statistics */\n stats?: SessionStats;\n /**\n * High-level origin for filtering/UI (e.g. `cron`, `heartbeat`, `webchat`).\n * Distinct from `sourceChannel` (routing namespace).\n */\n sessionType: SessionType;\n hiddenFromSessionList?: boolean;\n parentSessionKey?: string;\n workflowRunId?: string;\n workflowDefinitionId?: string;\n workflowAgentId?: string;\n workflowAgentLabel?: string;\n /** Active OpenClaw-style session instance id. Rotates on reset while `key` stays stable. */\n sessionId?: string;\n /** Workspace/cwd recorded for the active transcript. */\n cwd?: string;\n /** First activity time for this session row (ISO), from transcript header when available. */\n sessionStartedAt?: string;\n /** Last transcript write / interaction (ISO), updated on each persist. */\n lastInteractionAt?: string;\n /**\n * Epoch ms when the last webchat run was aborted (`POST /api/agent/abort`).\n * Used with `clientCreatedAtMs` on the next POST /api/agent to drop stale queued sends.\n */\n abortCutoffTimestamp?: number;\n}\n\n/** Summary of wrapped transcript (no duplicate message bodies). */\nexport interface SessionTranscriptSummary {\n id: string;\n version: number;\n createdAt: string;\n updatedAt: string;\n compactionCount: number;\n}\n\n/** Session detail (metadata + messages) */\nexport interface SessionDetail extends SessionMetadata {\n messages: Message[];\n /** Present when loaded with `includeTranscriptSummary` (gateway `?include=transcript`). */\n transcriptSummary?: SessionTranscriptSummary;\n /** Present when loaded with `include=transcriptRows` (full on-disk rows, LLM + `kind: 'context'`). */\n transcriptRows?: TranscriptStoredRow[];\n}\n\n/** Session list query parameters */\nexport interface SessionListQuery {\n status?: SessionStatus | SessionStatus[];\n /** Single `sourceChannel`, or comma-separated list (e.g. `telegram,weixin`). */\n channel?: string;\n tags?: string[];\n search?: string;\n sessionTypes?: SessionType[];\n includeHidden?: boolean;\n projectId?: string;\n unassigned?: boolean;\n updatedAfter?: number;\n includePinned?: boolean;\n includeSessionKey?: string;\n sortBy?: 'updatedAt' | 'createdAt' | 'messageCount' | 'lastAccessedAt';\n sortOrder?: 'asc' | 'desc';\n limit?: number;\n offset?: number;\n}\n\n/** Paginated result */\nexport interface PaginatedResult<T> {\n items: T[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\n/** Export format */\nexport type ExportFormat = 'json' | 'markdown';\n\n/** Session export data */\nexport interface SessionExport {\n version: string;\n exportedAt: string;\n metadata: SessionMetadata;\n messages: Message[];\n /** Full on-disk rows (LLM + `kind: 'context'`) for audit / round-trip. */\n transcriptRows: TranscriptStoredRow[];\n}\n\n/** On-disk pre-compaction snapshot (OpenClaw-style checkpoint list). */\nexport interface CompactionCheckpointSummary {\n id: string;\n sizeBytes: number;\n modifiedAt: string;\n}\n\nexport interface CompactionCheckpointDetail extends CompactionCheckpointSummary {\n messageCount: number;\n}\n"],"mappings":";;AA4CA,IAAY,gBAAL,yBAAA,eAAA;AACL,eAAA,YAAA;AACA,eAAA,UAAA;AACA,eAAA,cAAA;AACA,eAAA,YAAA;;KACD"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ActivityEventWithRelations, ActivityListResult, ActivityObjectRef, CreateObjectLinkInput, ListActivityOptions, ListObjectActivityOptions, ListProjectActivityOptions, ObjectLink, RecordActivityInput } from '../../activity/types.js';
|
|
2
|
+
export declare function recordActivityEvent(input: RecordActivityInput): ActivityEventWithRelations;
|
|
3
|
+
export declare function getActivityEventRecord(id: string): ActivityEventWithRelations | null;
|
|
4
|
+
export declare function listActivityRecords(options?: ListActivityOptions): ActivityListResult;
|
|
5
|
+
export declare function listObjectActivityRecords(options: ListObjectActivityOptions): ActivityListResult;
|
|
6
|
+
export declare function listProjectActivityRecords(options: ListProjectActivityOptions): ActivityListResult;
|
|
7
|
+
export declare function createObjectLinkRecord(input: CreateObjectLinkInput): ObjectLink;
|
|
8
|
+
export declare function listObjectLinkRecords(object: ActivityObjectRef): ObjectLink[];
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { getSqliteDatabase, runSqliteWriteTransaction } from "./transaction.js";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
//#region src/storage/sqlite/activity-repository.ts
|
|
4
|
+
function parseJson(json, fallback) {
|
|
5
|
+
try {
|
|
6
|
+
return JSON.parse(json);
|
|
7
|
+
} catch {
|
|
8
|
+
return fallback;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function eventFromRow(row) {
|
|
12
|
+
return {
|
|
13
|
+
id: row.id,
|
|
14
|
+
type: row.type,
|
|
15
|
+
primaryObject: {
|
|
16
|
+
kind: row.primary_object_kind,
|
|
17
|
+
id: row.primary_object_id,
|
|
18
|
+
title: row.primary_object_title ?? void 0
|
|
19
|
+
},
|
|
20
|
+
actor: parseJson(row.actor_json, { kind: "system" }),
|
|
21
|
+
initiator: row.initiator_json ? parseJson(row.initiator_json, void 0) : void 0,
|
|
22
|
+
source: parseJson(row.source_json, { kind: "system" }),
|
|
23
|
+
payload: parseJson(row.payload_json, {}),
|
|
24
|
+
visibility: row.visibility,
|
|
25
|
+
importance: row.importance,
|
|
26
|
+
createdAt: row.created_at
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function scopeFromRow(row) {
|
|
30
|
+
return {
|
|
31
|
+
activityId: row.activity_id,
|
|
32
|
+
scopeKind: row.scope_kind,
|
|
33
|
+
scopeId: row.scope_id,
|
|
34
|
+
reason: row.reason
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function relatedProjectFromRow(row) {
|
|
38
|
+
return {
|
|
39
|
+
activityId: row.activity_id,
|
|
40
|
+
projectId: row.project_id,
|
|
41
|
+
reason: row.reason,
|
|
42
|
+
confidence: row.confidence,
|
|
43
|
+
computedAt: row.computed_at
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function objectLinkFromRow(row) {
|
|
47
|
+
return {
|
|
48
|
+
id: row.id,
|
|
49
|
+
from: {
|
|
50
|
+
kind: row.from_kind,
|
|
51
|
+
id: row.from_id,
|
|
52
|
+
title: row.from_title ?? void 0
|
|
53
|
+
},
|
|
54
|
+
to: {
|
|
55
|
+
kind: row.to_kind,
|
|
56
|
+
id: row.to_id,
|
|
57
|
+
title: row.to_title ?? void 0
|
|
58
|
+
},
|
|
59
|
+
relation: row.relation,
|
|
60
|
+
source: row.source,
|
|
61
|
+
createdAt: row.created_at
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function listScopesForActivity(activityId) {
|
|
65
|
+
return getSqliteDatabase().prepare(`SELECT activity_id, scope_kind, scope_id, reason
|
|
66
|
+
FROM activity_scopes
|
|
67
|
+
WHERE activity_id = ?
|
|
68
|
+
ORDER BY scope_kind ASC, scope_id ASC, reason ASC`).all(activityId).map((row) => scopeFromRow(row));
|
|
69
|
+
}
|
|
70
|
+
function listRelatedProjectsForActivity(activityId) {
|
|
71
|
+
return getSqliteDatabase().prepare(`SELECT activity_id, project_id, reason, confidence, computed_at
|
|
72
|
+
FROM activity_related_projects
|
|
73
|
+
WHERE activity_id = ?
|
|
74
|
+
ORDER BY project_id ASC, reason ASC`).all(activityId).map((row) => relatedProjectFromRow(row));
|
|
75
|
+
}
|
|
76
|
+
function eventWithRelations(row) {
|
|
77
|
+
const event = eventFromRow(row);
|
|
78
|
+
return {
|
|
79
|
+
...event,
|
|
80
|
+
scopes: listScopesForActivity(event.id),
|
|
81
|
+
relatedProjects: listRelatedProjectsForActivity(event.id)
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function normalizeLimit(limit) {
|
|
85
|
+
return Math.max(1, Math.min(100, Math.floor(limit ?? 50)));
|
|
86
|
+
}
|
|
87
|
+
function normalizeOffset(offset) {
|
|
88
|
+
return Math.max(0, Math.floor(offset ?? 0));
|
|
89
|
+
}
|
|
90
|
+
function visibilityClause(visibility, params) {
|
|
91
|
+
if (!visibility) return "";
|
|
92
|
+
params.push(visibility);
|
|
93
|
+
return "WHERE visibility = ?";
|
|
94
|
+
}
|
|
95
|
+
function listResult(rows, total, limit, offset) {
|
|
96
|
+
return {
|
|
97
|
+
items: rows.map(eventWithRelations),
|
|
98
|
+
total,
|
|
99
|
+
limit,
|
|
100
|
+
offset,
|
|
101
|
+
hasMore: offset + rows.length < total
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function recordActivityEvent(input) {
|
|
105
|
+
const id = input.id ?? randomUUID();
|
|
106
|
+
const now = input.nowMs ?? Date.now();
|
|
107
|
+
const visibility = input.visibility ?? "timeline";
|
|
108
|
+
const importance = input.importance ?? "normal";
|
|
109
|
+
const payload = input.payload ?? {};
|
|
110
|
+
runSqliteWriteTransaction((db) => {
|
|
111
|
+
db.prepare(`INSERT INTO activity_events (
|
|
112
|
+
id, type, primary_object_kind, primary_object_id, primary_object_title,
|
|
113
|
+
actor_json, initiator_json, source_json, payload_json,
|
|
114
|
+
visibility, importance, created_at
|
|
115
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.type, input.primaryObject.kind, input.primaryObject.id, input.primaryObject.title ?? null, JSON.stringify(input.actor), input.initiator ? JSON.stringify(input.initiator) : null, JSON.stringify(input.source), JSON.stringify(payload), visibility, importance, now);
|
|
116
|
+
for (const scope of input.scopes ?? []) db.prepare(`INSERT OR IGNORE INTO activity_scopes (activity_id, scope_kind, scope_id, reason)
|
|
117
|
+
VALUES (?, ?, ?, ?)`).run(id, scope.scopeKind, scope.scopeId, scope.reason);
|
|
118
|
+
for (const related of input.relatedProjects ?? []) db.prepare(`INSERT OR REPLACE INTO activity_related_projects (
|
|
119
|
+
activity_id, project_id, reason, confidence, computed_at
|
|
120
|
+
) VALUES (?, ?, ?, ?, ?)`).run(id, related.projectId, related.reason, related.confidence, related.computedAt ?? now);
|
|
121
|
+
});
|
|
122
|
+
const event = getActivityEventRecord(id);
|
|
123
|
+
if (!event) throw new Error(`Activity event was not persisted: ${id}`);
|
|
124
|
+
return event;
|
|
125
|
+
}
|
|
126
|
+
function getActivityEventRecord(id) {
|
|
127
|
+
const row = getSqliteDatabase().prepare(`SELECT *
|
|
128
|
+
FROM activity_events
|
|
129
|
+
WHERE id = ?`).get(id);
|
|
130
|
+
return row ? eventWithRelations(row) : null;
|
|
131
|
+
}
|
|
132
|
+
function listActivityRecords(options = {}) {
|
|
133
|
+
const limit = normalizeLimit(options.limit);
|
|
134
|
+
const offset = normalizeOffset(options.offset);
|
|
135
|
+
const params = [];
|
|
136
|
+
const where = visibilityClause(options.visibility, params);
|
|
137
|
+
const total = getSqliteDatabase().prepare(`SELECT COUNT(*) AS count FROM activity_events ${where}`).get(...params).count;
|
|
138
|
+
return listResult(getSqliteDatabase().prepare(`SELECT *
|
|
139
|
+
FROM activity_events
|
|
140
|
+
${where}
|
|
141
|
+
ORDER BY created_at DESC, id DESC
|
|
142
|
+
LIMIT ? OFFSET ?`).all(...params, limit, offset).map((row) => row), total, limit, offset);
|
|
143
|
+
}
|
|
144
|
+
function listObjectActivityRecords(options) {
|
|
145
|
+
const limit = normalizeLimit(options.limit);
|
|
146
|
+
const offset = normalizeOffset(options.offset);
|
|
147
|
+
const params = [options.object.kind, options.object.id];
|
|
148
|
+
const visibility = options.visibility ? "AND visibility = ?" : "";
|
|
149
|
+
if (options.visibility) params.push(options.visibility);
|
|
150
|
+
const total = getSqliteDatabase().prepare(`SELECT COUNT(*) AS count
|
|
151
|
+
FROM activity_events
|
|
152
|
+
WHERE primary_object_kind = ? AND primary_object_id = ?
|
|
153
|
+
${visibility}`).get(...params).count;
|
|
154
|
+
return listResult(getSqliteDatabase().prepare(`SELECT *
|
|
155
|
+
FROM activity_events
|
|
156
|
+
WHERE primary_object_kind = ? AND primary_object_id = ?
|
|
157
|
+
${visibility}
|
|
158
|
+
ORDER BY created_at DESC, id DESC
|
|
159
|
+
LIMIT ? OFFSET ?`).all(...params, limit, offset).map((row) => row), total, limit, offset);
|
|
160
|
+
}
|
|
161
|
+
function listProjectActivityRecords(options) {
|
|
162
|
+
const limit = normalizeLimit(options.limit);
|
|
163
|
+
const offset = normalizeOffset(options.offset);
|
|
164
|
+
const params = ["project", options.projectId];
|
|
165
|
+
const relatedClause = options.includeRelated ? `OR EXISTS (
|
|
166
|
+
SELECT 1 FROM activity_related_projects ar
|
|
167
|
+
WHERE ar.activity_id = ae.id AND ar.project_id = ?
|
|
168
|
+
)` : "";
|
|
169
|
+
if (options.includeRelated) params.push(options.projectId);
|
|
170
|
+
const visibilityClauseSql = options.visibility ? "AND ae.visibility = ?" : "";
|
|
171
|
+
if (options.visibility) params.push(options.visibility);
|
|
172
|
+
const baseWhere = `EXISTS (
|
|
173
|
+
SELECT 1 FROM activity_scopes s
|
|
174
|
+
WHERE s.activity_id = ae.id AND s.scope_kind = ? AND s.scope_id = ?
|
|
175
|
+
) ${relatedClause}`;
|
|
176
|
+
const total = getSqliteDatabase().prepare(`SELECT COUNT(*) AS count
|
|
177
|
+
FROM activity_events ae
|
|
178
|
+
WHERE (${baseWhere})
|
|
179
|
+
${visibilityClauseSql}`).get(...params).count;
|
|
180
|
+
return listResult(getSqliteDatabase().prepare(`SELECT ae.*
|
|
181
|
+
FROM activity_events ae
|
|
182
|
+
WHERE (${baseWhere})
|
|
183
|
+
${visibilityClauseSql}
|
|
184
|
+
ORDER BY ae.created_at DESC, ae.id DESC
|
|
185
|
+
LIMIT ? OFFSET ?`).all(...params, limit, offset).map((row) => row), total, limit, offset);
|
|
186
|
+
}
|
|
187
|
+
function createObjectLinkRecord(input) {
|
|
188
|
+
const id = input.id ?? randomUUID();
|
|
189
|
+
const now = input.nowMs ?? Date.now();
|
|
190
|
+
runSqliteWriteTransaction((db) => {
|
|
191
|
+
db.prepare(`INSERT INTO object_links (
|
|
192
|
+
id, from_kind, from_id, from_title, to_kind, to_id, to_title, relation, source, created_at
|
|
193
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.from.kind, input.from.id, input.from.title ?? null, input.to.kind, input.to.id, input.to.title ?? null, input.relation, input.source, now);
|
|
194
|
+
});
|
|
195
|
+
const row = getSqliteDatabase().prepare(`SELECT * FROM object_links WHERE id = ?`).get(id);
|
|
196
|
+
if (!row) throw new Error(`Object link was not persisted: ${id}`);
|
|
197
|
+
return objectLinkFromRow(row);
|
|
198
|
+
}
|
|
199
|
+
function listObjectLinkRecords(object) {
|
|
200
|
+
return getSqliteDatabase().prepare(`SELECT *
|
|
201
|
+
FROM object_links
|
|
202
|
+
WHERE (from_kind = ? AND from_id = ?) OR (to_kind = ? AND to_id = ?)
|
|
203
|
+
ORDER BY created_at DESC, id DESC`).all(object.kind, object.id, object.kind, object.id).map((row) => objectLinkFromRow(row));
|
|
204
|
+
}
|
|
205
|
+
//#endregion
|
|
206
|
+
export { createObjectLinkRecord, getActivityEventRecord, listActivityRecords, listObjectActivityRecords, listObjectLinkRecords, listProjectActivityRecords, recordActivityEvent };
|
|
207
|
+
|
|
208
|
+
//# sourceMappingURL=activity-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-repository.js","names":[],"sources":["../../../../src/storage/sqlite/activity-repository.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type {\n ActivityEvent,\n ActivityEventWithRelations,\n ActivityListResult,\n ActivityObjectRef,\n ActivityRelatedProject,\n ActivityScope,\n ActivitySource,\n ActivityPrincipal,\n CreateObjectLinkInput,\n ListActivityOptions,\n ListObjectActivityOptions,\n ListProjectActivityOptions,\n ObjectLink,\n RecordActivityInput,\n} from '../../activity/types.js';\nimport { getSqliteDatabase, runSqliteWriteTransaction } from './transaction.js';\n\ntype ActivityEventRow = {\n id: string;\n type: string;\n primary_object_kind: string;\n primary_object_id: string;\n primary_object_title: string | null;\n actor_json: string;\n initiator_json: string | null;\n source_json: string;\n payload_json: string;\n visibility: string;\n importance: string;\n created_at: number;\n};\n\ntype ActivityScopeRow = {\n activity_id: string;\n scope_kind: string;\n scope_id: string;\n reason: string;\n};\n\ntype ActivityRelatedProjectRow = {\n activity_id: string;\n project_id: string;\n reason: string;\n confidence: number;\n computed_at: number;\n};\n\ntype ObjectLinkRow = {\n id: string;\n from_kind: string;\n from_id: string;\n from_title: string | null;\n to_kind: string;\n to_id: string;\n to_title: string | null;\n relation: string;\n source: string;\n created_at: number;\n};\n\ntype CountRow = { count: number };\ntype SqlParam = string | number | bigint | null;\n\nfunction parseJson<T>(json: string, fallback: T): T {\n try {\n return JSON.parse(json) as T;\n } catch {\n return fallback;\n }\n}\n\nfunction eventFromRow(row: ActivityEventRow): ActivityEvent {\n return {\n id: row.id,\n type: row.type,\n primaryObject: {\n kind: row.primary_object_kind as ActivityObjectRef['kind'],\n id: row.primary_object_id,\n title: row.primary_object_title ?? undefined,\n },\n actor: parseJson<ActivityPrincipal>(row.actor_json, { kind: 'system' }),\n initiator: row.initiator_json ? parseJson<ActivityPrincipal | undefined>(row.initiator_json, undefined) : undefined,\n source: parseJson<ActivitySource>(row.source_json, { kind: 'system' }),\n payload: parseJson<Record<string, unknown>>(row.payload_json, {}),\n visibility: row.visibility as ActivityEvent['visibility'],\n importance: row.importance as ActivityEvent['importance'],\n createdAt: row.created_at,\n };\n}\n\nfunction scopeFromRow(row: ActivityScopeRow): ActivityScope {\n return {\n activityId: row.activity_id,\n scopeKind: row.scope_kind as ActivityScope['scopeKind'],\n scopeId: row.scope_id,\n reason: row.reason as ActivityScope['reason'],\n };\n}\n\nfunction relatedProjectFromRow(row: ActivityRelatedProjectRow): ActivityRelatedProject {\n return {\n activityId: row.activity_id,\n projectId: row.project_id,\n reason: row.reason as ActivityRelatedProject['reason'],\n confidence: row.confidence,\n computedAt: row.computed_at,\n };\n}\n\nfunction objectLinkFromRow(row: ObjectLinkRow): ObjectLink {\n return {\n id: row.id,\n from: {\n kind: row.from_kind as ActivityObjectRef['kind'],\n id: row.from_id,\n title: row.from_title ?? undefined,\n },\n to: {\n kind: row.to_kind as ActivityObjectRef['kind'],\n id: row.to_id,\n title: row.to_title ?? undefined,\n },\n relation: row.relation as ObjectLink['relation'],\n source: row.source as ObjectLink['source'],\n createdAt: row.created_at,\n };\n}\n\nfunction listScopesForActivity(activityId: string): ActivityScope[] {\n return getSqliteDatabase()\n .prepare(\n `SELECT activity_id, scope_kind, scope_id, reason\n FROM activity_scopes\n WHERE activity_id = ?\n ORDER BY scope_kind ASC, scope_id ASC, reason ASC`,\n )\n .all(activityId)\n .map((row) => scopeFromRow(row as ActivityScopeRow));\n}\n\nfunction listRelatedProjectsForActivity(activityId: string): ActivityRelatedProject[] {\n return getSqliteDatabase()\n .prepare(\n `SELECT activity_id, project_id, reason, confidence, computed_at\n FROM activity_related_projects\n WHERE activity_id = ?\n ORDER BY project_id ASC, reason ASC`,\n )\n .all(activityId)\n .map((row) => relatedProjectFromRow(row as ActivityRelatedProjectRow));\n}\n\nfunction eventWithRelations(row: ActivityEventRow): ActivityEventWithRelations {\n const event = eventFromRow(row);\n return {\n ...event,\n scopes: listScopesForActivity(event.id),\n relatedProjects: listRelatedProjectsForActivity(event.id),\n };\n}\n\nfunction normalizeLimit(limit: number | undefined): number {\n return Math.max(1, Math.min(100, Math.floor(limit ?? 50)));\n}\n\nfunction normalizeOffset(offset: number | undefined): number {\n return Math.max(0, Math.floor(offset ?? 0));\n}\n\nfunction visibilityClause(visibility: string | undefined, params: SqlParam[]): string {\n if (!visibility) return '';\n params.push(visibility);\n return 'WHERE visibility = ?';\n}\n\nfunction listResult(rows: ActivityEventRow[], total: number, limit: number, offset: number): ActivityListResult {\n return {\n items: rows.map(eventWithRelations),\n total,\n limit,\n offset,\n hasMore: offset + rows.length < total,\n };\n}\n\nexport function recordActivityEvent(input: RecordActivityInput): ActivityEventWithRelations {\n const id = input.id ?? randomUUID();\n const now = input.nowMs ?? Date.now();\n const visibility = input.visibility ?? 'timeline';\n const importance = input.importance ?? 'normal';\n const payload = input.payload ?? {};\n\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO activity_events (\n id, type, primary_object_kind, primary_object_id, primary_object_title,\n actor_json, initiator_json, source_json, payload_json,\n visibility, importance, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n id,\n input.type,\n input.primaryObject.kind,\n input.primaryObject.id,\n input.primaryObject.title ?? null,\n JSON.stringify(input.actor),\n input.initiator ? JSON.stringify(input.initiator) : null,\n JSON.stringify(input.source),\n JSON.stringify(payload),\n visibility,\n importance,\n now,\n );\n\n for (const scope of input.scopes ?? []) {\n db.prepare(\n `INSERT OR IGNORE INTO activity_scopes (activity_id, scope_kind, scope_id, reason)\n VALUES (?, ?, ?, ?)`,\n ).run(id, scope.scopeKind, scope.scopeId, scope.reason);\n }\n\n for (const related of input.relatedProjects ?? []) {\n db.prepare(\n `INSERT OR REPLACE INTO activity_related_projects (\n activity_id, project_id, reason, confidence, computed_at\n ) VALUES (?, ?, ?, ?, ?)`,\n ).run(id, related.projectId, related.reason, related.confidence, related.computedAt ?? now);\n }\n });\n\n const event = getActivityEventRecord(id);\n if (!event) {\n throw new Error(`Activity event was not persisted: ${id}`);\n }\n return event;\n}\n\nexport function getActivityEventRecord(id: string): ActivityEventWithRelations | null {\n const row = getSqliteDatabase()\n .prepare(\n `SELECT *\n FROM activity_events\n WHERE id = ?`,\n )\n .get(id) as ActivityEventRow | undefined;\n return row ? eventWithRelations(row) : null;\n}\n\nexport function listActivityRecords(options: ListActivityOptions = {}): ActivityListResult {\n const limit = normalizeLimit(options.limit);\n const offset = normalizeOffset(options.offset);\n const params: SqlParam[] = [];\n const where = visibilityClause(options.visibility, params);\n const total = (getSqliteDatabase()\n .prepare(`SELECT COUNT(*) AS count FROM activity_events ${where}`)\n .get(...params) as CountRow).count;\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT *\n FROM activity_events\n ${where}\n ORDER BY created_at DESC, id DESC\n LIMIT ? OFFSET ?`,\n )\n .all(...params, limit, offset)\n .map((row) => row as ActivityEventRow);\n return listResult(rows, total, limit, offset);\n}\n\nexport function listObjectActivityRecords(options: ListObjectActivityOptions): ActivityListResult {\n const limit = normalizeLimit(options.limit);\n const offset = normalizeOffset(options.offset);\n const params: SqlParam[] = [options.object.kind, options.object.id];\n const visibility = options.visibility ? 'AND visibility = ?' : '';\n if (options.visibility) params.push(options.visibility);\n const total = (getSqliteDatabase()\n .prepare(\n `SELECT COUNT(*) AS count\n FROM activity_events\n WHERE primary_object_kind = ? AND primary_object_id = ?\n ${visibility}`,\n )\n .get(...params) as CountRow).count;\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT *\n FROM activity_events\n WHERE primary_object_kind = ? AND primary_object_id = ?\n ${visibility}\n ORDER BY created_at DESC, id DESC\n LIMIT ? OFFSET ?`,\n )\n .all(...params, limit, offset)\n .map((row) => row as ActivityEventRow);\n return listResult(rows, total, limit, offset);\n}\n\nexport function listProjectActivityRecords(options: ListProjectActivityOptions): ActivityListResult {\n const limit = normalizeLimit(options.limit);\n const offset = normalizeOffset(options.offset);\n const params: SqlParam[] = ['project', options.projectId];\n const relatedClause = options.includeRelated\n ? `OR EXISTS (\n SELECT 1 FROM activity_related_projects ar\n WHERE ar.activity_id = ae.id AND ar.project_id = ?\n )`\n : '';\n if (options.includeRelated) params.push(options.projectId);\n const visibilityClauseSql = options.visibility ? 'AND ae.visibility = ?' : '';\n if (options.visibility) params.push(options.visibility);\n\n const baseWhere = `EXISTS (\n SELECT 1 FROM activity_scopes s\n WHERE s.activity_id = ae.id AND s.scope_kind = ? AND s.scope_id = ?\n ) ${relatedClause}`;\n\n const total = (getSqliteDatabase()\n .prepare(\n `SELECT COUNT(*) AS count\n FROM activity_events ae\n WHERE (${baseWhere})\n ${visibilityClauseSql}`,\n )\n .get(...params) as CountRow).count;\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT ae.*\n FROM activity_events ae\n WHERE (${baseWhere})\n ${visibilityClauseSql}\n ORDER BY ae.created_at DESC, ae.id DESC\n LIMIT ? OFFSET ?`,\n )\n .all(...params, limit, offset)\n .map((row) => row as ActivityEventRow);\n return listResult(rows, total, limit, offset);\n}\n\nexport function createObjectLinkRecord(input: CreateObjectLinkInput): ObjectLink {\n const id = input.id ?? randomUUID();\n const now = input.nowMs ?? Date.now();\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO object_links (\n id, from_kind, from_id, from_title, to_kind, to_id, to_title, relation, source, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n id,\n input.from.kind,\n input.from.id,\n input.from.title ?? null,\n input.to.kind,\n input.to.id,\n input.to.title ?? null,\n input.relation,\n input.source,\n now,\n );\n });\n const row = getSqliteDatabase()\n .prepare(`SELECT * FROM object_links WHERE id = ?`)\n .get(id) as ObjectLinkRow | undefined;\n if (!row) {\n throw new Error(`Object link was not persisted: ${id}`);\n }\n return objectLinkFromRow(row);\n}\n\nexport function listObjectLinkRecords(object: ActivityObjectRef): ObjectLink[] {\n return getSqliteDatabase()\n .prepare(\n `SELECT *\n FROM object_links\n WHERE (from_kind = ? AND from_id = ?) OR (to_kind = ? AND to_id = ?)\n ORDER BY created_at DESC, id DESC`,\n )\n .all(object.kind, object.id, object.kind, object.id)\n .map((row) => objectLinkFromRow(row as ObjectLinkRow));\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { resolveXopcDatabasePath } from "../../config/paths-state.js";
|
|
2
2
|
import { createLogger } from "../../utils/logger/index.js";
|
|
3
3
|
import { init_logger } from "../../utils/logger.js";
|
|
4
|
+
import { ensureXopcDatabaseSchema } from "./schema.js";
|
|
4
5
|
import { requireNodeSqlite } from "../../infra/node-sqlite.js";
|
|
5
6
|
import { installSqliteTransientRejectionHandler } from "../../infra/unhandled-rejections.js";
|
|
6
|
-
import { ensureXopcDatabaseSchema } from "./schema.js";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import fs from "node:fs";
|
|
9
9
|
import childProcess from "node:child_process";
|
|
@@ -14,3 +14,4 @@ export { deleteNoteAgentContextRecord, getNoteAgentContextRecord, upsertNoteAgen
|
|
|
14
14
|
export { resolveAgentIdFromMemoriesDir, searchMemoryIndex, syncMemoryIndex, type MemorySearchHit, } from './memory-index-repository.js';
|
|
15
15
|
export { appendMemorySignal, appendMemoryTraceEvent, deleteMemoryRecord, getMemoryProviderState, getMemoryRecord, listMemorySignals, listMemoryTraceEvents, listMemoryRecords, searchMemoryRecords, setMemoryProviderState, setMemoryTraceFeedback, summarizeMemoryRecallFeedback, upsertMemoryRecord, type AppendMemorySignalInput, type AppendMemoryTraceEventInput, type ListMemoryRecordsOptions, type MemoryRecallFeedbackSummary, type MemorySignalRowPayload, type MemoryTraceFeedback, type MemoryTraceEventPayload, type SearchMemoryRecordsOptions, type SetMemoryTraceFeedbackInput, type UpsertMemoryRecordInput, } from './memory-records-repository.js';
|
|
16
16
|
export { deleteSessionConfig, getSessionConfig, hasSessionConfig, setSessionConfig, updateSessionConfig, } from './config-repository.js';
|
|
17
|
+
export { createObjectLinkRecord, getActivityEventRecord, listActivityRecords, listObjectActivityRecords, listObjectLinkRecords, listProjectActivityRecords, recordActivityEvent, } from './activity-repository.js';
|