@xopcai/xopc 0.0.127 → 0.0.129
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CgGgO4OV.js → agent-avatar-dicebear-DHzfg5D-.js} +1 -1
- package/dist/gateway/static/root/assets/agents-CMxKKFQD.js +2 -0
- package/dist/gateway/static/root/assets/app-management-settings-panel-ptk9c5BN.js +1 -0
- package/dist/gateway/static/root/assets/{appearance-settings-BnHQW7Hv.js → appearance-settings-bYeEj3IW.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-DdZvDf5B.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-5NXT7Zqv.js → archive-plugin-Dy6lsTHw.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-DghwhYe0.js → attachment-load-qOtBYR6Y.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-DYmESgwl.js → automation-suggestion-card-BFH-RfaV.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-B8JTjkCr.js +4 -0
- package/dist/gateway/static/root/assets/{binary-plugins-DSbPKdWX.js → binary-plugins-DJvi_thc.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-CoZ-_np-.js → block-editor-DlMnizlt.js} +4 -4
- package/dist/gateway/static/root/assets/browser-settings-page-slHLiZgi.js +8 -0
- package/dist/gateway/static/root/assets/capability-presets-api-D4q7vHVJ.js +1 -0
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-Dng8Grpv.js +1 -0
- package/dist/gateway/static/root/assets/channel-recipient-api-BrW5Kcgc.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-KIsISXw0.js +1 -0
- package/dist/gateway/static/root/assets/{code-editor-gbRgS14t.js → code-editor--tMO38Tr.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-C5fx4Qs9.js +2 -0
- package/dist/gateway/static/root/assets/desktop-pet-Jmdn6q47.js +1 -0
- package/dist/gateway/static/root/assets/{desktop-pet-settings-npJ0Oh5p.js → desktop-pet-settings-B-u63N4e.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-BR2ItBuf.js → directory-picker-path-field-8p1ehIHn.js} +1 -1
- package/dist/gateway/static/root/assets/dreaming-settings-CCkKP-I4.js +3 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-Tl0f1rNv.js → extension-debug-page-DJ1ABK95.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-Ck7QuwE3.js → extension-page-CP9P2lGM.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-DnhU-tx0.js +1 -0
- package/dist/gateway/static/root/assets/fetch-BMjlW0g1.js +1 -0
- package/dist/gateway/static/root/assets/gateway-config-api-CjxDn46s.js +1 -0
- package/dist/gateway/static/root/assets/gateway-settings-TING7224.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-Yh8h3hkj.js → gateway-startup-retry-CY5W2Pp4.js} +1 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-2lPbGfp3.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-DHWVRafk.js +4 -0
- package/dist/gateway/static/root/assets/goals-config-api-BqhuxG8P.js +1 -0
- package/dist/gateway/static/root/assets/goals-page-CMk-9ZYN.js +2 -0
- package/dist/gateway/static/root/assets/goals-settings-BHzOAU_l.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-BQIm0P3V.js +1 -0
- package/dist/gateway/static/root/assets/{html-workspace-editor-DV90ddkE.js → html-workspace-editor-COqmtESs.js} +1 -1
- package/dist/gateway/static/root/assets/index-C4k6QSXw.js +257 -0
- package/dist/gateway/static/root/assets/index-EKmpPvZj.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CfQrt_Rb.js → keyboard-shortcuts-settings-BrENZzM7.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-BiL302Ve.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-editor-BFOjBBla.js → markdown-editor-jD1LPiP-.js} +2 -2
- package/dist/gateway/static/root/assets/{markdown-split-BwiMdCf_.js → markdown-split-BYF4ReGM.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-DhoizyZv.js → media-plugins-CpQDz-QJ.js} +1 -1
- package/dist/gateway/static/root/assets/messages-YzjnURPJ.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-CFPpl3Cq.js +2 -0
- package/dist/gateway/static/root/assets/notes-page-BurgcdVZ.js +1 -0
- package/dist/gateway/static/root/assets/notes-workbench-BAELRM_O.js +2 -0
- package/dist/gateway/static/root/assets/{page-tabs-DDC2t4Z8.js → page-tabs-RnYYOIWO.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-BTmk-XDM.js → preview-open-alternatives-CTRt06Ri.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-iXX1Ceo2.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-GDVTFAY7.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-BUrbUnwv.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-DqU-yKIh.js → save-bar-controls-c4pZeikq.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-CUttkoSB.js → schedule-field-Cb6R7iuP.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-CVSQA9nI.js → schema-form-BqET_hEP.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-CiOreA2k.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-C-qvptlu.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-CK7ogS-P.js → settings-collapsible-section-DuOvqNsI.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-Bugp38Fa.js → settings-form-section-DptSsbWd.js} +1 -1
- package/dist/gateway/static/root/assets/settings-loading-skeleton-BlVfBCb9.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-layout-DBRWwdhh.js → settings-page-layout-DVt69dJJ.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-xQx2Pkg5.js +1 -0
- package/dist/gateway/static/root/assets/setup-status-panel-DWB8VdLK.js +1 -0
- package/dist/gateway/static/root/assets/share-preview-page-8m_ACH0z.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-CgxfhNxI.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-BBFUOxmf.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-bo695odu.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-DJ6YuLqR.js → system-settings-panel-CW22OkZ4.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-B0T7N8Km.js → typed-models-lib-BqpGAKxV.js} +1 -1
- package/dist/gateway/static/root/assets/url-DFGNSBNT.js +1 -0
- package/dist/gateway/static/root/assets/use-channel-catalog-CPdJ0o0i.js +1 -0
- package/dist/gateway/static/root/assets/user-profile-settings-panel-CERVLuaW.js +1 -0
- package/dist/gateway/static/root/assets/{utils-CehXcWAY.js → utils-BnmysurA.js} +1 -1
- package/dist/gateway/static/root/assets/vendor-codemirror-C1WKjCk5.js +45 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-Jr3jvPcB.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-B-8rex4u.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-c4jeaohQ.js → workflow-run-setup-panel-Ci9GSPnS.js} +2 -2
- package/dist/gateway/static/root/assets/{workflow-start-dialog-ENRHjoqa.js → workflow-start-dialog-BhDj8pqH.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-Brb3UGvs.js +18 -0
- package/dist/gateway/static/root/index.html +7 -6
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +3 -0
- package/dist/src/agent/agent-manager.js +14 -6
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/capabilities/definitions.d.ts +14 -0
- package/dist/src/agent/capabilities/definitions.js +19 -0
- package/dist/src/agent/capabilities/definitions.js.map +1 -1
- package/dist/src/agent/goals/post-turn.js +23 -2
- package/dist/src/agent/goals/post-turn.js.map +1 -1
- package/dist/src/agent/messaging/command-handler.d.ts +4 -0
- package/dist/src/agent/messaging/command-handler.js +6 -0
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/service.d.ts +2 -0
- package/dist/src/agent/service.js +23 -0
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/skills/hub-lock.d.ts +5 -5
- package/dist/src/agent/skills/hub-lock.js +12 -12
- package/dist/src/agent/skills/hub-lock.js.map +1 -1
- package/dist/src/agent/skills/hub-pull.d.ts +15 -0
- package/dist/src/agent/skills/hub-pull.js +299 -24
- package/dist/src/agent/skills/hub-pull.js.map +1 -1
- package/dist/src/agent/skills/index.js +4 -40
- package/dist/src/agent/skills/index.js.map +1 -1
- package/dist/src/agent/skills/install-target.d.ts +3 -0
- package/dist/src/agent/skills/install-target.js +11 -0
- package/dist/src/agent/skills/install-target.js.map +1 -0
- package/dist/src/agent/skills/managed-store.d.ts +5 -3
- package/dist/src/agent/skills/managed-store.js +10 -9
- package/dist/src/agent/skills/managed-store.js.map +1 -1
- package/dist/src/agent/skills/marketplace/adapters/store/adapter.js +5 -3
- package/dist/src/agent/skills/marketplace/adapters/store/adapter.js.map +1 -1
- package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.d.ts +21 -1
- package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.js +62 -6
- package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.js.map +1 -1
- package/dist/src/agent/skills/marketplace/resolve-adapter.js +1 -1
- package/dist/src/agent/skills/marketplace/resolve-adapter.js.map +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +2 -2
- package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
- package/dist/src/agent/skills/skill-manager.js +1 -6
- package/dist/src/agent/skills/skill-manager.js.map +1 -1
- package/dist/src/agent/skills/workspace-skills-dir.d.ts +1 -0
- package/dist/src/agent/skills/workspace-skills-dir.js +5 -1
- package/dist/src/agent/skills/workspace-skills-dir.js.map +1 -1
- package/dist/src/agent/tools/automation-tool.d.ts +6 -6
- package/dist/src/agent/tools/browser/tool/schemas.d.ts +20 -20
- package/dist/src/agent/tools/composio-tool.d.ts +3 -3
- package/dist/src/agent/tools/desktop-pet-tool.d.ts +7 -7
- package/dist/src/agent/tools/factory.d.ts +3 -0
- package/dist/src/agent/tools/factory.js +6 -1
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/goal-tool.d.ts +16 -16
- package/dist/src/agent/tools/index.d.ts +1 -0
- package/dist/src/agent/tools/index.js +2 -1
- package/dist/src/agent/tools/skill-install-tool.d.ts +28 -0
- package/dist/src/agent/tools/skill-install-tool.js +81 -0
- package/dist/src/agent/tools/skill-install-tool.js.map +1 -0
- package/dist/src/agent/tools/skill-manage-tool.js +3 -6
- package/dist/src/agent/tools/skill-manage-tool.js.map +1 -1
- package/dist/src/agent/tools/tool-manual-tool.d.ts +2 -2
- package/dist/src/agent/tools/xopc-use-tool.d.ts +5 -5
- package/dist/src/automations/domain/types.d.ts +1 -0
- package/dist/src/automations/draft/automation-draft-service.js +1 -1
- package/dist/src/automations/service/automation-service.js +9 -0
- package/dist/src/automations/service/automation-service.js.map +1 -1
- package/dist/src/browser/schemas.d.ts +52 -52
- package/dist/src/capabilities/store-connector.d.ts +17 -0
- package/dist/src/capabilities/store-connector.js +191 -0
- package/dist/src/capabilities/store-connector.js.map +1 -0
- package/dist/src/chat-commands/builtins/skill.d.ts +1 -0
- package/dist/src/chat-commands/builtins/skill.js +132 -0
- package/dist/src/chat-commands/builtins/skill.js.map +1 -0
- package/dist/src/chat-commands/context.d.ts +3 -0
- package/dist/src/chat-commands/context.js +2 -0
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/chat-commands/index.d.ts +1 -0
- package/dist/src/chat-commands/index.js +3 -1
- package/dist/src/chat-commands/index.js.map +1 -1
- package/dist/src/chat-commands/types.d.ts +17 -0
- package/dist/src/cli/command-loaders.js +1 -0
- package/dist/src/cli/command-loaders.js.map +1 -1
- package/dist/src/cli/command-manifest.js +4 -0
- package/dist/src/cli/command-manifest.js.map +1 -1
- package/dist/src/cli/commands/agent.js +2 -2
- package/dist/src/cli/commands/connectors.d.ts +4 -0
- package/dist/src/cli/commands/connectors.js +96 -0
- package/dist/src/cli/commands/connectors.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.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/skills.js +1 -1
- 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/index.js +1 -1
- package/dist/src/config/schema.js +2 -2
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/connectors/health.js +1 -1
- package/dist/src/connectors/install.js +3 -1
- package/dist/src/connectors/install.js.map +1 -1
- package/dist/src/connectors/materialize.js +2 -0
- package/dist/src/connectors/materialize.js.map +1 -1
- package/dist/src/connectors/types.d.ts +19 -1
- package/dist/src/extensions/install.js +1 -1
- package/dist/src/extensions/types/manifest.d.ts +1 -1
- package/dist/src/extensions/update.js +5 -3
- package/dist/src/extensions/update.js.map +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/chat-stream/mapper.js +5 -1
- package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
- package/dist/src/gateway/chat-stream/protocol.d.ts +2 -0
- package/dist/src/gateway/hono/lib/config-payload.js +1 -1
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/middleware/auth.d.ts +2 -2
- package/dist/src/gateway/hono/middleware/logger.d.ts +1 -1
- package/dist/src/gateway/hono/middleware/route-errors.d.ts +1 -1
- package/dist/src/gateway/hono/middleware/strict-rate-limit.d.ts +2 -2
- package/dist/src/gateway/hono/routes/capabilities.d.ts +3 -0
- package/dist/src/gateway/hono/routes/capabilities.js +79 -0
- package/dist/src/gateway/hono/routes/capabilities.js.map +1 -0
- package/dist/src/gateway/hono/routes/commands-skills.js +81 -3
- package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
- package/dist/src/gateway/hono/routes/connectors.js +2 -2
- package/dist/src/gateway/hono/routes/goals.js +212 -3
- package/dist/src/gateway/hono/routes/goals.js.map +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 +8 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/mcp.js +1 -1
- package/dist/src/gateway/hono/routes/mobile.d.ts +3 -0
- package/dist/src/gateway/hono/routes/mobile.js +113 -0
- package/dist/src/gateway/hono/routes/mobile.js.map +1 -0
- package/dist/src/gateway/hono/routes/projects.js +17 -0
- package/dist/src/gateway/hono/routes/projects.js.map +1 -1
- package/dist/src/gateway/hono/routes/workspace.js +4 -1
- package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
- package/dist/src/gateway/hono/sse.d.ts +3 -3
- package/dist/src/gateway/run-loop.js +1 -1
- package/dist/src/gateway/service/marketplace-service.d.ts +23 -1
- package/dist/src/gateway/service/marketplace-service.js +57 -6
- package/dist/src/gateway/service/marketplace-service.js.map +1 -1
- package/dist/src/gateway/service.d.ts +2 -0
- package/dist/src/gateway/service.js +12 -2
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/goals/goal-contract-draft.d.ts +15 -0
- package/dist/src/goals/goal-contract-draft.js +114 -0
- package/dist/src/goals/goal-contract-draft.js.map +1 -0
- package/dist/src/goals/goal-evidence-review.d.ts +15 -0
- package/dist/src/goals/goal-evidence-review.js +91 -0
- package/dist/src/goals/goal-evidence-review.js.map +1 -0
- package/dist/src/goals/goal-runner.js +3 -1
- package/dist/src/goals/goal-runner.js.map +1 -1
- package/dist/src/goals/goal-service.d.ts +17 -1
- package/dist/src/goals/goal-service.js +44 -3
- package/dist/src/goals/goal-service.js.map +1 -1
- package/dist/src/goals/goal-store.d.ts +18 -1
- package/dist/src/goals/goal-store.js +190 -1
- package/dist/src/goals/goal-store.js.map +1 -1
- package/dist/src/goals/index.d.ts +4 -0
- package/dist/src/goals/index.js +3 -1
- package/dist/src/goals/types.d.ts +42 -0
- package/dist/src/mcp/channel-bridge.js +1 -1
- package/dist/src/mobile/notification-service.d.ts +8 -0
- package/dist/src/mobile/notification-service.js +139 -0
- package/dist/src/mobile/notification-service.js.map +1 -0
- package/dist/src/mobile/notification-store.d.ts +28 -0
- package/dist/src/mobile/notification-store.js +134 -0
- package/dist/src/mobile/notification-store.js.map +1 -0
- package/dist/src/mobile/notification-types.d.ts +37 -0
- package/dist/src/mobile/notification-types.js +11 -0
- package/dist/src/mobile/notification-types.js.map +1 -0
- package/dist/src/storage/sqlite/migrations/026_goal_contracts.sql +10 -0
- package/dist/src/storage/sqlite/migrations/027_mobile_notifications.sql +43 -0
- package/dist/src/storage/sqlite/migrations/028_goal_evidence_requirements.sql +47 -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/schema.js +1 -1
- package/dist/src/storage/sqlite/schema.sql +44 -0
- package/dist/src/tui/tui-startup-resources.js +1 -1
- package/package.json +1 -1
- package/skills/THIRD_PARTY_NOTICES.md +16 -0
- package/skills/creative/algorithmic-art/SKILL.md +37 -0
- package/skills/creative/algorithmic-art/templates/generator-template.js +27 -0
- package/skills/creative/algorithmic-art/templates/viewer.html +27 -0
- package/skills/documents/doc-coauthoring/SKILL.md +40 -0
- package/skills/documents/doc-coauthoring/references/reader-review.md +9 -0
- package/skills/documents/docx/SKILL.md +40 -0
- package/skills/documents/docx/references/editing-checklist.md +8 -0
- package/skills/documents/docx/scripts/inspect_ooxml.py +36 -0
- package/skills/documents/docx/scripts/render_docx.py +33 -0
- package/skills/documents/pdf/SKILL.md +47 -0
- package/skills/documents/pdf/references/operations.md +16 -0
- package/skills/documents/pdf/scripts/inspect_pdf.py +41 -0
- package/skills/documents/pdf/scripts/render_pdf.py +32 -0
- package/skills/documents/pptx/SKILL.md +41 -0
- package/skills/documents/pptx/references/qa-checklist.md +6 -0
- package/skills/documents/pptx/scripts/inspect_ooxml.py +34 -0
- package/skills/documents/pptx/scripts/render_pptx.py +33 -0
- package/skills/engineering/define-goal/SKILL.md +41 -0
- package/skills/engineering/define-goal/references/objective-rubric.md +12 -0
- package/dist/gateway/static/root/assets/agents-k59l7NMb.js +0 -2
- package/dist/gateway/static/root/assets/app-management-settings-panel-DeQsTsyy.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-BG9b4abG.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-De8mVutL.js +0 -4
- package/dist/gateway/static/root/assets/browser-settings-page-C_ZRa4LF.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-api-hny224j-.js +0 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-Drc0qLsw.js +0 -1
- package/dist/gateway/static/root/assets/channel-recipient-api-DAlOHxKJ.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-CA20zBV9.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-qMyMB5te.js +0 -2
- package/dist/gateway/static/root/assets/desktop-pet-XG6Vb_fZ.js +0 -1
- package/dist/gateway/static/root/assets/dreaming-settings-CkUZ997f.js +0 -3
- package/dist/gateway/static/root/assets/extension-settings-page-3RMhQtZ3.js +0 -1
- package/dist/gateway/static/root/assets/fetch-pg2ErdAk.js +0 -1
- package/dist/gateway/static/root/assets/gateway-config-api-DbG97ia2.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-B8cyqKk-.js +0 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-DmeW7Zz3.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-DrTyGbVD.js +0 -1
- package/dist/gateway/static/root/assets/goals-config-api-CxCWGIBB.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-DV1bJhIh.js +0 -2
- package/dist/gateway/static/root/assets/goals-settings-9DrfOkP5.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-KaDpZrv0.js +0 -1
- package/dist/gateway/static/root/assets/index-C2F0Clvm.js +0 -256
- package/dist/gateway/static/root/assets/index-COQSX5d_.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-C9dpiNTF.js +0 -2
- package/dist/gateway/static/root/assets/messages-DmnbiNxU.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-zdCdmBmH.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-MpGeFfPt.js +0 -1
- package/dist/gateway/static/root/assets/notes-workbench-DYCM3yd2.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-Cu0ElRnS.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-DakNaUjZ.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-DYKjt7L9.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-CcGHDsz1.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-CuFdeKNu.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-0AJoUvxi.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-D6a_hEk7.js +0 -1
- package/dist/gateway/static/root/assets/share-preview-page-BtsuETKF.js +0 -2
- package/dist/gateway/static/root/assets/shares-settings-CcEFT7S9.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-BcN_RuTD.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-BbRZZZA5.js +0 -2
- package/dist/gateway/static/root/assets/use-channel-catalog-C6vrxdzZ.js +0 -1
- package/dist/gateway/static/root/assets/user-profile-settings-panel-DZ0nKMgj.js +0 -1
- package/dist/gateway/static/root/assets/vendor-codemirror-BvNgyE1Q.js +0 -45
- package/dist/gateway/static/root/assets/voice-api-key-field-2QKflCx8.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-DfJnuWq1.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-BJwwFTm_.js +0 -18
|
@@ -22,7 +22,9 @@ function buildHistoryExcerpt(messages, maxChars) {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
function buildGoalContextText(goal) {
|
|
25
|
-
const lines = [`Title: ${goal.title}`];
|
|
25
|
+
const lines = [`Title: ${goal.contract?.objective || goal.title}`];
|
|
26
|
+
if (goal.contract?.scopeBoundary) lines.push(`Scope boundary:\n${goal.contract.scopeBoundary}`);
|
|
27
|
+
if (goal.contract?.evidencePlan.length) lines.push(`Expected completion evidence:\n${goal.contract.evidencePlan.map((item) => `- ${item}`).join("\n")}`);
|
|
26
28
|
if (goal.contextMessage?.text.trim()) lines.push(`Context:\n${goal.contextMessage.text.trim()}`);
|
|
27
29
|
if (goal.contextMessage?.attachments.length) lines.push(["Attachments:", ...goal.contextMessage.attachments.map((attachment) => `- ${attachment.name} (${attachment.mimeType}, ${attachment.size} bytes) ${attachment.uri}`)].join("\n"));
|
|
28
30
|
return lines.join("\n\n");
|
|
@@ -93,12 +95,31 @@ async function handlePersistentGoalPostTurn(opts) {
|
|
|
93
95
|
sessionKey
|
|
94
96
|
}, `Persistent goal: could not load messages for judge context: ${err instanceof Error ? err.message : String(err)}`);
|
|
95
97
|
}
|
|
96
|
-
|
|
98
|
+
let decision = await evaluateAfterTurnHermesLike(state, assistantPlainText, judgeRef, signal, {
|
|
97
99
|
goalsSlice: goalsCfg,
|
|
98
100
|
historyExcerpt,
|
|
99
101
|
goalContextExcerpt: buildGoalContextText(goal),
|
|
100
102
|
uiLocale: resolveGoalUiLocale(state)
|
|
101
103
|
});
|
|
104
|
+
const completionReadiness = goalService.getCompletionReadiness(goal.id);
|
|
105
|
+
if (decision.verdict === "done" && completionReadiness && !completionReadiness.ready && decision.newState) {
|
|
106
|
+
const reason = `Completion evidence still needed: ${completionReadiness.missingEvidence.join("; ")}`;
|
|
107
|
+
decision = {
|
|
108
|
+
...decision,
|
|
109
|
+
newState: {
|
|
110
|
+
...decision.newState,
|
|
111
|
+
status: "active",
|
|
112
|
+
lastVerdict: "continue",
|
|
113
|
+
lastReason: reason
|
|
114
|
+
},
|
|
115
|
+
shouldContinue: true,
|
|
116
|
+
continuationPrompt: `Before declaring the goal complete, collect this evidence:\n${completionReadiness.missingEvidence.map((item) => `- ${item}`).join("\n")}`,
|
|
117
|
+
verdict: "continue",
|
|
118
|
+
reason,
|
|
119
|
+
message: reason,
|
|
120
|
+
missingEvidence: completionReadiness.missingEvidence
|
|
121
|
+
};
|
|
122
|
+
}
|
|
102
123
|
if (decision.newState) {
|
|
103
124
|
const ns = decision.newState;
|
|
104
125
|
const completedChecklistItemIds = decision.completedChecklistItemIndexes?.map((index) => goal.checklist[index]?.id).filter((id) => typeof id === "string" && id.length > 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"post-turn.js","names":[],"sources":["../../../../src/agent/goals/post-turn.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { Config, GoalsConfig } from '../../config/schema.js';\nimport { getAgentDefaultModelRef } from '../../config/schema.js';\nimport { resolveEffectiveAgentProfileForSession } from '../../config/agent-profile.js';\nimport { GoalService } from '../../goals/index.js';\nimport { createLogger } from '../../utils/logger.js';\n\nimport { evaluateAfterTurnHermesLike } from './evaluate-turn.js';\nimport { resolveGoalUiLocale } from './goal-locale.js';\nimport type { PersistentGoalApis } from './persistent-goal-apis.js';\nimport type { PersistentGoalState } from './state.js';\n\nconst log = createLogger('PersistentGoal');\nconst goalService = new GoalService();\n\nfunction buildHistoryExcerpt(messages: AgentMessage[], maxChars: number): string {\n if (maxChars <= 0) return '';\n try {\n const raw = JSON.stringify(messages);\n if (raw.length <= maxChars) return raw;\n return raw.slice(-maxChars);\n } catch {\n return '';\n }\n}\n\nfunction buildGoalContextText(goal: NonNullable<ReturnType<GoalService['get']>>): string {\n const lines = [`Title: ${goal.title}`];\n if (goal.contextMessage?.text.trim()) {\n lines.push(`Context:\\n${goal.contextMessage.text.trim()}`);\n }\n if (goal.contextMessage?.attachments.length) {\n lines.push(\n [\n 'Attachments:',\n ...goal.contextMessage.attachments.map((attachment) =>\n `- ${attachment.name} (${attachment.mimeType}, ${attachment.size} bytes) ${attachment.uri}`,\n ),\n ].join('\\n'),\n );\n }\n return lines.join('\\n\\n');\n}\n\nfunction resolveJudgeModelRef(\n config: Config | undefined,\n sessionKey: string,\n storedJudge?: string,\n runtimeSessionModelRef?: string,\n): string | undefined {\n const fromCfg = config?.goals?.judgeModelRef?.trim();\n if (fromCfg) return fromCfg;\n if (storedJudge?.trim()) return storedJudge.trim();\n if (config) {\n const profile = resolveEffectiveAgentProfileForSession(config, sessionKey);\n if (profile.primaryModelRef?.trim()) return profile.primaryModelRef.trim();\n }\n if (runtimeSessionModelRef?.trim()) return runtimeSessionModelRef.trim();\n if (config) return getAgentDefaultModelRef(config);\n return undefined;\n}\n\nexport function shouldRunInitialChecklistDecomposition(input: {\n checklistLength: number;\n turnsUsed: number;\n checklistDecomposePolicy?: GoalsConfig['checklistDecomposePolicy'];\n}): boolean {\n if (input.checklistLength === 0) return true;\n return input.checklistDecomposePolicy === 'supplement_existing' && input.turnsUsed === 0;\n}\n\nasync function appendAssistantLine(apis: PersistentGoalApis, sessionKey: string, text: string): Promise<void> {\n await apis.appendAssistantReceipt(sessionKey, text);\n}\n\nexport async function handlePersistentGoalPostTurn(opts: {\n apis: PersistentGoalApis;\n sessionKey: string;\n assistantPlainText: string;\n aborted: boolean;\n streamError?: string;\n skipPersistentGoalPostTurn: boolean;\n config: Config | undefined;\n /** Active session model (overrides, per-agent defaults) when goals/global defaults are unset. */\n runtimeSessionModelRef?: string;\n signal?: AbortSignal;\n /** Hermes-style: verdict line also sent as a normal outbound message (non-webchat). */\n publishVerdictToChannel?: (text: string) => Promise<void>;\n onGoalStatusUpdated?: (payload: {\n goalId: string;\n sessionKey: string;\n previousStatus: string;\n status: string;\n goal: import('../../goals/types.js').GoalWithDetails;\n }) => void;\n}): Promise<void> {\n const {\n apis,\n sessionKey,\n assistantPlainText,\n aborted,\n streamError,\n skipPersistentGoalPostTurn,\n config,\n runtimeSessionModelRef,\n signal,\n publishVerdictToChannel,\n onGoalStatusUpdated,\n } = opts;\n\n if (skipPersistentGoalPostTurn) return;\n if (aborted || streamError) return;\n\n const meta = await apis.getSessionMetadata(sessionKey);\n if (!meta) return;\n\n const goal = goalService.getActiveForSession(sessionKey);\n if (!goal || goal.status !== 'active') return;\n\n const goalsCfg = config?.goals;\n const shouldDecomposeChecklist = shouldRunInitialChecklistDecomposition({\n checklistLength: goal.checklist.length,\n turnsUsed: goal.turnsUsed,\n checklistDecomposePolicy: goalsCfg?.checklistDecomposePolicy,\n });\n const state: PersistentGoalState = {\n goal: buildGoalContextText(goal),\n status: 'active',\n turnsUsed: goal.turnsUsed,\n maxTurns: goal.maxTurns,\n createdAt: goal.createdAt,\n lastTurnAt: goal.updatedAt,\n lastReason: goal.blockedReason,\n judgeModelRef: goal.judgeModelRef,\n decomposed: shouldDecomposeChecklist ? undefined : true,\n uiLocale: goal.uiLocale,\n checklist: goal.checklist.map((it) => ({\n text: it.text,\n status: it.status,\n addedBy: it.addedBy,\n addedAt: it.addedAt,\n completedAt: it.completedAt,\n evidence: it.evidenceSummary,\n })),\n };\n\n const judgeRef = resolveJudgeModelRef(config, sessionKey, state.judgeModelRef, runtimeSessionModelRef);\n if (!judgeRef) {\n log.warn({ sessionKey }, 'Persistent goal: no judge model ref; skipping post-turn');\n return;\n }\n\n const historyCap = goalsCfg?.checklistHistoryChars ?? 24_000;\n let historyExcerpt = '';\n try {\n const msgs = await apis.loadMessages(sessionKey);\n historyExcerpt = buildHistoryExcerpt(msgs, historyCap);\n } catch (err) {\n log.debug(\n { err, sessionKey },\n `Persistent goal: could not load messages for judge context: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n const decision = await evaluateAfterTurnHermesLike(state, assistantPlainText, judgeRef, signal, {\n goalsSlice: goalsCfg,\n historyExcerpt,\n goalContextExcerpt: buildGoalContextText(goal),\n uiLocale: resolveGoalUiLocale(state),\n });\n\n if (decision.newState) {\n const ns = decision.newState;\n const completedChecklistItemIds = decision.completedChecklistItemIndexes\n ?.map((index) => goal.checklist[index]?.id)\n .filter((id): id is string => typeof id === 'string' && id.length > 0);\n try {\n const updatedGoal = goalService.syncPostTurnState({\n goalId: goal.id,\n sessionKey,\n source: 'chat',\n status: ns.status === 'done'\n ? 'done'\n : decision.verdict === 'blocked'\n ? 'blocked'\n : decision.verdict === 'needs_input'\n ? 'needs_input'\n : ns.status === 'paused'\n ? 'paused'\n : 'active',\n turnsUsed: ns.turnsUsed,\n maxTurns: ns.maxTurns,\n reason: ns.pausedReason ?? decision.reason,\n nextAction: decision.continuationPrompt ?? undefined,\n assistantPreview: assistantPlainText,\n verdict: decision.verdict === 'done'\n ? 'done'\n : decision.verdict === 'decompose'\n ? 'decompose'\n : decision.verdict === 'blocked'\n ? 'blocked'\n : decision.verdict === 'needs_input'\n ? 'needs_input'\n : 'continue',\n confidence: decision.confidence,\n missingEvidence: decision.missingEvidence,\n userQuestion: decision.userQuestion,\n completedChecklistItemIds,\n checklist: ns.checklist?.map((it) => ({\n text: it.text,\n status: it.status,\n addedBy: it.addedBy,\n addedAt: it.addedAt,\n completedAt: it.completedAt,\n evidenceSummary: it.evidence,\n })),\n });\n if (updatedGoal && updatedGoal.status !== goal.status) {\n onGoalStatusUpdated?.({\n goalId: goal.id,\n sessionKey,\n previousStatus: goal.status,\n status: updatedGoal.status,\n goal: updatedGoal,\n });\n }\n } catch (err) {\n log.warn(\n { err, sessionKey, goalId: goal.id },\n `Persistent goal: SQLite goal update failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n if (decision.message) {\n if (publishVerdictToChannel) {\n try {\n await publishVerdictToChannel(decision.message);\n } catch (err) {\n log.warn(\n { err, sessionKey },\n `Persistent goal: channel verdict send failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n } else {\n try {\n await appendAssistantLine(apis, sessionKey, decision.message);\n } catch (err) {\n log.warn(\n { err, sessionKey },\n `Persistent goal: failed to append status message: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n if (decision.shouldContinue && decision.continuationPrompt) {\n apis.scheduleContinuation(sessionKey, decision.continuationPrompt);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"post-turn.js","names":[],"sources":["../../../../src/agent/goals/post-turn.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { Config, GoalsConfig } from '../../config/schema.js';\nimport { getAgentDefaultModelRef } from '../../config/schema.js';\nimport { resolveEffectiveAgentProfileForSession } from '../../config/agent-profile.js';\nimport { GoalService } from '../../goals/index.js';\nimport { createLogger } from '../../utils/logger.js';\n\nimport { evaluateAfterTurnHermesLike } from './evaluate-turn.js';\nimport { resolveGoalUiLocale } from './goal-locale.js';\nimport type { PersistentGoalApis } from './persistent-goal-apis.js';\nimport type { PersistentGoalState } from './state.js';\n\nconst log = createLogger('PersistentGoal');\nconst goalService = new GoalService();\n\nfunction buildHistoryExcerpt(messages: AgentMessage[], maxChars: number): string {\n if (maxChars <= 0) return '';\n try {\n const raw = JSON.stringify(messages);\n if (raw.length <= maxChars) return raw;\n return raw.slice(-maxChars);\n } catch {\n return '';\n }\n}\n\nfunction buildGoalContextText(goal: NonNullable<ReturnType<GoalService['get']>>): string {\n const lines = [`Title: ${goal.contract?.objective || goal.title}`];\n if (goal.contract?.scopeBoundary) {\n lines.push(`Scope boundary:\\n${goal.contract.scopeBoundary}`);\n }\n if (goal.contract?.evidencePlan.length) {\n lines.push(`Expected completion evidence:\\n${goal.contract.evidencePlan.map((item) => `- ${item}`).join('\\n')}`);\n }\n if (goal.contextMessage?.text.trim()) {\n lines.push(`Context:\\n${goal.contextMessage.text.trim()}`);\n }\n if (goal.contextMessage?.attachments.length) {\n lines.push(\n [\n 'Attachments:',\n ...goal.contextMessage.attachments.map((attachment) =>\n `- ${attachment.name} (${attachment.mimeType}, ${attachment.size} bytes) ${attachment.uri}`,\n ),\n ].join('\\n'),\n );\n }\n return lines.join('\\n\\n');\n}\n\nfunction resolveJudgeModelRef(\n config: Config | undefined,\n sessionKey: string,\n storedJudge?: string,\n runtimeSessionModelRef?: string,\n): string | undefined {\n const fromCfg = config?.goals?.judgeModelRef?.trim();\n if (fromCfg) return fromCfg;\n if (storedJudge?.trim()) return storedJudge.trim();\n if (config) {\n const profile = resolveEffectiveAgentProfileForSession(config, sessionKey);\n if (profile.primaryModelRef?.trim()) return profile.primaryModelRef.trim();\n }\n if (runtimeSessionModelRef?.trim()) return runtimeSessionModelRef.trim();\n if (config) return getAgentDefaultModelRef(config);\n return undefined;\n}\n\nexport function shouldRunInitialChecklistDecomposition(input: {\n checklistLength: number;\n turnsUsed: number;\n checklistDecomposePolicy?: GoalsConfig['checklistDecomposePolicy'];\n}): boolean {\n if (input.checklistLength === 0) return true;\n return input.checklistDecomposePolicy === 'supplement_existing' && input.turnsUsed === 0;\n}\n\nasync function appendAssistantLine(apis: PersistentGoalApis, sessionKey: string, text: string): Promise<void> {\n await apis.appendAssistantReceipt(sessionKey, text);\n}\n\nexport async function handlePersistentGoalPostTurn(opts: {\n apis: PersistentGoalApis;\n sessionKey: string;\n assistantPlainText: string;\n aborted: boolean;\n streamError?: string;\n skipPersistentGoalPostTurn: boolean;\n config: Config | undefined;\n /** Active session model (overrides, per-agent defaults) when goals/global defaults are unset. */\n runtimeSessionModelRef?: string;\n signal?: AbortSignal;\n /** Hermes-style: verdict line also sent as a normal outbound message (non-webchat). */\n publishVerdictToChannel?: (text: string) => Promise<void>;\n onGoalStatusUpdated?: (payload: {\n goalId: string;\n sessionKey: string;\n previousStatus: string;\n status: string;\n goal: import('../../goals/types.js').GoalWithDetails;\n }) => void;\n}): Promise<void> {\n const {\n apis,\n sessionKey,\n assistantPlainText,\n aborted,\n streamError,\n skipPersistentGoalPostTurn,\n config,\n runtimeSessionModelRef,\n signal,\n publishVerdictToChannel,\n onGoalStatusUpdated,\n } = opts;\n\n if (skipPersistentGoalPostTurn) return;\n if (aborted || streamError) return;\n\n const meta = await apis.getSessionMetadata(sessionKey);\n if (!meta) return;\n\n const goal = goalService.getActiveForSession(sessionKey);\n if (!goal || goal.status !== 'active') return;\n\n const goalsCfg = config?.goals;\n const shouldDecomposeChecklist = shouldRunInitialChecklistDecomposition({\n checklistLength: goal.checklist.length,\n turnsUsed: goal.turnsUsed,\n checklistDecomposePolicy: goalsCfg?.checklistDecomposePolicy,\n });\n const state: PersistentGoalState = {\n goal: buildGoalContextText(goal),\n status: 'active',\n turnsUsed: goal.turnsUsed,\n maxTurns: goal.maxTurns,\n createdAt: goal.createdAt,\n lastTurnAt: goal.updatedAt,\n lastReason: goal.blockedReason,\n judgeModelRef: goal.judgeModelRef,\n decomposed: shouldDecomposeChecklist ? undefined : true,\n uiLocale: goal.uiLocale,\n checklist: goal.checklist.map((it) => ({\n text: it.text,\n status: it.status,\n addedBy: it.addedBy,\n addedAt: it.addedAt,\n completedAt: it.completedAt,\n evidence: it.evidenceSummary,\n })),\n };\n\n const judgeRef = resolveJudgeModelRef(config, sessionKey, state.judgeModelRef, runtimeSessionModelRef);\n if (!judgeRef) {\n log.warn({ sessionKey }, 'Persistent goal: no judge model ref; skipping post-turn');\n return;\n }\n\n const historyCap = goalsCfg?.checklistHistoryChars ?? 24_000;\n let historyExcerpt = '';\n try {\n const msgs = await apis.loadMessages(sessionKey);\n historyExcerpt = buildHistoryExcerpt(msgs, historyCap);\n } catch (err) {\n log.debug(\n { err, sessionKey },\n `Persistent goal: could not load messages for judge context: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n let decision = await evaluateAfterTurnHermesLike(state, assistantPlainText, judgeRef, signal, {\n goalsSlice: goalsCfg,\n historyExcerpt,\n goalContextExcerpt: buildGoalContextText(goal),\n uiLocale: resolveGoalUiLocale(state),\n });\n\n const completionReadiness = goalService.getCompletionReadiness(goal.id);\n if (decision.verdict === 'done' && completionReadiness && !completionReadiness.ready && decision.newState) {\n const reason = `Completion evidence still needed: ${completionReadiness.missingEvidence.join('; ')}`;\n decision = {\n ...decision,\n newState: {\n ...decision.newState,\n status: 'active',\n lastVerdict: 'continue',\n lastReason: reason,\n },\n shouldContinue: true,\n continuationPrompt: `Before declaring the goal complete, collect this evidence:\\n${completionReadiness.missingEvidence.map((item) => `- ${item}`).join('\\n')}`,\n verdict: 'continue',\n reason,\n message: reason,\n missingEvidence: completionReadiness.missingEvidence,\n };\n }\n\n if (decision.newState) {\n const ns = decision.newState;\n const completedChecklistItemIds = decision.completedChecklistItemIndexes\n ?.map((index) => goal.checklist[index]?.id)\n .filter((id): id is string => typeof id === 'string' && id.length > 0);\n try {\n const updatedGoal = goalService.syncPostTurnState({\n goalId: goal.id,\n sessionKey,\n source: 'chat',\n status: ns.status === 'done'\n ? 'done'\n : decision.verdict === 'blocked'\n ? 'blocked'\n : decision.verdict === 'needs_input'\n ? 'needs_input'\n : ns.status === 'paused'\n ? 'paused'\n : 'active',\n turnsUsed: ns.turnsUsed,\n maxTurns: ns.maxTurns,\n reason: ns.pausedReason ?? decision.reason,\n nextAction: decision.continuationPrompt ?? undefined,\n assistantPreview: assistantPlainText,\n verdict: decision.verdict === 'done'\n ? 'done'\n : decision.verdict === 'decompose'\n ? 'decompose'\n : decision.verdict === 'blocked'\n ? 'blocked'\n : decision.verdict === 'needs_input'\n ? 'needs_input'\n : 'continue',\n confidence: decision.confidence,\n missingEvidence: decision.missingEvidence,\n userQuestion: decision.userQuestion,\n completedChecklistItemIds,\n checklist: ns.checklist?.map((it) => ({\n text: it.text,\n status: it.status,\n addedBy: it.addedBy,\n addedAt: it.addedAt,\n completedAt: it.completedAt,\n evidenceSummary: it.evidence,\n })),\n });\n if (updatedGoal && updatedGoal.status !== goal.status) {\n onGoalStatusUpdated?.({\n goalId: goal.id,\n sessionKey,\n previousStatus: goal.status,\n status: updatedGoal.status,\n goal: updatedGoal,\n });\n }\n } catch (err) {\n log.warn(\n { err, sessionKey, goalId: goal.id },\n `Persistent goal: SQLite goal update failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n if (decision.message) {\n if (publishVerdictToChannel) {\n try {\n await publishVerdictToChannel(decision.message);\n } catch (err) {\n log.warn(\n { err, sessionKey },\n `Persistent goal: channel verdict send failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n } else {\n try {\n await appendAssistantLine(apis, sessionKey, decision.message);\n } catch (err) {\n log.warn(\n { err, sessionKey },\n `Persistent goal: failed to append status message: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n if (decision.shouldContinue && decision.continuationPrompt) {\n apis.scheduleContinuation(sessionKey, decision.continuationPrompt);\n }\n}\n"],"mappings":"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"}
|
|
@@ -10,6 +10,7 @@ import type { ThinkLevel } from '../transcript/thinking-types.js';
|
|
|
10
10
|
import type { CompactionResult } from '../memory/compaction.js';
|
|
11
11
|
import type { PersistentGoalApis } from '../goals/persistent-goal-apis.js';
|
|
12
12
|
import type { WorkflowRunServiceLike } from '../../workflows/service/workflow-run-service.types.js';
|
|
13
|
+
import type { SkillInstallToolOptions, SkillInstallToolResult } from '../tools/skill-install-tool.js';
|
|
13
14
|
export interface CommandContext {
|
|
14
15
|
sessionKey: string;
|
|
15
16
|
channel: string;
|
|
@@ -39,6 +40,8 @@ export interface CommandHandlerConfig {
|
|
|
39
40
|
abortSessionTurn?: (sessionKey: string) => Promise<void>;
|
|
40
41
|
/** Reload skills from disk and refresh active agent prompts. */
|
|
41
42
|
reloadSkills?: () => void | Promise<void>;
|
|
43
|
+
/** Install a managed skill from an explicit source and refresh active agent prompts. */
|
|
44
|
+
installSkillFromSource?: (opts: SkillInstallToolOptions) => Promise<SkillInstallToolResult>;
|
|
42
45
|
compactSession?: (sessionKey: string, options?: {
|
|
43
46
|
instructions?: string;
|
|
44
47
|
force?: boolean;
|
|
@@ -67,6 +70,7 @@ export declare class CommandHandler {
|
|
|
67
70
|
private invalidateAgentSession?;
|
|
68
71
|
private abortSessionTurn?;
|
|
69
72
|
private reloadSkills?;
|
|
73
|
+
private installSkillFromSource?;
|
|
70
74
|
private compactSession?;
|
|
71
75
|
private btwQuery?;
|
|
72
76
|
private getSessionContextReport?;
|
|
@@ -23,6 +23,7 @@ var CommandHandler = class {
|
|
|
23
23
|
invalidateAgentSession;
|
|
24
24
|
abortSessionTurn;
|
|
25
25
|
reloadSkills;
|
|
26
|
+
installSkillFromSource;
|
|
26
27
|
compactSession;
|
|
27
28
|
btwQuery;
|
|
28
29
|
getSessionContextReport;
|
|
@@ -40,6 +41,7 @@ var CommandHandler = class {
|
|
|
40
41
|
this.invalidateAgentSession = handlerConfig.invalidateAgentSession;
|
|
41
42
|
this.abortSessionTurn = handlerConfig.abortSessionTurn;
|
|
42
43
|
this.reloadSkills = handlerConfig.reloadSkills;
|
|
44
|
+
this.installSkillFromSource = handlerConfig.installSkillFromSource;
|
|
43
45
|
this.compactSession = handlerConfig.compactSession;
|
|
44
46
|
this.btwQuery = handlerConfig.btwQuery;
|
|
45
47
|
this.getSessionContextReport = handlerConfig.getSessionContextReport;
|
|
@@ -131,6 +133,10 @@ var CommandHandler = class {
|
|
|
131
133
|
reloadSkills: this.reloadSkills ? async () => {
|
|
132
134
|
await this.reloadSkills();
|
|
133
135
|
} : void 0,
|
|
136
|
+
installSkillFromSource: this.installSkillFromSource ? (opts) => this.installSkillFromSource({
|
|
137
|
+
...opts,
|
|
138
|
+
sessionKey: context.sessionKey
|
|
139
|
+
}) : void 0,
|
|
134
140
|
compactSession: this.compactSession,
|
|
135
141
|
btwQuery: this.btwQuery,
|
|
136
142
|
getSessionContextReport: this.getSessionContextReport,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-handler.js","names":[],"sources":["../../../../src/agent/messaging/command-handler.ts"],"sourcesContent":["/**\n * Command Handler - Parses and executes commands\n *\n * Handles command execution using the unified command system.\n */\n\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport type { Config } from '../../config/schema.js';\nimport { isProviderConfiguredSync } from '../../providers/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport type { ThinkLevel } from '../transcript/thinking-types.js';\nimport type { CompactionResult } from '../memory/compaction.js';\nimport { createLogger } from '../../utils/logger.js';\nimport {\n commandRegistry,\n createCommandContext,\n type CommandContext as UnifiedCommandContext,\n} from '../../chat-commands/index.js';\nimport { getAllProviders, getModelsByProvider, getProviderDisplayName } from '../../providers/index.js';\nimport type { PersistentGoalApis } from '../goals/persistent-goal-apis.js';\nimport type { WorkflowRunServiceLike } from '../../workflows/service/workflow-run-service.types.js';\n\nconst log = createLogger('CommandHandler');\n\n/** Gateway console direct stream uses SSE tokens; there is no ChannelPlugin outbound for `webchat`. */\nfunction shouldSkipBusOutboundForChannel(channel: string): boolean {\n return channel === 'webchat';\n}\n\nexport interface CommandContext {\n sessionKey: string;\n channel: string;\n chatId: string;\n senderId: string;\n isGroup: boolean;\n /** From inbound message metadata (thread/account, etc.) for `/goal` continuation routing. */\n inboundMetadata?: Record<string, unknown>;\n}\n\nexport interface CommandHandlerConfig {\n config: Config;\n bus: MessageBus;\n sessionStore: SessionStore;\n sessionConfigStore?: SessionConfigStore;\n /** After /think persists, sync pi-agent */\n applySessionThinkingLevel?: (sessionKey: string, level: ThinkLevel) => void;\n getCurrentModel: () => string;\n switchModelForSession: (sessionKey: string, modelId: string) => Promise<boolean>;\n /** Drop in-memory agent after session file is cleared (e.g. /new) */\n invalidateAgentSession?: (sessionKey: string) => void;\n /** Reset session in place (archive transcript, new session id; preserve overrides) */\n resetSession?: (sessionKey: string) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n /** Cancel streaming preview + in-flight LLM work for this session (e.g. /abort) */\n abortSessionTurn?: (sessionKey: string) => Promise<void>;\n /** Reload skills from disk and refresh active agent prompts. */\n reloadSkills?: () => void | Promise<void>;\n\n compactSession?: (\n sessionKey: string,\n options?: { instructions?: string; force?: boolean },\n ) => Promise<CompactionResult>;\n\n btwQuery?: (sessionKey: string, question: string) => Promise<{ text: string; error?: string }>;\n\n getSessionContextReport?: (\n sessionKey: string,\n mode: 'list' | 'detail' | 'json',\n ) => Promise<string>;\n\n getPersistentGoalApisForCommand: (routing: {\n sessionKey: string;\n channel: string;\n chatId: string;\n inboundMetadata?: Record<string, unknown>;\n }) => PersistentGoalApis;\n getWorkflowRunService?: () => WorkflowRunServiceLike | undefined;\n}\n\nexport class CommandHandler {\n private config: Config;\n private bus: MessageBus;\n private sessionStore: SessionStore;\n private sessionConfigStore?: SessionConfigStore;\n private applySessionThinkingLevel?: (sessionKey: string, level: ThinkLevel) => void;\n private getCurrentModel: () => string;\n private switchModelForSession: (sessionKey: string, modelId: string) => Promise<boolean>;\n private invalidateAgentSession?: (sessionKey: string) => void;\n private abortSessionTurn?: (sessionKey: string) => Promise<void>;\n private reloadSkills?: CommandHandlerConfig['reloadSkills'];\n private compactSession?: CommandHandlerConfig['compactSession'];\n private btwQuery?: CommandHandlerConfig['btwQuery'];\n private getSessionContextReport?: CommandHandlerConfig['getSessionContextReport'];\n private getPersistentGoalApisForCommand: CommandHandlerConfig['getPersistentGoalApisForCommand'];\n private resetSession?: CommandHandlerConfig['resetSession'];\n private getWorkflowRunService?: CommandHandlerConfig['getWorkflowRunService'];\n\n constructor(handlerConfig: CommandHandlerConfig) {\n this.config = handlerConfig.config;\n this.bus = handlerConfig.bus;\n this.sessionStore = handlerConfig.sessionStore;\n this.sessionConfigStore = handlerConfig.sessionConfigStore;\n this.applySessionThinkingLevel = handlerConfig.applySessionThinkingLevel;\n this.getCurrentModel = handlerConfig.getCurrentModel;\n this.switchModelForSession = handlerConfig.switchModelForSession;\n this.invalidateAgentSession = handlerConfig.invalidateAgentSession;\n this.abortSessionTurn = handlerConfig.abortSessionTurn;\n this.reloadSkills = handlerConfig.reloadSkills;\n this.compactSession = handlerConfig.compactSession;\n this.btwQuery = handlerConfig.btwQuery;\n this.getSessionContextReport = handlerConfig.getSessionContextReport;\n this.getPersistentGoalApisForCommand = handlerConfig.getPersistentGoalApisForCommand;\n this.resetSession = handlerConfig.resetSession;\n this.getWorkflowRunService = handlerConfig.getWorkflowRunService;\n }\n\n /** Replace config reference after hot reload or gateway PATCH so commands see current defaults. */\n updateAgentConfig(config: Config): void {\n this.config = config;\n }\n\n /**\n * Build the unified command context shared by all execute paths.\n * When `recorder` is set, every reply text is also captured (for SSE / CLI aggregation).\n */\n private buildCommandContext(\n context: CommandContext,\n recorder?: (text: string) => void,\n ): UnifiedCommandContext {\n const skipBusOutbound = shouldSkipBusOutboundForChannel(context.channel);\n\n return createCommandContext({\n sessionKey: context.sessionKey,\n source: context.channel as 'telegram' | 'webui' | 'cli' | 'api' | 'system' | 'gateway',\n channelId: context.channel,\n chatId: context.chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n config: this.config,\n bus: this.bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n applySessionThinkingLevel: this.applySessionThinkingLevel,\n\n replyHandler: async (text: string, _options?) => {\n recorder?.(text);\n if (skipBusOutbound) return;\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: text,\n type: 'message',\n });\n },\n\n typingHandler: async (typing: boolean) => {\n if (skipBusOutbound) return;\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n type: typing ? 'typing_on' : 'typing_off',\n });\n },\n\n supportedFeatures: ['markdown', 'typing'],\n\n getCurrentModel: this.getCurrentModel,\n\n switchModel: async (modelId: string) => {\n return this.switchModelForSession(context.sessionKey, modelId);\n },\n\n listModels: async () => {\n const providers = getAllProviders();\n const models: Array<{ id: string; name: string; provider: string }> = [];\n\n for (const providerId of providers) {\n if (isProviderConfiguredSync(providerId)) {\n const providerModels = getModelsByProvider(providerId);\n for (const m of providerModels) {\n models.push({\n id: `${m.provider}/${m.id}`,\n name: m.name || m.id,\n provider: getProviderDisplayName(providerId),\n });\n }\n }\n }\n\n return models;\n },\n\n getUsage: async () => {\n const messages = await this.sessionStore.load(context.sessionKey);\n let promptTokens = 0;\n let completionTokens = 0;\n\n for (const msg of messages) {\n if ('usage' in msg && msg.usage) {\n const usage = msg.usage as { input?: number; output?: number };\n promptTokens += usage.input || 0;\n completionTokens += usage.output || 0;\n }\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n messageCount: messages.length,\n };\n },\n\n invalidateAgentSession: this.invalidateAgentSession,\n\n resetSession: this.resetSession\n ? async (sk) => {\n await this.resetSession!(sk);\n }\n : undefined,\n\n abortCurrentTurn: this.abortSessionTurn\n ? async () => {\n await this.abortSessionTurn!(context.sessionKey);\n }\n : undefined,\n reloadSkills: this.reloadSkills\n ? async () => {\n await this.reloadSkills!();\n }\n : undefined,\n\n compactSession: this.compactSession,\n btwQuery: this.btwQuery,\n getSessionContextReport: this.getSessionContextReport,\n persistentGoalApis: this.getPersistentGoalApisForCommand({\n sessionKey: context.sessionKey,\n channel: context.channel,\n chatId: context.chatId,\n inboundMetadata: context.inboundMetadata,\n }),\n workflowRunService: this.getWorkflowRunService?.(),\n });\n }\n\n /**\n * Execute a command using the unified command system\n */\n async executeCommand(\n commandName: string,\n args: string,\n context: CommandContext,\n ): Promise<boolean> {\n if (!commandRegistry.has(commandName)) {\n return false;\n }\n\n log.info({ command: commandName, sessionKey: context.sessionKey }, 'Executing command via new system');\n\n const cmdCtx = this.buildCommandContext(context);\n const result = await commandRegistry.execute(commandName, cmdCtx, args);\n\n if (result.content && !shouldSkipBusOutboundForChannel(context.channel)) {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: result.content,\n type: 'message',\n });\n }\n\n return true;\n }\n\n /**\n * Run command and return all user-visible text (ctx.reply + result.content) for SSE/CLI.\n * Same bus side effects as {@link executeCommand}.\n */\n async executeCommandAndAggregateReply(\n commandName: string,\n args: string,\n context: CommandContext,\n ): Promise<{ handled: boolean; aggregatedText: string; metadata?: Record<string, unknown> }> {\n if (!commandRegistry.has(commandName)) {\n return { handled: false, aggregatedText: '' };\n }\n\n log.info({ command: commandName, sessionKey: context.sessionKey }, 'Executing command (aggregate reply)');\n\n const segments: string[] = [];\n const wrapped = this.buildCommandContext(context, (text) => segments.push(text));\n const result = await commandRegistry.execute(commandName, wrapped, args);\n\n if (result.content) {\n segments.push(result.content);\n if (!shouldSkipBusOutboundForChannel(context.channel)) {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: result.content,\n type: 'message',\n });\n }\n }\n\n const aggregatedText = segments.filter((s) => s && s.trim()).join('\\n\\n');\n return { handled: true, aggregatedText, metadata: result.metadata };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"command-handler.js","names":[],"sources":["../../../../src/agent/messaging/command-handler.ts"],"sourcesContent":["/**\n * Command Handler - Parses and executes commands\n *\n * Handles command execution using the unified command system.\n */\n\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport type { Config } from '../../config/schema.js';\nimport { isProviderConfiguredSync } from '../../providers/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport type { ThinkLevel } from '../transcript/thinking-types.js';\nimport type { CompactionResult } from '../memory/compaction.js';\nimport { createLogger } from '../../utils/logger.js';\nimport {\n commandRegistry,\n createCommandContext,\n type CommandContext as UnifiedCommandContext,\n} from '../../chat-commands/index.js';\nimport { getAllProviders, getModelsByProvider, getProviderDisplayName } from '../../providers/index.js';\nimport type { PersistentGoalApis } from '../goals/persistent-goal-apis.js';\nimport type { WorkflowRunServiceLike } from '../../workflows/service/workflow-run-service.types.js';\nimport type {\n SkillInstallToolOptions,\n SkillInstallToolResult,\n} from '../tools/skill-install-tool.js';\n\nconst log = createLogger('CommandHandler');\n\n/** Gateway console direct stream uses SSE tokens; there is no ChannelPlugin outbound for `webchat`. */\nfunction shouldSkipBusOutboundForChannel(channel: string): boolean {\n return channel === 'webchat';\n}\n\nexport interface CommandContext {\n sessionKey: string;\n channel: string;\n chatId: string;\n senderId: string;\n isGroup: boolean;\n /** From inbound message metadata (thread/account, etc.) for `/goal` continuation routing. */\n inboundMetadata?: Record<string, unknown>;\n}\n\nexport interface CommandHandlerConfig {\n config: Config;\n bus: MessageBus;\n sessionStore: SessionStore;\n sessionConfigStore?: SessionConfigStore;\n /** After /think persists, sync pi-agent */\n applySessionThinkingLevel?: (sessionKey: string, level: ThinkLevel) => void;\n getCurrentModel: () => string;\n switchModelForSession: (sessionKey: string, modelId: string) => Promise<boolean>;\n /** Drop in-memory agent after session file is cleared (e.g. /new) */\n invalidateAgentSession?: (sessionKey: string) => void;\n /** Reset session in place (archive transcript, new session id; preserve overrides) */\n resetSession?: (sessionKey: string) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n /** Cancel streaming preview + in-flight LLM work for this session (e.g. /abort) */\n abortSessionTurn?: (sessionKey: string) => Promise<void>;\n /** Reload skills from disk and refresh active agent prompts. */\n reloadSkills?: () => void | Promise<void>;\n /** Install a managed skill from an explicit source and refresh active agent prompts. */\n installSkillFromSource?: (opts: SkillInstallToolOptions) => Promise<SkillInstallToolResult>;\n\n compactSession?: (\n sessionKey: string,\n options?: { instructions?: string; force?: boolean },\n ) => Promise<CompactionResult>;\n\n btwQuery?: (sessionKey: string, question: string) => Promise<{ text: string; error?: string }>;\n\n getSessionContextReport?: (\n sessionKey: string,\n mode: 'list' | 'detail' | 'json',\n ) => Promise<string>;\n\n getPersistentGoalApisForCommand: (routing: {\n sessionKey: string;\n channel: string;\n chatId: string;\n inboundMetadata?: Record<string, unknown>;\n }) => PersistentGoalApis;\n getWorkflowRunService?: () => WorkflowRunServiceLike | undefined;\n}\n\nexport class CommandHandler {\n private config: Config;\n private bus: MessageBus;\n private sessionStore: SessionStore;\n private sessionConfigStore?: SessionConfigStore;\n private applySessionThinkingLevel?: (sessionKey: string, level: ThinkLevel) => void;\n private getCurrentModel: () => string;\n private switchModelForSession: (sessionKey: string, modelId: string) => Promise<boolean>;\n private invalidateAgentSession?: (sessionKey: string) => void;\n private abortSessionTurn?: (sessionKey: string) => Promise<void>;\n private reloadSkills?: CommandHandlerConfig['reloadSkills'];\n private installSkillFromSource?: CommandHandlerConfig['installSkillFromSource'];\n private compactSession?: CommandHandlerConfig['compactSession'];\n private btwQuery?: CommandHandlerConfig['btwQuery'];\n private getSessionContextReport?: CommandHandlerConfig['getSessionContextReport'];\n private getPersistentGoalApisForCommand: CommandHandlerConfig['getPersistentGoalApisForCommand'];\n private resetSession?: CommandHandlerConfig['resetSession'];\n private getWorkflowRunService?: CommandHandlerConfig['getWorkflowRunService'];\n\n constructor(handlerConfig: CommandHandlerConfig) {\n this.config = handlerConfig.config;\n this.bus = handlerConfig.bus;\n this.sessionStore = handlerConfig.sessionStore;\n this.sessionConfigStore = handlerConfig.sessionConfigStore;\n this.applySessionThinkingLevel = handlerConfig.applySessionThinkingLevel;\n this.getCurrentModel = handlerConfig.getCurrentModel;\n this.switchModelForSession = handlerConfig.switchModelForSession;\n this.invalidateAgentSession = handlerConfig.invalidateAgentSession;\n this.abortSessionTurn = handlerConfig.abortSessionTurn;\n this.reloadSkills = handlerConfig.reloadSkills;\n this.installSkillFromSource = handlerConfig.installSkillFromSource;\n this.compactSession = handlerConfig.compactSession;\n this.btwQuery = handlerConfig.btwQuery;\n this.getSessionContextReport = handlerConfig.getSessionContextReport;\n this.getPersistentGoalApisForCommand = handlerConfig.getPersistentGoalApisForCommand;\n this.resetSession = handlerConfig.resetSession;\n this.getWorkflowRunService = handlerConfig.getWorkflowRunService;\n }\n\n /** Replace config reference after hot reload or gateway PATCH so commands see current defaults. */\n updateAgentConfig(config: Config): void {\n this.config = config;\n }\n\n /**\n * Build the unified command context shared by all execute paths.\n * When `recorder` is set, every reply text is also captured (for SSE / CLI aggregation).\n */\n private buildCommandContext(\n context: CommandContext,\n recorder?: (text: string) => void,\n ): UnifiedCommandContext {\n const skipBusOutbound = shouldSkipBusOutboundForChannel(context.channel);\n\n return createCommandContext({\n sessionKey: context.sessionKey,\n source: context.channel as 'telegram' | 'webui' | 'cli' | 'api' | 'system' | 'gateway',\n channelId: context.channel,\n chatId: context.chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n config: this.config,\n bus: this.bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n applySessionThinkingLevel: this.applySessionThinkingLevel,\n\n replyHandler: async (text: string, _options?) => {\n recorder?.(text);\n if (skipBusOutbound) return;\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: text,\n type: 'message',\n });\n },\n\n typingHandler: async (typing: boolean) => {\n if (skipBusOutbound) return;\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n type: typing ? 'typing_on' : 'typing_off',\n });\n },\n\n supportedFeatures: ['markdown', 'typing'],\n\n getCurrentModel: this.getCurrentModel,\n\n switchModel: async (modelId: string) => {\n return this.switchModelForSession(context.sessionKey, modelId);\n },\n\n listModels: async () => {\n const providers = getAllProviders();\n const models: Array<{ id: string; name: string; provider: string }> = [];\n\n for (const providerId of providers) {\n if (isProviderConfiguredSync(providerId)) {\n const providerModels = getModelsByProvider(providerId);\n for (const m of providerModels) {\n models.push({\n id: `${m.provider}/${m.id}`,\n name: m.name || m.id,\n provider: getProviderDisplayName(providerId),\n });\n }\n }\n }\n\n return models;\n },\n\n getUsage: async () => {\n const messages = await this.sessionStore.load(context.sessionKey);\n let promptTokens = 0;\n let completionTokens = 0;\n\n for (const msg of messages) {\n if ('usage' in msg && msg.usage) {\n const usage = msg.usage as { input?: number; output?: number };\n promptTokens += usage.input || 0;\n completionTokens += usage.output || 0;\n }\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n messageCount: messages.length,\n };\n },\n\n invalidateAgentSession: this.invalidateAgentSession,\n\n resetSession: this.resetSession\n ? async (sk) => {\n await this.resetSession!(sk);\n }\n : undefined,\n\n abortCurrentTurn: this.abortSessionTurn\n ? async () => {\n await this.abortSessionTurn!(context.sessionKey);\n }\n : undefined,\n reloadSkills: this.reloadSkills\n ? async () => {\n await this.reloadSkills!();\n }\n : undefined,\n installSkillFromSource: this.installSkillFromSource\n ? (opts) => this.installSkillFromSource!({ ...opts, sessionKey: context.sessionKey })\n : undefined,\n\n compactSession: this.compactSession,\n btwQuery: this.btwQuery,\n getSessionContextReport: this.getSessionContextReport,\n persistentGoalApis: this.getPersistentGoalApisForCommand({\n sessionKey: context.sessionKey,\n channel: context.channel,\n chatId: context.chatId,\n inboundMetadata: context.inboundMetadata,\n }),\n workflowRunService: this.getWorkflowRunService?.(),\n });\n }\n\n /**\n * Execute a command using the unified command system\n */\n async executeCommand(\n commandName: string,\n args: string,\n context: CommandContext,\n ): Promise<boolean> {\n if (!commandRegistry.has(commandName)) {\n return false;\n }\n\n log.info({ command: commandName, sessionKey: context.sessionKey }, 'Executing command via new system');\n\n const cmdCtx = this.buildCommandContext(context);\n const result = await commandRegistry.execute(commandName, cmdCtx, args);\n\n if (result.content && !shouldSkipBusOutboundForChannel(context.channel)) {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: result.content,\n type: 'message',\n });\n }\n\n return true;\n }\n\n /**\n * Run command and return all user-visible text (ctx.reply + result.content) for SSE/CLI.\n * Same bus side effects as {@link executeCommand}.\n */\n async executeCommandAndAggregateReply(\n commandName: string,\n args: string,\n context: CommandContext,\n ): Promise<{ handled: boolean; aggregatedText: string; metadata?: Record<string, unknown> }> {\n if (!commandRegistry.has(commandName)) {\n return { handled: false, aggregatedText: '' };\n }\n\n log.info({ command: commandName, sessionKey: context.sessionKey }, 'Executing command (aggregate reply)');\n\n const segments: string[] = [];\n const wrapped = this.buildCommandContext(context, (text) => segments.push(text));\n const result = await commandRegistry.execute(commandName, wrapped, args);\n\n if (result.content) {\n segments.push(result.content);\n if (!shouldSkipBusOutboundForChannel(context.channel)) {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: result.content,\n type: 'message',\n });\n }\n }\n\n const aggregatedText = segments.filter((s) => s && s.trim()).join('\\n\\n');\n return { handled: true, aggregatedText, metadata: result.metadata };\n }\n}\n"],"mappings":"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"}
|
|
@@ -10,6 +10,7 @@ import type { SkillMarkdownPreviewPayload } from './skills/types.js';
|
|
|
10
10
|
import type { AgentCapabilityCatalogEntry } from './capabilities/index.js';
|
|
11
11
|
import type { AgentServiceConfig, StreamHandle } from './service.types.js';
|
|
12
12
|
import { PersistentGoalService } from './goals/persistent-goal-service.js';
|
|
13
|
+
import type { SkillInstallToolOptions, SkillInstallToolResult } from './tools/skill-install-tool.js';
|
|
13
14
|
import { type InboundAttachmentInput, type MediaRef } from '../channels/attachments/inbound-persist.js';
|
|
14
15
|
export type { AgentServiceConfig, AgentContext, StreamHandle } from './service.types.js';
|
|
15
16
|
export declare class AgentService {
|
|
@@ -113,6 +114,7 @@ export declare class AgentService {
|
|
|
113
114
|
getCapabilityCatalog(sessionKey?: string): AgentCapabilityCatalogEntry[];
|
|
114
115
|
getSkillMarkdownSource(skillName: string): SkillMarkdownPreviewPayload | null;
|
|
115
116
|
refreshSkillsAfterDiskChange(): void;
|
|
117
|
+
installSkillFromSource(opts: SkillInstallToolOptions): Promise<SkillInstallToolResult>;
|
|
116
118
|
refreshSkillsAfterSkillConfigChange(): void;
|
|
117
119
|
getModelForSession(sessionKey: string): string;
|
|
118
120
|
switchModelForSession(sessionKey: string, modelId: string): Promise<boolean>;
|
|
@@ -12,6 +12,8 @@ import { getProjectForSession } from "../projects/workspace.js";
|
|
|
12
12
|
import { getWorkflowProgressBroker } from "./workflow/progress-broker.js";
|
|
13
13
|
import { onSessionTranscriptUpdate } from "../session/transcript-events.js";
|
|
14
14
|
import "./workflow/index.js";
|
|
15
|
+
import { resolveWorkspaceSkillsDir, resolveWorkspaceSkillsLockPath } from "./skills/workspace-skills-dir.js";
|
|
16
|
+
import { normalizeSkillInstallTarget } from "./skills/install-target.js";
|
|
15
17
|
import { persistInboundAttachments } from "../channels/attachments/inbound-persist.js";
|
|
16
18
|
import { AgentManager } from "./agent-manager.js";
|
|
17
19
|
import { maybeRefineSessionTitleWithLlm, maybeSetProvisionalSessionTitle } from "../session/session-title.js";
|
|
@@ -59,6 +61,7 @@ import "./orchestration/index.js";
|
|
|
59
61
|
import { FeedbackCoordinator } from "./feedback/feedback-coordinator.js";
|
|
60
62
|
import "./feedback/index.js";
|
|
61
63
|
import { PersistentGoalService } from "./goals/persistent-goal-service.js";
|
|
64
|
+
import { pullSkillFromSource } from "./skills/hub-pull.js";
|
|
62
65
|
import { existsSync, readFileSync } from "node:fs";
|
|
63
66
|
import { readFile } from "node:fs/promises";
|
|
64
67
|
//#region src/agent/service.ts
|
|
@@ -230,6 +233,7 @@ var AgentService = class {
|
|
|
230
233
|
getWorkItemService: config.getWorkItemService,
|
|
231
234
|
getWorkflowRunService: config.getWorkflowRunService,
|
|
232
235
|
onSkillsUpdated: config.onSkillsUpdated,
|
|
236
|
+
installSkillFromSource: (opts) => this.installSkillFromSource(opts),
|
|
233
237
|
isWorkspaceTrusted: config.isWorkspaceTrusted,
|
|
234
238
|
getSelfVerifyPromptContext: (sessionKey, agentId) => [this.selfVerifyMiddleware.getPendingVerificationContext(sessionKey, agentId), this.turnDiffTracker.buildFinalGuardContext(sessionKey)].filter(Boolean).join("\n\n")
|
|
235
239
|
});
|
|
@@ -302,6 +306,7 @@ var AgentService = class {
|
|
|
302
306
|
this.agentOrchestrator.abort(sessionKey);
|
|
303
307
|
},
|
|
304
308
|
reloadSkills: () => this.refreshSkillsAfterDiskChange(),
|
|
309
|
+
installSkillFromSource: (opts) => this.installSkillFromSource(opts),
|
|
305
310
|
compactSession: (sessionKey, options) => this.sessionInspector.compact(sessionKey, options),
|
|
306
311
|
btwQuery: (sessionKey, question) => this.sessionInspector.btwQuery(sessionKey, question),
|
|
307
312
|
getSessionContextReport: (sessionKey, mode) => this.sessionInspector.report(sessionKey, mode)
|
|
@@ -526,6 +531,24 @@ var AgentService = class {
|
|
|
526
531
|
refreshSkillsAfterDiskChange() {
|
|
527
532
|
this.agentManager.refreshSkillsAfterDiskChange();
|
|
528
533
|
}
|
|
534
|
+
async installSkillFromSource(opts) {
|
|
535
|
+
const target = normalizeSkillInstallTarget(opts.target);
|
|
536
|
+
const workspace = opts.workspace?.trim() || (opts.sessionKey ? this.agentManager.getResolvedWorkspaceForSession(opts.sessionKey) : void 0) || this.workspaceDir;
|
|
537
|
+
const result = await pullSkillFromSource(opts.source, {
|
|
538
|
+
ref: opts.ref,
|
|
539
|
+
subpath: opts.path,
|
|
540
|
+
skillId: opts.skillId,
|
|
541
|
+
force: opts.force ?? false,
|
|
542
|
+
installRoot: target === "workspace" ? resolveWorkspaceSkillsDir(workspace) : void 0,
|
|
543
|
+
lockPath: target === "workspace" ? resolveWorkspaceSkillsLockPath(workspace) : void 0,
|
|
544
|
+
strictScan: opts.strictScan ?? false
|
|
545
|
+
});
|
|
546
|
+
this.refreshSkillsAfterDiskChange();
|
|
547
|
+
return {
|
|
548
|
+
...result,
|
|
549
|
+
target
|
|
550
|
+
};
|
|
551
|
+
}
|
|
529
552
|
refreshSkillsAfterSkillConfigChange() {
|
|
530
553
|
this.agentManager.refreshSkillsAfterSkillConfigChange();
|
|
531
554
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","names":[],"sources":["../../../src/agent/service.ts"],"sourcesContent":["import type { AgentEvent, AgentMessage, ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport type { MessageBus } from '../infra/bus/index.js';\nimport { type Config, getAgentDefaultModelRef } from '../config/schema.js';\nimport {\n maybeRefineSessionTitleWithLlm,\n maybeSetProvisionalSessionTitle,\n} from '../session/session-title.js';\nimport type { ChannelManager } from '../channels/manager.js';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\n\nimport {\n SessionStore,\n SessionConfigStore,\n onSessionTranscriptUpdate,\n effectiveWorkspacePathForSession,\n type CompactionConfig,\n type WindowConfig,\n} from '../session/index.js';\nimport { type ThinkLevel } from './transcript/thinking-types.js';\nimport { createLogger } from '../utils/logger.js';\nimport { ExtensionHookRunner } from '../extensions/index.js';\nimport { extractTextContent } from './context/workspace.js';\nimport { SessionTracker } from './session/tracker.js';\nimport { ModelManager } from './models/index.js';\nimport { initializeCommands } from '../chat-commands/index.js';\nimport { ProgressFeedbackManager } from './lifecycle/progress.js';\nimport { HookHandler } from './lifecycle/hook-handler.js';\nimport { ToolErrorTracker } from './tools/error-tracker.js';\nimport { RequestLimiter } from './models/request-limiter.js';\nimport { SystemReminder } from './prompt/system-reminder.js';\nimport { ToolUsageAnalyzer } from './tools/usage-analyzer.js';\nimport { ToolChainTracker } from './tools/chain-tracker.js';\nimport { ErrorPatternMatcher } from './tools/error-pattern-matcher.js';\nimport { ContextMiddleware, SelfVerifyMiddleware } from './middleware/index.js';\nimport { TurnDiffTracker } from './coding/index.js';\nimport { LifecycleManager } from './lifecycle/index.js';\nimport { CompactionLifecycleHandler } from './lifecycle/handlers/compaction.js';\n\nimport {\n MessageRouter,\n CommandHandler,\n StreamManager,\n OutboundCoordinator,\n} from './messaging/index.js';\nimport { InboundLoop } from './inbound/inbound-loop.js';\nimport { TurnDispatcher } from './inbound/turn-dispatcher.js';\nimport {\n SessionContextManager,\n SessionLifecycleManager,\n SessionStateBag,\n SessionConfigService,\n SessionHydrator,\n SessionInspector,\n type SessionContext,\n} from './session/index.js';\nimport { AgentOrchestrator, AgentEventHandler } from './orchestration/index.js';\nimport {\n getWorkflowProgressBroker,\n type BrokerListenerHandle,\n} from './workflow/index.js';\nimport { FeedbackCoordinator } from './feedback/index.js';\nimport {\n AgentManager,\n type AgentSkillAvailabilityPayload,\n type SkillCatalogEntry,\n type SkillCatalogSnapshot,\n} from './agent-manager.js';\nimport type { SkillMarkdownPreviewPayload } from './skills/types.js';\nimport type { AgentCapabilityCatalogEntry } from './capabilities/index.js';\nimport type { AgentServiceConfig, StreamHandle } from './service.types.js';\nimport { PersistentGoalService } from './goals/persistent-goal-service.js';\nimport { parseNoteAttachmentTarget } from '../notes/attachment-ref.js';\n\nimport {\n resolveAgentHomeDir,\n resolveDefaultAgentId,\n} from './agent-scope.js';\nimport {\n extractProfileAgentId,\n} from '../config/agent-profile.js';\nimport { resolveUserProfilePath } from '../config/paths.js';\nimport { getProjectForSession } from '../projects/workspace.js';\nimport {\n persistInboundAttachments,\n type InboundAttachmentInput,\n type MediaRef,\n} from '../channels/attachments/inbound-persist.js';\nimport { applyConfigOverrides } from '../config/runtime-overrides.js';\n\nexport type { AgentServiceConfig, AgentContext, StreamHandle } from './service.types.js';\n\nconst log = createLogger('AgentService');\n\nfunction assistantMessageText(message: AgentMessage): string {\n const content = (message as { content?: unknown }).content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return content\n .map((block) => {\n if (!block || typeof block !== 'object') return '';\n const rec = block as { type?: unknown; text?: unknown };\n return rec.type === 'text' && typeof rec.text === 'string' ? rec.text : '';\n })\n .join('');\n}\n\nexport class AgentService {\n /**\n * Persistent transcript + session-metadata store. Public so the gateway/TUI\n * can read sessions, delete them, etc. without forcing every CRUD-style\n * operation through a delegation method on `AgentService`.\n */\n readonly sessionStore: SessionStore;\n private sessionConfigStore: SessionConfigStore;\n private hookRunner?: ExtensionHookRunner;\n private agentId: string;\n private workspaceDir: string;\n private config: AgentServiceConfig;\n private sessionTracker: SessionTracker;\n private modelManager: ModelManager;\n private progressManager: ProgressFeedbackManager;\n private hookHandler: HookHandler;\n private lifecycleManager: LifecycleManager;\n private errorTracker: ToolErrorTracker;\n private requestLimiter: RequestLimiter;\n private systemReminder: SystemReminder;\n private toolUsageAnalyzer: ToolUsageAnalyzer;\n private toolChainTracker: ToolChainTracker;\n private errorPatternMatcher: ErrorPatternMatcher;\n private selfVerifyMiddleware: SelfVerifyMiddleware;\n private turnDiffTracker: TurnDiffTracker;\n private contextMiddleware: ContextMiddleware;\n\n private messageRouter: MessageRouter;\n private commandHandler: CommandHandler;\n private streamManager: StreamManager;\n /**\n * Outbound pipeline: typing controller, silence guard, final response publish,\n * extension `message_sending`/`message_sent` hooks, post-turn `webchat_turn_complete`\n * event. Public so the gateway / channels can drive it directly.\n */\n readonly outboundCoordinator: OutboundCoordinator;\n private inboundLoop: InboundLoop;\n /**\n * Direct-turn entry points: `processDirect` (one-shot), `processDirectStreaming`\n * (SSE generator), webchat steering and SSE injection. Public so the gateway,\n * TUI, CLI, and automations do not need to thread every call through `AgentService`.\n */\n readonly turnDispatcher: TurnDispatcher;\n /**\n * `/goal` runtime: continuation scheduling, persistent-goal API factory,\n * stream-outcome state, post-turn verdict. Public so the gateway can wire\n * the webchat continuation scheduler and read stream outcomes directly.\n */\n readonly persistentGoals: PersistentGoalService;\n /**\n * Per-session config writes (model / thinking / reasoning / working directory).\n * Public so REST endpoints and CLI flows can hit it without going through a\n * monolithic patch entrypoint on `AgentService`.\n */\n readonly sessionConfig: SessionConfigService;\n /**\n * Hydration — read persisted per-session config and apply it to the runtime\n * (AgentManager / ModelManager). The mirror image of `sessionConfig`: writes\n * go through `sessionConfig`, reads-into-runtime go through `sessionHydrator`.\n */\n readonly sessionHydrator: SessionHydrator;\n /**\n * Read-only introspection (compaction, /context report, /btw, contextUsage,\n * agentConfig view). Public so REST endpoints and CLI flows can query a\n * session's view without going through delegating methods on `AgentService`.\n */\n readonly sessionInspector: SessionInspector;\n private sessionContextManager: SessionContextManager;\n private sessionLifecycleManager: SessionLifecycleManager;\n private agentOrchestrator: AgentOrchestrator;\n private agentEventHandler: AgentEventHandler;\n private workflowProgressBrokerHandle: BrokerListenerHandle | null = null;\n private feedbackCoordinator: FeedbackCoordinator;\n private agentManager: AgentManager;\n\n /**\n * Unified per-session state container (replaces six ad-hoc Maps). Owns webchat\n * publishers, last assistant text, embedded stream buffer, persistent-goal stream\n * outcomes, concurrent-turn depth, and event-listener unsubscribers; runs a TTL\n * sweep for slots that have no explicit owner.\n */\n private sessionState = new SessionStateBag();\n\n /** Gateway: notify UI after direct `SessionStore.updateMetadata` (no SessionManager emit). */\n private onSessionMetadataUpdated?: (sessionKey: string, patch?: { name?: string }) => void;\n private onSessionTranscriptUpdated?: (sessionKey: string) => void;\n\n private effectiveAppConfig(): Config | undefined {\n const base = this.config.config;\n return base ? applyConfigOverrides(base) : undefined;\n }\n\n constructor(bus: MessageBus, config: AgentServiceConfig) {\n this.config = config;\n this.onSessionMetadataUpdated = config.onSessionMetadataUpdated;\n this.onSessionTranscriptUpdated = config.onSessionTranscriptUpdated;\n this.agentId = `agent-${Date.now()}`;\n this.workspaceDir = config.workspace;\n\n this.sessionTracker = new SessionTracker();\n this.modelManager = new ModelManager({\n defaultModel: config.model,\n config: config.config,\n });\n\n initializeCommands();\n log.debug('Command system initialized');\n\n this.sessionStore = config.sessionStore ?? this.createSessionStore();\n onSessionTranscriptUpdate((update) => {\n void this.sessionStore.syncEmbeddedTranscriptUpdate(update).catch((err) => {\n log.warn(\n { err, sessionKey: update.sessionKey },\n 'Transcript index sync failed',\n );\n });\n const sk = update.sessionKey?.trim();\n if (sk) {\n this.onSessionTranscriptUpdated?.(sk);\n }\n });\n const appCfgForPaths = this.config.config;\n if (!appCfgForPaths) {\n throw new Error('AgentService requires config.config for session paths');\n }\n const defaultAid = resolveDefaultAgentId(appCfgForPaths);\n const defaultAgentHome = resolveAgentHomeDir(appCfgForPaths, defaultAid);\n this.sessionConfigStore = new SessionConfigStore(defaultAgentHome);\n\n this.hookRunner = this.createHookRunner();\n this.hookHandler = new HookHandler({\n hookRunner: this.hookRunner,\n agentId: this.agentId,\n get sessionKey() { return this.currentContext?.sessionKey; },\n });\n\n this.progressManager = this.createProgressManager();\n this.initializeReliabilityModules();\n\n this.lifecycleManager = new LifecycleManager();\n this.initializeLifecycleHandlers();\n\n this.streamManager = new StreamManager();\n this.sessionContextManager = new SessionContextManager();\n this.feedbackCoordinator = new FeedbackCoordinator({\n progressManager: this.progressManager,\n bus,\n });\n\n this.agentManager = new AgentManager({\n workspace: config.workspace,\n model: config.model,\n config: config.config,\n extensionRegistry: config.extensionRegistry,\n hookRunner: this.hookRunner,\n bus,\n getCurrentContext: () => this.sessionContextManager.getContext(),\n getSessionStore: () => this.sessionStore,\n getModelManager: () => this.modelManager,\n thinkingLevel: config.thinkingLevel,\n reasoningLevel: config.reasoningLevel,\n verboseLevel: config.verboseLevel,\n gatewayClarify: config.gatewayClarify,\n getAutomationService: config.getAutomationService,\n getNotesService: config.getNotesService,\n getProjectService: config.getProjectService,\n getWorkItemService: config.getWorkItemService,\n getWorkflowRunService: config.getWorkflowRunService,\n onSkillsUpdated: config.onSkillsUpdated,\n isWorkspaceTrusted: config.isWorkspaceTrusted,\n getSelfVerifyPromptContext: (sessionKey, agentId) => [\n this.selfVerifyMiddleware.getPendingVerificationContext(sessionKey, agentId),\n this.turnDiffTracker.buildFinalGuardContext(sessionKey),\n ].filter(Boolean).join('\\n\\n'),\n });\n\n this.agentEventHandler = new AgentEventHandler({\n progressManager: this.progressManager,\n errorTracker: this.errorTracker,\n requestLimiter: this.requestLimiter,\n lifecycleManager: this.lifecycleManager,\n toolChainTracker: this.toolChainTracker,\n selfVerifyMiddleware: this.selfVerifyMiddleware,\n systemReminder: this.systemReminder,\n toolUsageAnalyzer: this.toolUsageAnalyzer,\n errorPatternMatcher: this.errorPatternMatcher,\n turnDiffTracker: this.turnDiffTracker,\n });\n\n // Wire the workflow progress broker into the session event bus. Channel\n // extensions self-register their capability against the singleton at\n // boot; the broker only forwards updates to whatever has registered, so\n // this is safe even when no channel is configured.\n this.workflowProgressBrokerHandle = getWorkflowProgressBroker().attachTo(\n this.agentEventHandler,\n );\n\n // sessionHydrator is constructed early because AgentOrchestrator + InboundLoop +\n // TurnDispatcher all need it; the SessionConfigService instance below also\n // shares the same constructor parameters.\n this.sessionHydrator = new SessionHydrator({\n sessionConfigStore: this.sessionConfigStore,\n agentManager: this.agentManager,\n modelManager: this.modelManager,\n getConfig: () => this.effectiveAppConfig(),\n });\n\n this.agentOrchestrator = new AgentOrchestrator({\n agentManager: this.agentManager,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n eventHandler: this.agentEventHandler,\n feedbackCoordinator: this.feedbackCoordinator,\n sessionConfigStore: this.sessionConfigStore,\n sessionHydrator: this.sessionHydrator,\n getConfig: () => this.effectiveAppConfig(),\n getThinkingDefault: () => undefined,\n getThinkingDefaultForSession: (sessionKey: string) =>\n this.agentManager.getThinkingDefaultForSession(sessionKey),\n workspaceRoot: this.workspaceDir,\n getWorkspaceRootForSession: (sessionKey: string) =>\n this.agentManager.getResolvedWorkspaceForSession(sessionKey),\n getAgentInternalStorageRootForSession: (sessionKey: string) =>\n resolveAgentHomeDir(this.config.config!, extractProfileAgentId(sessionKey, this.config.config!)),\n enqueueAutoTitle: (sessionKey: string) => this.enqueueMaybeAutoTitleAfterPersist(sessionKey),\n onEmbeddedStreamEvent: (sessionKey, event) => {\n const ctx = this.sessionContextManager.getContext();\n if (!ctx || ctx.sessionKey !== sessionKey) {\n return;\n }\n if (event.type === 'message_update' && event.message.role === 'assistant') {\n const next = assistantMessageText(event.message);\n this.streamManager.update(next);\n }\n },\n onEmbeddedTurnComplete: (sessionKey, text) => {\n if (text) {\n this.sessionState.setLastAssistantText(sessionKey, text);\n }\n this.sessionState.clearEmbeddedStreamText(sessionKey);\n },\n });\n\n this.messageRouter = new MessageRouter();\n this.commandHandler = new CommandHandler({\n config: config.config!,\n bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n getPersistentGoalApisForCommand: (routing) => this.persistentGoals.buildApisForRouting(routing),\n getWorkflowRunService: config.getWorkflowRunService,\n applySessionThinkingLevel: (sessionKey: string, level: ThinkLevel) => {\n this.agentManager.setThinkingLevel(sessionKey, level as ThinkingLevel);\n },\n getCurrentModel: () => this.agentOrchestrator.getCurrentModel(),\n switchModelForSession: (sessionKey: string, modelId: string) =>\n this.switchModelForSession(sessionKey, modelId),\n invalidateAgentSession: (sessionKey: string) => {\n this.agentManager.removeAgent(sessionKey);\n },\n resetSession: (sessionKey: string) => this.resetSession(sessionKey),\n abortSessionTurn: async (sessionKey: string) => {\n await this.streamManager.abort();\n this.agentOrchestrator.abort(sessionKey);\n },\n reloadSkills: () => this.refreshSkillsAfterDiskChange(),\n compactSession: (sessionKey, options) => this.sessionInspector.compact(sessionKey, options),\n btwQuery: (sessionKey, question) => this.sessionInspector.btwQuery(sessionKey, question),\n getSessionContextReport: (sessionKey, mode) => this.sessionInspector.report(sessionKey, mode),\n });\n\n this.sessionLifecycleManager = new SessionLifecycleManager(\n this.sessionStore,\n this.sessionTracker,\n this.lifecycleManager\n );\n\n this.persistentGoals = new PersistentGoalService({\n bus,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n sessionState: this.sessionState,\n getConfig: () => this.effectiveAppConfig(),\n getResolvedWorkspaceForSession: (sk) => this.agentManager.getResolvedWorkspaceForSession(sk),\n onSessionMetadataUpdated: this.onSessionMetadataUpdated,\n onGoalStatusUpdated: config.onGoalStatusUpdated,\n notifyWebchatTranscriptAppend: (sk, text) => this.turnDispatcher.notifyWebchatTranscriptAppend(sk, text),\n });\n\n this.sessionConfig = new SessionConfigService({\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n modelManager: this.modelManager,\n agentManager: this.agentManager,\n getConfig: () => this.effectiveAppConfig(),\n });\n\n this.sessionInspector = new SessionInspector({\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n modelManager: this.modelManager,\n agentManager: this.agentManager,\n sessionHydrator: this.sessionHydrator,\n getConfig: () => this.effectiveAppConfig(),\n getContextWindow: (sessionKey) => this.getContextWindowForSession(sessionKey),\n });\n\n this.outboundCoordinator = new OutboundCoordinator({\n bus,\n hookHandler: this.hookHandler,\n streamManager: this.streamManager,\n getConfig: () => this.effectiveAppConfig(),\n getLastAssistantPlainText: (sk) => this.getLastAssistantPlainText(sk),\n runPersistentGoalPostTurn: (payload) => this.persistentGoals.runPostTurn(payload),\n });\n\n this.turnDispatcher = new TurnDispatcher({\n log,\n agentManager: this.agentManager,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n sessionConfigStore: this.sessionConfigStore,\n sessionState: this.sessionState,\n commandHandler: this.commandHandler,\n getConfig: () => this.effectiveAppConfig(),\n requireConfig: () => {\n const c = this.config.config;\n if (!c) throw new Error('AgentService requires config.config');\n return c;\n },\n resolveSessionEndpoint: (sk) => this.resolveSessionEndpoint(sk),\n initSessionContext: (sk, channel, chatId) => this.initSessionContext(sk, channel, chatId),\n sessionHydrator: this.sessionHydrator,\n prepareInboundAttachments: (sk, att) => this.prepareInboundAttachments(sk, att),\n enqueueMaybeAutoTitleAfterPersist: (sk) => this.enqueueMaybeAutoTitleAfterPersist(sk),\n enqueueProvisionalSessionTitle: (sk, text) => this.enqueueProvisionalSessionTitle(sk, text),\n endDirectRequestContext: () => this.endDirectRequestContext(),\n onSessionTranscriptUpdated: this.onSessionTranscriptUpdated,\n resetSession: (sk) => this.resetSession(sk),\n sourceContextResolver: config.sourceContextResolver,\n });\n\n this.inboundLoop = new InboundLoop({\n log,\n agentId: this.agentId,\n bus,\n hookHandler: this.hookHandler,\n messageRouter: this.messageRouter,\n commandHandler: this.commandHandler,\n sessionContextManager: this.sessionContextManager,\n feedbackCoordinator: this.feedbackCoordinator,\n agentManager: this.agentManager,\n sessionLifecycleManager: this.sessionLifecycleManager,\n agentOrchestrator: this.agentOrchestrator,\n outboundCoordinator: this.outboundCoordinator,\n streamManager: this.streamManager,\n sessionState: this.sessionState,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n setupSessionEventHandling: (sk) => this.setupSessionEventHandling(sk),\n sessionHydrator: this.sessionHydrator,\n getLastAssistantPlainText: (sk) => this.getLastAssistantPlainText(sk),\n checkAndCompact: (sk, msgs) => this.checkAndCompact(sk, msgs),\n enqueueMaybeAutoTitleAfterPersist: (sk) => this.enqueueMaybeAutoTitleAfterPersist(sk),\n getConfig: () => this.effectiveAppConfig(),\n resetSession: (sk) => this.resetSession(sk),\n setStreamHandle: (handle) => this.setStreamHandle(handle),\n });\n\n // Register signal handlers only if not running as an Electron subprocess.\n // In Electron, the parent process manages the lifecycle and signals should not trigger disposal.\n const isElectronSubprocess = !!process.env.ELECTRON_RUN_AS_NODE;\n if (!isElectronSubprocess) {\n process.on('SIGINT', () => this.dispose());\n process.on('SIGTERM', () => this.dispose());\n }\n\n log.info('AgentService initialized');\n }\n\n private createSessionStore(): SessionStore {\n const windowConfig: Partial<WindowConfig> = {\n maxMessages: 100,\n keepRecentMessages: 20,\n preserveSystemMessages: true,\n };\n const compactionConfig: Partial<CompactionConfig> = {\n enabled: true,\n mode: 'abstractive',\n reserveTokens: 8000,\n triggerThreshold: 0.8,\n minMessagesBeforeCompact: 10,\n keepRecentMessages: 10,\n evictionWindow: 0.2,\n retentionWindow: 6,\n };\n const appCfg = this.config.config;\n if (!appCfg) {\n throw new Error('AgentService requires config.config for session store paths');\n }\n return new SessionStore(\n {\n config: appCfg,\n },\n windowConfig,\n compactionConfig,\n );\n }\n\n private createHookRunner(): ExtensionHookRunner | undefined {\n if (!this.config.extensionRegistry) return undefined;\n\n return new ExtensionHookRunner(this.config.extensionRegistry, {\n catchErrors: true,\n logger: {\n info: (msg: string) => log.info({ hook: true }, msg),\n warn: (msg: string) => log.warn({ hook: true }, msg),\n error: (msg: string) => log.error({ hook: true }, msg),\n },\n });\n }\n\n private createProgressManager(): ProgressFeedbackManager {\n return new ProgressFeedbackManager({\n level: 'normal',\n showThinking: true,\n streamToolProgress: true,\n heartbeatEnabled: true,\n heartbeatIntervalMs: 20000,\n longTaskThresholdMs: 30000,\n });\n }\n\n private initializeReliabilityModules(): void {\n this.errorTracker = new ToolErrorTracker({\n maxFailuresPerTool: 3,\n maxTotalFailures: 5,\n resetOnTurnEnd: true,\n });\n\n this.selfVerifyMiddleware = new SelfVerifyMiddleware({\n maxEditsPerFile: 5,\n enablePreCompletionCheck: true,\n minTurnsForVerification: 4,\n resetOnVerification: true,\n });\n this.turnDiffTracker = new TurnDiffTracker();\n\n this.requestLimiter = new RequestLimiter({\n maxRequestsPerTurn: 50,\n warnThreshold: 0.8,\n softLimit: false,\n });\n\n this.systemReminder = new SystemReminder({\n enabled: true,\n appendToToolResults: true,\n maxRemindersPerTurn: 3,\n });\n\n this.toolUsageAnalyzer = new ToolUsageAnalyzer({\n enabled: true,\n lowUsageThreshold: 5,\n veryLowUsageThreshold: 1,\n minCallsForAnalysis: 100,\n reportIntervalMs: 60 * 60 * 1000,\n });\n\n this.toolChainTracker = new ToolChainTracker({\n enabled: true,\n maxChainsPerSession: 10,\n maxNodesPerChain: 100,\n trackParams: true,\n trackResults: true,\n autoPrune: true,\n });\n\n this.errorPatternMatcher = new ErrorPatternMatcher({\n enabled: true,\n defaultMaxRetries: 1,\n logMatches: true,\n });\n\n // Initialize context middleware for automatic request tracking\n this.contextMiddleware = new ContextMiddleware();\n }\n\n private initializeLifecycleHandlers(): void {\n this.lifecycleManager.on('llm_response', new CompactionLifecycleHandler({\n minMessages: 20,\n maxTokens: 8000,\n preserveReasoning: true,\n accumulateUsage: true,\n }));\n\n log.debug(\n { handlers: this.lifecycleManager.getRegisteredHandlers() },\n 'Lifecycle handlers initialized'\n );\n }\n\n setChannelManager(channelManager: ChannelManager): void {\n this.inboundLoop.setChannelManager(channelManager);\n }\n\n /**\n * Apply config after save or hot reload so the default model updates without restarting the gateway.\n */\n applyAgentDefaultsFromConfig(config: Config): void {\n this.config.config = config;\n const ref = getAgentDefaultModelRef(config);\n this.config.model = ref;\n this.modelManager.updateFromConfig(config);\n this.agentManager.updateAgentDefaults(config);\n this.commandHandler.updateAgentConfig(config);\n }\n\n getSkillCatalog(): SkillCatalogEntry[] {\n return this.agentManager.getSkillCatalog();\n }\n\n getSkillCatalogSnapshot(): SkillCatalogSnapshot {\n return this.agentManager.getSkillCatalogSnapshot();\n }\n\n getAgentSkillAvailability(agentId: string): AgentSkillAvailabilityPayload {\n return this.agentManager.getAgentSkillAvailability(agentId);\n }\n\n getCapabilityCatalog(sessionKey?: string): AgentCapabilityCatalogEntry[] {\n return this.agentManager.getCapabilityCatalogForSession(sessionKey);\n }\n\n getSkillMarkdownSource(skillName: string): SkillMarkdownPreviewPayload | null {\n return this.agentManager.getSkillMarkdownSource(skillName);\n }\n\n refreshSkillsAfterDiskChange(): void {\n this.agentManager.refreshSkillsAfterDiskChange();\n }\n\n refreshSkillsAfterSkillConfigChange(): void {\n this.agentManager.refreshSkillsAfterSkillConfigChange();\n }\n\n getModelForSession(sessionKey: string): string {\n return this.modelManager.getModelForSession(sessionKey);\n }\n\n async switchModelForSession(sessionKey: string, modelId: string): Promise<boolean> {\n const ok = await this.modelManager.switchModelForSession(sessionKey, modelId);\n if (!ok) return false;\n await this.sessionConfigStore.update(sessionKey, { modelOverride: modelId });\n const result = this.agentManager.setModelForSession(sessionKey, modelId);\n if (result) {\n this.sessionTracker.touchSession(sessionKey);\n }\n return true;\n }\n\n /**\n * Clears per-session model override so the next turn uses the configured agent default\n * (e.g. cron isolated job with no explicit model).\n */\n async resetSessionModelToAgentDefault(sessionKey: string): Promise<void> {\n await this.sessionConfig.clearModelOverride(sessionKey);\n }\n\n setStreamHandle(handle: StreamHandle): void {\n this.streamManager.setHandle(handle);\n this.feedbackCoordinator.setStreamHandle(handle);\n }\n\n clearStreamHandle(): void {\n this.streamManager.clearHandle();\n this.feedbackCoordinator.endTask();\n }\n\n /** Last assistant visible plain text for a session (e.g. after a webchat stream). */\n getLastAssistantPlainText(sessionKey: string): string {\n return (\n this.sessionState.getLastAssistantText(sessionKey) ??\n this.agentManager.getLastAssistantContent(sessionKey) ??\n ''\n );\n }\n\n beginInboundTurn(sessionKey: string): void {\n this.sessionState.beginInboundTurn(sessionKey);\n }\n\n endInboundTurn(sessionKey: string): void {\n this.sessionState.endInboundTurn(sessionKey);\n }\n\n getInboundTurnDepth(sessionKey: string): number {\n return this.sessionState.getInboundTurnDepth(sessionKey);\n }\n\n async start(): Promise<void> {\n await this.sessionConfigStore.initialize();\n await this.hookHandler.trigger('gateway_start', { port: 0, host: 'cli' });\n log.debug('Agent service started');\n void this.inboundLoop.start().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage: em }, `Inbound loop failed: ${em}`);\n });\n }\n\n stop(): Promise<void> {\n this.inboundLoop.stop();\n this.agentManager.dispose();\n this.dispose();\n\n this.hookHandler.trigger('gateway_stop', { reason: 'stopped' });\n log.debug('Agent service stopped');\n return Promise.resolve();\n }\n\n async reconcileDreamingNow(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Persist agent messages with the same sanitizer + transcript hygiene as AgentOrchestrator.\n * Uses persistence hygiene so `thinking` blocks remain on disk for the web UI (LLM load path still drops them).\n */\n private notifySessionTitleUpdated(sessionKey: string, name: string): void {\n this.onSessionMetadataUpdated?.(sessionKey, { name });\n }\n\n /** Fire-and-forget provisional title from first user text (webchat sidebar). */\n enqueueProvisionalSessionTitle(sessionKey: string, userText: string): void {\n void (async () => {\n try {\n await maybeSetProvisionalSessionTitle(\n this.sessionStore,\n sessionKey,\n userText,\n (sk, name) => this.notifySessionTitleUpdated(sk, name),\n );\n } catch (err) {\n log.warn({ err, sessionKey }, 'Provisional session title failed');\n }\n })();\n }\n\n /**\n * Fire-and-forget LLM refine after turn persist; skips user-locked and finalized LLM titles.\n */\n private enqueueMaybeAutoTitleAfterPersist(sessionKey: string): void {\n void (async () => {\n try {\n let modelRef =\n getAgentDefaultModelRef(this.config.config ?? ({} as Config)) ?? this.config.model;\n if (!modelRef?.trim()) {\n try {\n modelRef = this.modelManager.getModelForSession(sessionKey);\n } catch {\n modelRef = undefined;\n }\n }\n await maybeRefineSessionTitleWithLlm(\n this.sessionStore,\n sessionKey,\n modelRef?.trim() || undefined,\n (sk, name) => this.notifySessionTitleUpdated(sk, name),\n );\n } catch (err) {\n log.warn({ err, sessionKey }, 'Auto session title failed');\n }\n })();\n }\n\n private async resolveSessionEndpoint(sessionKey: string): Promise<{ channel: string; chatId: string }> {\n const metadata = await this.sessionStore.getMetadata(sessionKey).catch(() => null);\n const channel = metadata?.routing?.source?.trim() || metadata?.sourceChannel?.trim() || 'cli';\n const chatId = metadata?.routing?.peerId?.trim() || metadata?.sourceChatId?.trim() || 'main';\n return { channel, chatId };\n }\n\n private initSessionContext(\n sessionKey: string,\n channel: string,\n chatId: string,\n senderId = '',\n ): SessionContext {\n const context: SessionContext = {\n sessionKey,\n channel,\n chatId,\n senderId,\n isGroup: false,\n };\n\n this.contextMiddleware.onRequest({\n sessionKey,\n userId: context.senderId,\n channel,\n chatId,\n });\n\n // Direct turn entry points (one-shot + streaming generator) cannot wrap their\n // body in `sessionContextManager.runWith(ctx, fn)` cleanly — the streaming\n // path is an async generator, and both flows already use a try/finally for\n // side-effect cleanup. We use `enter` so the context is visible via ALS for\n // every async resource launched after this call returns. The context is\n // overwritten or cleared by the next direct turn (each direct turn calls\n // `initSessionContext` first), so there is no cross-session leak in practice.\n this.sessionContextManager.enter(context);\n this.feedbackCoordinator.setContext(context);\n this.agentManager.getOrCreateAgent(sessionKey);\n this.setupSessionEventHandling(sessionKey);\n\n return context;\n }\n\n /**\n * Persist inbound file attachments to the global media store.\n */\n async prepareInboundAttachments(\n _sessionKey: string,\n attachments?: InboundAttachmentInput[],\n ): Promise<MediaRef[] | undefined> {\n return persistInboundAttachments(attachments, {\n resolveUri: async (uri) => {\n const target = parseNoteAttachmentTarget(uri);\n if (!target) return null;\n const notesService = (this as unknown as {\n notesServiceInstance?: {\n getAttachmentPath(noteId: string, attachmentId: string): Promise<{\n filePath: string;\n mimeType?: string;\n fileName?: string;\n } | null>;\n };\n }).notesServiceInstance;\n if (!notesService) return null;\n const located = await notesService.getAttachmentPath(target.noteId, target.attachmentId);\n if (!located) return null;\n const buffer = await readFile(located.filePath);\n return {\n buffer,\n path: located.filePath,\n mimeType: located.mimeType,\n name: located.fileName,\n size: buffer.byteLength,\n };\n },\n });\n }\n\n private endDirectRequestContext(): void {\n // `sessionContextManager` is ALS-backed: the context for the current async\n // chain drops automatically when the chain unwinds. The feedback\n // coordinator + context middleware still use singleton state, so clear\n // them explicitly here.\n this.feedbackCoordinator.clearContext();\n this.contextMiddleware.onResponse();\n }\n\n /**\n * Reset a session's transcript and drop the in-memory agent so the next turn\n * reloads from disk. Combines two collaborators (sessionStore + agentManager)\n * so it stays on `AgentService`; pure sessionStore reads should use\n * `agentService.sessionStore.*` directly.\n */\n async clearSessionMessages(key: string): Promise<void> {\n await this.sessionStore.saveMessages(key, []);\n this.agentManager.removeAgent(key);\n }\n\n /**\n * Reset session transcript (archive + new session id) and evict in-memory agent state.\n * Preserves the session key and persisted per-session overrides.\n */\n async resetSession(\n key: string,\n ): Promise<{ sessionId: string; previousSessionId: string } | null> {\n const { abortEmbeddedRun } = await import('./embedded/runs.js');\n const { retireSessionMcpRuntimeForSessionKey } = await import('./mcp/bundle-mcp-tools.js');\n await abortEmbeddedRun(key);\n const outcome = await this.sessionStore.reset(key);\n if (!outcome) {\n return null;\n }\n this.agentManager.removeAgent(key);\n await retireSessionMcpRuntimeForSessionKey({ sessionKey: key, reason: 'session-reset' });\n return outcome;\n }\n\n /**\n * Drop in-memory agent so the next turn reloads transcript from disk (e.g. after checkpoint restore).\n */\n evictSessionAgent(sessionKey: string): void {\n this.agentManager.removeAgent(sessionKey);\n }\n\n /**\n * Load session working directory override into AgentManager, ensure directory exists.\n * Call before AgentManager.getOrCreateAgent for this session.\n */\n /** Workspace root for UI file tree / editor (same as agent tools after hydration). */\n async getEffectiveWorkspacePathForSession(sessionKey: string): Promise<string> {\n await this.sessionHydrator.workspace(sessionKey);\n const cfg = this.config.config!;\n const sc = await this.sessionConfigStore.get(sessionKey);\n return effectiveWorkspacePathForSession(cfg, sessionKey, sc, getProjectForSession(sessionKey));\n }\n\n /**\n * Best-effort timezone resolution for webchat envelope timestamps.\n * Reads the global user profile and extracts a `Timezone:` line.\n */\n resolveUserTimezoneForSession(sessionKey: string): string | undefined {\n void sessionKey;\n try {\n const cfg = this.effectiveAppConfig();\n if (!cfg) return undefined;\n const userPath = resolveUserProfilePath();\n if (!existsSync(userPath)) return undefined;\n const raw = readFileSync(userPath, 'utf-8');\n const match = raw.match(/Timezone:\\s*(.+)/i);\n const tz = match?.[1]?.trim();\n return tz || undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Setup event handling for a specific session\n */\n private setupSessionEventHandling(sessionKey: string): void {\n if (this.sessionState.hasSessionEventUnsubscriber(sessionKey)) {\n return;\n }\n\n const unsubscribe = this.agentManager.subscribeToSession(sessionKey, (event) => {\n this.handleSessionEvent(sessionKey, event);\n });\n\n if (unsubscribe) {\n this.sessionState.setSessionEventUnsubscriber(sessionKey, unsubscribe);\n }\n }\n\n /**\n * Handle events from a specific session's agent\n */\n private handleSessionEvent(sessionKey: string, event: AgentEvent): void {\n const currentContext = this.sessionContextManager.getContext();\n if (!currentContext) {\n // Inbound `finally` clears context before trailing agent `message_update` events finish — ignore (not a bug).\n return;\n }\n\n if (currentContext.sessionKey !== sessionKey) {\n // Event from a different session — still process with current context where applicable\n this.agentEventHandler.handle(event, currentContext);\n return;\n }\n\n // Handle streaming updates for the current session\n if (event.type === 'message_update') {\n const msgEvent = event as Extract<AgentEvent, { type: 'message_update' }>;\n if (msgEvent.message?.role === 'assistant') {\n const content = msgEvent.message.content;\n const text = Array.isArray(content)\n ? extractTextContent(content as Array<{ type: string; text?: string }>)\n : String(content);\n\n this.streamManager.update(text);\n }\n }\n\n this.agentEventHandler.handle(event, currentContext);\n }\n\n private async checkAndCompact(sessionKey: string, messages: AgentMessage[]): Promise<void> {\n const contextWindow = this.getContextWindowForSession(sessionKey);\n const prep = this.sessionStore.prepareCompaction(sessionKey, messages, contextWindow);\n if (!prep.needsCompaction) return;\n\n log.info({ sessionKey, reason: prep.stats?.reason, usagePercent: prep.stats?.usagePercent }, 'Session needs compaction');\n\n const result = await this.sessionStore.compact(sessionKey, messages, contextWindow, undefined, false);\n await this.hookHandler.trigger('after_compaction', {\n messageCount: messages.length,\n tokenCount: result.tokensBefore,\n compactedCount: messages.length - result.firstKeptIndex,\n });\n log.info({ sessionKey, tokensBefore: result.tokensBefore, tokensAfter: result.tokensAfter }, 'Session compacted');\n }\n\n private getContextWindowForSession(sessionKey: string): number {\n try {\n return this.modelManager.getResolvedModelForSession(sessionKey).contextWindow ?? 128000;\n } catch (err) {\n log.warn({ err, sessionKey }, 'Failed to resolve session context window; using default');\n return 128000;\n }\n }\n\n private dispose(): void {\n this.sessionTracker.dispose();\n this.sessionState.disposeAll();\n this.agentManager.dispose();\n this.workflowProgressBrokerHandle?.dispose();\n this.workflowProgressBrokerHandle = null;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"service.js","names":[],"sources":["../../../src/agent/service.ts"],"sourcesContent":["import type { AgentEvent, AgentMessage, ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport type { MessageBus } from '../infra/bus/index.js';\nimport { type Config, getAgentDefaultModelRef } from '../config/schema.js';\nimport {\n maybeRefineSessionTitleWithLlm,\n maybeSetProvisionalSessionTitle,\n} from '../session/session-title.js';\nimport type { ChannelManager } from '../channels/manager.js';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\n\nimport {\n SessionStore,\n SessionConfigStore,\n onSessionTranscriptUpdate,\n effectiveWorkspacePathForSession,\n type CompactionConfig,\n type WindowConfig,\n} from '../session/index.js';\nimport { type ThinkLevel } from './transcript/thinking-types.js';\nimport { createLogger } from '../utils/logger.js';\nimport { ExtensionHookRunner } from '../extensions/index.js';\nimport { extractTextContent } from './context/workspace.js';\nimport { SessionTracker } from './session/tracker.js';\nimport { ModelManager } from './models/index.js';\nimport { initializeCommands } from '../chat-commands/index.js';\nimport { ProgressFeedbackManager } from './lifecycle/progress.js';\nimport { HookHandler } from './lifecycle/hook-handler.js';\nimport { ToolErrorTracker } from './tools/error-tracker.js';\nimport { RequestLimiter } from './models/request-limiter.js';\nimport { SystemReminder } from './prompt/system-reminder.js';\nimport { ToolUsageAnalyzer } from './tools/usage-analyzer.js';\nimport { ToolChainTracker } from './tools/chain-tracker.js';\nimport { ErrorPatternMatcher } from './tools/error-pattern-matcher.js';\nimport { ContextMiddleware, SelfVerifyMiddleware } from './middleware/index.js';\nimport { TurnDiffTracker } from './coding/index.js';\nimport { LifecycleManager } from './lifecycle/index.js';\nimport { CompactionLifecycleHandler } from './lifecycle/handlers/compaction.js';\n\nimport {\n MessageRouter,\n CommandHandler,\n StreamManager,\n OutboundCoordinator,\n} from './messaging/index.js';\nimport { InboundLoop } from './inbound/inbound-loop.js';\nimport { TurnDispatcher } from './inbound/turn-dispatcher.js';\nimport {\n SessionContextManager,\n SessionLifecycleManager,\n SessionStateBag,\n SessionConfigService,\n SessionHydrator,\n SessionInspector,\n type SessionContext,\n} from './session/index.js';\nimport { AgentOrchestrator, AgentEventHandler } from './orchestration/index.js';\nimport {\n getWorkflowProgressBroker,\n type BrokerListenerHandle,\n} from './workflow/index.js';\nimport { FeedbackCoordinator } from './feedback/index.js';\nimport {\n AgentManager,\n type AgentSkillAvailabilityPayload,\n type SkillCatalogEntry,\n type SkillCatalogSnapshot,\n} from './agent-manager.js';\nimport type { SkillMarkdownPreviewPayload } from './skills/types.js';\nimport type { AgentCapabilityCatalogEntry } from './capabilities/index.js';\nimport type { AgentServiceConfig, StreamHandle } from './service.types.js';\nimport { PersistentGoalService } from './goals/persistent-goal-service.js';\nimport { parseNoteAttachmentTarget } from '../notes/attachment-ref.js';\nimport { pullSkillFromSource } from './skills/hub-pull.js';\nimport {\n resolveWorkspaceSkillsDir,\n resolveWorkspaceSkillsLockPath,\n} from './skills/workspace-skills-dir.js';\nimport { normalizeSkillInstallTarget } from './skills/install-target.js';\nimport type {\n SkillInstallToolOptions,\n SkillInstallToolResult,\n} from './tools/skill-install-tool.js';\n\nimport {\n resolveAgentHomeDir,\n resolveDefaultAgentId,\n} from './agent-scope.js';\nimport {\n extractProfileAgentId,\n} from '../config/agent-profile.js';\nimport { resolveUserProfilePath } from '../config/paths.js';\nimport { getProjectForSession } from '../projects/workspace.js';\nimport {\n persistInboundAttachments,\n type InboundAttachmentInput,\n type MediaRef,\n} from '../channels/attachments/inbound-persist.js';\nimport { applyConfigOverrides } from '../config/runtime-overrides.js';\n\nexport type { AgentServiceConfig, AgentContext, StreamHandle } from './service.types.js';\n\nconst log = createLogger('AgentService');\n\nfunction assistantMessageText(message: AgentMessage): string {\n const content = (message as { content?: unknown }).content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return content\n .map((block) => {\n if (!block || typeof block !== 'object') return '';\n const rec = block as { type?: unknown; text?: unknown };\n return rec.type === 'text' && typeof rec.text === 'string' ? rec.text : '';\n })\n .join('');\n}\n\nexport class AgentService {\n /**\n * Persistent transcript + session-metadata store. Public so the gateway/TUI\n * can read sessions, delete them, etc. without forcing every CRUD-style\n * operation through a delegation method on `AgentService`.\n */\n readonly sessionStore: SessionStore;\n private sessionConfigStore: SessionConfigStore;\n private hookRunner?: ExtensionHookRunner;\n private agentId: string;\n private workspaceDir: string;\n private config: AgentServiceConfig;\n private sessionTracker: SessionTracker;\n private modelManager: ModelManager;\n private progressManager: ProgressFeedbackManager;\n private hookHandler: HookHandler;\n private lifecycleManager: LifecycleManager;\n private errorTracker: ToolErrorTracker;\n private requestLimiter: RequestLimiter;\n private systemReminder: SystemReminder;\n private toolUsageAnalyzer: ToolUsageAnalyzer;\n private toolChainTracker: ToolChainTracker;\n private errorPatternMatcher: ErrorPatternMatcher;\n private selfVerifyMiddleware: SelfVerifyMiddleware;\n private turnDiffTracker: TurnDiffTracker;\n private contextMiddleware: ContextMiddleware;\n\n private messageRouter: MessageRouter;\n private commandHandler: CommandHandler;\n private streamManager: StreamManager;\n /**\n * Outbound pipeline: typing controller, silence guard, final response publish,\n * extension `message_sending`/`message_sent` hooks, post-turn `webchat_turn_complete`\n * event. Public so the gateway / channels can drive it directly.\n */\n readonly outboundCoordinator: OutboundCoordinator;\n private inboundLoop: InboundLoop;\n /**\n * Direct-turn entry points: `processDirect` (one-shot), `processDirectStreaming`\n * (SSE generator), webchat steering and SSE injection. Public so the gateway,\n * TUI, CLI, and automations do not need to thread every call through `AgentService`.\n */\n readonly turnDispatcher: TurnDispatcher;\n /**\n * `/goal` runtime: continuation scheduling, persistent-goal API factory,\n * stream-outcome state, post-turn verdict. Public so the gateway can wire\n * the webchat continuation scheduler and read stream outcomes directly.\n */\n readonly persistentGoals: PersistentGoalService;\n /**\n * Per-session config writes (model / thinking / reasoning / working directory).\n * Public so REST endpoints and CLI flows can hit it without going through a\n * monolithic patch entrypoint on `AgentService`.\n */\n readonly sessionConfig: SessionConfigService;\n /**\n * Hydration — read persisted per-session config and apply it to the runtime\n * (AgentManager / ModelManager). The mirror image of `sessionConfig`: writes\n * go through `sessionConfig`, reads-into-runtime go through `sessionHydrator`.\n */\n readonly sessionHydrator: SessionHydrator;\n /**\n * Read-only introspection (compaction, /context report, /btw, contextUsage,\n * agentConfig view). Public so REST endpoints and CLI flows can query a\n * session's view without going through delegating methods on `AgentService`.\n */\n readonly sessionInspector: SessionInspector;\n private sessionContextManager: SessionContextManager;\n private sessionLifecycleManager: SessionLifecycleManager;\n private agentOrchestrator: AgentOrchestrator;\n private agentEventHandler: AgentEventHandler;\n private workflowProgressBrokerHandle: BrokerListenerHandle | null = null;\n private feedbackCoordinator: FeedbackCoordinator;\n private agentManager: AgentManager;\n\n /**\n * Unified per-session state container (replaces six ad-hoc Maps). Owns webchat\n * publishers, last assistant text, embedded stream buffer, persistent-goal stream\n * outcomes, concurrent-turn depth, and event-listener unsubscribers; runs a TTL\n * sweep for slots that have no explicit owner.\n */\n private sessionState = new SessionStateBag();\n\n /** Gateway: notify UI after direct `SessionStore.updateMetadata` (no SessionManager emit). */\n private onSessionMetadataUpdated?: (sessionKey: string, patch?: { name?: string }) => void;\n private onSessionTranscriptUpdated?: (sessionKey: string) => void;\n\n private effectiveAppConfig(): Config | undefined {\n const base = this.config.config;\n return base ? applyConfigOverrides(base) : undefined;\n }\n\n constructor(bus: MessageBus, config: AgentServiceConfig) {\n this.config = config;\n this.onSessionMetadataUpdated = config.onSessionMetadataUpdated;\n this.onSessionTranscriptUpdated = config.onSessionTranscriptUpdated;\n this.agentId = `agent-${Date.now()}`;\n this.workspaceDir = config.workspace;\n\n this.sessionTracker = new SessionTracker();\n this.modelManager = new ModelManager({\n defaultModel: config.model,\n config: config.config,\n });\n\n initializeCommands();\n log.debug('Command system initialized');\n\n this.sessionStore = config.sessionStore ?? this.createSessionStore();\n onSessionTranscriptUpdate((update) => {\n void this.sessionStore.syncEmbeddedTranscriptUpdate(update).catch((err) => {\n log.warn(\n { err, sessionKey: update.sessionKey },\n 'Transcript index sync failed',\n );\n });\n const sk = update.sessionKey?.trim();\n if (sk) {\n this.onSessionTranscriptUpdated?.(sk);\n }\n });\n const appCfgForPaths = this.config.config;\n if (!appCfgForPaths) {\n throw new Error('AgentService requires config.config for session paths');\n }\n const defaultAid = resolveDefaultAgentId(appCfgForPaths);\n const defaultAgentHome = resolveAgentHomeDir(appCfgForPaths, defaultAid);\n this.sessionConfigStore = new SessionConfigStore(defaultAgentHome);\n\n this.hookRunner = this.createHookRunner();\n this.hookHandler = new HookHandler({\n hookRunner: this.hookRunner,\n agentId: this.agentId,\n get sessionKey() { return this.currentContext?.sessionKey; },\n });\n\n this.progressManager = this.createProgressManager();\n this.initializeReliabilityModules();\n\n this.lifecycleManager = new LifecycleManager();\n this.initializeLifecycleHandlers();\n\n this.streamManager = new StreamManager();\n this.sessionContextManager = new SessionContextManager();\n this.feedbackCoordinator = new FeedbackCoordinator({\n progressManager: this.progressManager,\n bus,\n });\n\n this.agentManager = new AgentManager({\n workspace: config.workspace,\n model: config.model,\n config: config.config,\n extensionRegistry: config.extensionRegistry,\n hookRunner: this.hookRunner,\n bus,\n getCurrentContext: () => this.sessionContextManager.getContext(),\n getSessionStore: () => this.sessionStore,\n getModelManager: () => this.modelManager,\n thinkingLevel: config.thinkingLevel,\n reasoningLevel: config.reasoningLevel,\n verboseLevel: config.verboseLevel,\n gatewayClarify: config.gatewayClarify,\n getAutomationService: config.getAutomationService,\n getNotesService: config.getNotesService,\n getProjectService: config.getProjectService,\n getWorkItemService: config.getWorkItemService,\n getWorkflowRunService: config.getWorkflowRunService,\n onSkillsUpdated: config.onSkillsUpdated,\n installSkillFromSource: (opts) => this.installSkillFromSource(opts),\n isWorkspaceTrusted: config.isWorkspaceTrusted,\n getSelfVerifyPromptContext: (sessionKey, agentId) => [\n this.selfVerifyMiddleware.getPendingVerificationContext(sessionKey, agentId),\n this.turnDiffTracker.buildFinalGuardContext(sessionKey),\n ].filter(Boolean).join('\\n\\n'),\n });\n\n this.agentEventHandler = new AgentEventHandler({\n progressManager: this.progressManager,\n errorTracker: this.errorTracker,\n requestLimiter: this.requestLimiter,\n lifecycleManager: this.lifecycleManager,\n toolChainTracker: this.toolChainTracker,\n selfVerifyMiddleware: this.selfVerifyMiddleware,\n systemReminder: this.systemReminder,\n toolUsageAnalyzer: this.toolUsageAnalyzer,\n errorPatternMatcher: this.errorPatternMatcher,\n turnDiffTracker: this.turnDiffTracker,\n });\n\n // Wire the workflow progress broker into the session event bus. Channel\n // extensions self-register their capability against the singleton at\n // boot; the broker only forwards updates to whatever has registered, so\n // this is safe even when no channel is configured.\n this.workflowProgressBrokerHandle = getWorkflowProgressBroker().attachTo(\n this.agentEventHandler,\n );\n\n // sessionHydrator is constructed early because AgentOrchestrator + InboundLoop +\n // TurnDispatcher all need it; the SessionConfigService instance below also\n // shares the same constructor parameters.\n this.sessionHydrator = new SessionHydrator({\n sessionConfigStore: this.sessionConfigStore,\n agentManager: this.agentManager,\n modelManager: this.modelManager,\n getConfig: () => this.effectiveAppConfig(),\n });\n\n this.agentOrchestrator = new AgentOrchestrator({\n agentManager: this.agentManager,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n eventHandler: this.agentEventHandler,\n feedbackCoordinator: this.feedbackCoordinator,\n sessionConfigStore: this.sessionConfigStore,\n sessionHydrator: this.sessionHydrator,\n getConfig: () => this.effectiveAppConfig(),\n getThinkingDefault: () => undefined,\n getThinkingDefaultForSession: (sessionKey: string) =>\n this.agentManager.getThinkingDefaultForSession(sessionKey),\n workspaceRoot: this.workspaceDir,\n getWorkspaceRootForSession: (sessionKey: string) =>\n this.agentManager.getResolvedWorkspaceForSession(sessionKey),\n getAgentInternalStorageRootForSession: (sessionKey: string) =>\n resolveAgentHomeDir(this.config.config!, extractProfileAgentId(sessionKey, this.config.config!)),\n enqueueAutoTitle: (sessionKey: string) => this.enqueueMaybeAutoTitleAfterPersist(sessionKey),\n onEmbeddedStreamEvent: (sessionKey, event) => {\n const ctx = this.sessionContextManager.getContext();\n if (!ctx || ctx.sessionKey !== sessionKey) {\n return;\n }\n if (event.type === 'message_update' && event.message.role === 'assistant') {\n const next = assistantMessageText(event.message);\n this.streamManager.update(next);\n }\n },\n onEmbeddedTurnComplete: (sessionKey, text) => {\n if (text) {\n this.sessionState.setLastAssistantText(sessionKey, text);\n }\n this.sessionState.clearEmbeddedStreamText(sessionKey);\n },\n });\n\n this.messageRouter = new MessageRouter();\n this.commandHandler = new CommandHandler({\n config: config.config!,\n bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n getPersistentGoalApisForCommand: (routing) => this.persistentGoals.buildApisForRouting(routing),\n getWorkflowRunService: config.getWorkflowRunService,\n applySessionThinkingLevel: (sessionKey: string, level: ThinkLevel) => {\n this.agentManager.setThinkingLevel(sessionKey, level as ThinkingLevel);\n },\n getCurrentModel: () => this.agentOrchestrator.getCurrentModel(),\n switchModelForSession: (sessionKey: string, modelId: string) =>\n this.switchModelForSession(sessionKey, modelId),\n invalidateAgentSession: (sessionKey: string) => {\n this.agentManager.removeAgent(sessionKey);\n },\n resetSession: (sessionKey: string) => this.resetSession(sessionKey),\n abortSessionTurn: async (sessionKey: string) => {\n await this.streamManager.abort();\n this.agentOrchestrator.abort(sessionKey);\n },\n reloadSkills: () => this.refreshSkillsAfterDiskChange(),\n installSkillFromSource: (opts) => this.installSkillFromSource(opts),\n compactSession: (sessionKey, options) => this.sessionInspector.compact(sessionKey, options),\n btwQuery: (sessionKey, question) => this.sessionInspector.btwQuery(sessionKey, question),\n getSessionContextReport: (sessionKey, mode) => this.sessionInspector.report(sessionKey, mode),\n });\n\n this.sessionLifecycleManager = new SessionLifecycleManager(\n this.sessionStore,\n this.sessionTracker,\n this.lifecycleManager\n );\n\n this.persistentGoals = new PersistentGoalService({\n bus,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n sessionState: this.sessionState,\n getConfig: () => this.effectiveAppConfig(),\n getResolvedWorkspaceForSession: (sk) => this.agentManager.getResolvedWorkspaceForSession(sk),\n onSessionMetadataUpdated: this.onSessionMetadataUpdated,\n onGoalStatusUpdated: config.onGoalStatusUpdated,\n notifyWebchatTranscriptAppend: (sk, text) => this.turnDispatcher.notifyWebchatTranscriptAppend(sk, text),\n });\n\n this.sessionConfig = new SessionConfigService({\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n modelManager: this.modelManager,\n agentManager: this.agentManager,\n getConfig: () => this.effectiveAppConfig(),\n });\n\n this.sessionInspector = new SessionInspector({\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n modelManager: this.modelManager,\n agentManager: this.agentManager,\n sessionHydrator: this.sessionHydrator,\n getConfig: () => this.effectiveAppConfig(),\n getContextWindow: (sessionKey) => this.getContextWindowForSession(sessionKey),\n });\n\n this.outboundCoordinator = new OutboundCoordinator({\n bus,\n hookHandler: this.hookHandler,\n streamManager: this.streamManager,\n getConfig: () => this.effectiveAppConfig(),\n getLastAssistantPlainText: (sk) => this.getLastAssistantPlainText(sk),\n runPersistentGoalPostTurn: (payload) => this.persistentGoals.runPostTurn(payload),\n });\n\n this.turnDispatcher = new TurnDispatcher({\n log,\n agentManager: this.agentManager,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n sessionConfigStore: this.sessionConfigStore,\n sessionState: this.sessionState,\n commandHandler: this.commandHandler,\n getConfig: () => this.effectiveAppConfig(),\n requireConfig: () => {\n const c = this.config.config;\n if (!c) throw new Error('AgentService requires config.config');\n return c;\n },\n resolveSessionEndpoint: (sk) => this.resolveSessionEndpoint(sk),\n initSessionContext: (sk, channel, chatId) => this.initSessionContext(sk, channel, chatId),\n sessionHydrator: this.sessionHydrator,\n prepareInboundAttachments: (sk, att) => this.prepareInboundAttachments(sk, att),\n enqueueMaybeAutoTitleAfterPersist: (sk) => this.enqueueMaybeAutoTitleAfterPersist(sk),\n enqueueProvisionalSessionTitle: (sk, text) => this.enqueueProvisionalSessionTitle(sk, text),\n endDirectRequestContext: () => this.endDirectRequestContext(),\n onSessionTranscriptUpdated: this.onSessionTranscriptUpdated,\n resetSession: (sk) => this.resetSession(sk),\n sourceContextResolver: config.sourceContextResolver,\n });\n\n this.inboundLoop = new InboundLoop({\n log,\n agentId: this.agentId,\n bus,\n hookHandler: this.hookHandler,\n messageRouter: this.messageRouter,\n commandHandler: this.commandHandler,\n sessionContextManager: this.sessionContextManager,\n feedbackCoordinator: this.feedbackCoordinator,\n agentManager: this.agentManager,\n sessionLifecycleManager: this.sessionLifecycleManager,\n agentOrchestrator: this.agentOrchestrator,\n outboundCoordinator: this.outboundCoordinator,\n streamManager: this.streamManager,\n sessionState: this.sessionState,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n setupSessionEventHandling: (sk) => this.setupSessionEventHandling(sk),\n sessionHydrator: this.sessionHydrator,\n getLastAssistantPlainText: (sk) => this.getLastAssistantPlainText(sk),\n checkAndCompact: (sk, msgs) => this.checkAndCompact(sk, msgs),\n enqueueMaybeAutoTitleAfterPersist: (sk) => this.enqueueMaybeAutoTitleAfterPersist(sk),\n getConfig: () => this.effectiveAppConfig(),\n resetSession: (sk) => this.resetSession(sk),\n setStreamHandle: (handle) => this.setStreamHandle(handle),\n });\n\n // Register signal handlers only if not running as an Electron subprocess.\n // In Electron, the parent process manages the lifecycle and signals should not trigger disposal.\n const isElectronSubprocess = !!process.env.ELECTRON_RUN_AS_NODE;\n if (!isElectronSubprocess) {\n process.on('SIGINT', () => this.dispose());\n process.on('SIGTERM', () => this.dispose());\n }\n\n log.info('AgentService initialized');\n }\n\n private createSessionStore(): SessionStore {\n const windowConfig: Partial<WindowConfig> = {\n maxMessages: 100,\n keepRecentMessages: 20,\n preserveSystemMessages: true,\n };\n const compactionConfig: Partial<CompactionConfig> = {\n enabled: true,\n mode: 'abstractive',\n reserveTokens: 8000,\n triggerThreshold: 0.8,\n minMessagesBeforeCompact: 10,\n keepRecentMessages: 10,\n evictionWindow: 0.2,\n retentionWindow: 6,\n };\n const appCfg = this.config.config;\n if (!appCfg) {\n throw new Error('AgentService requires config.config for session store paths');\n }\n return new SessionStore(\n {\n config: appCfg,\n },\n windowConfig,\n compactionConfig,\n );\n }\n\n private createHookRunner(): ExtensionHookRunner | undefined {\n if (!this.config.extensionRegistry) return undefined;\n\n return new ExtensionHookRunner(this.config.extensionRegistry, {\n catchErrors: true,\n logger: {\n info: (msg: string) => log.info({ hook: true }, msg),\n warn: (msg: string) => log.warn({ hook: true }, msg),\n error: (msg: string) => log.error({ hook: true }, msg),\n },\n });\n }\n\n private createProgressManager(): ProgressFeedbackManager {\n return new ProgressFeedbackManager({\n level: 'normal',\n showThinking: true,\n streamToolProgress: true,\n heartbeatEnabled: true,\n heartbeatIntervalMs: 20000,\n longTaskThresholdMs: 30000,\n });\n }\n\n private initializeReliabilityModules(): void {\n this.errorTracker = new ToolErrorTracker({\n maxFailuresPerTool: 3,\n maxTotalFailures: 5,\n resetOnTurnEnd: true,\n });\n\n this.selfVerifyMiddleware = new SelfVerifyMiddleware({\n maxEditsPerFile: 5,\n enablePreCompletionCheck: true,\n minTurnsForVerification: 4,\n resetOnVerification: true,\n });\n this.turnDiffTracker = new TurnDiffTracker();\n\n this.requestLimiter = new RequestLimiter({\n maxRequestsPerTurn: 50,\n warnThreshold: 0.8,\n softLimit: false,\n });\n\n this.systemReminder = new SystemReminder({\n enabled: true,\n appendToToolResults: true,\n maxRemindersPerTurn: 3,\n });\n\n this.toolUsageAnalyzer = new ToolUsageAnalyzer({\n enabled: true,\n lowUsageThreshold: 5,\n veryLowUsageThreshold: 1,\n minCallsForAnalysis: 100,\n reportIntervalMs: 60 * 60 * 1000,\n });\n\n this.toolChainTracker = new ToolChainTracker({\n enabled: true,\n maxChainsPerSession: 10,\n maxNodesPerChain: 100,\n trackParams: true,\n trackResults: true,\n autoPrune: true,\n });\n\n this.errorPatternMatcher = new ErrorPatternMatcher({\n enabled: true,\n defaultMaxRetries: 1,\n logMatches: true,\n });\n\n // Initialize context middleware for automatic request tracking\n this.contextMiddleware = new ContextMiddleware();\n }\n\n private initializeLifecycleHandlers(): void {\n this.lifecycleManager.on('llm_response', new CompactionLifecycleHandler({\n minMessages: 20,\n maxTokens: 8000,\n preserveReasoning: true,\n accumulateUsage: true,\n }));\n\n log.debug(\n { handlers: this.lifecycleManager.getRegisteredHandlers() },\n 'Lifecycle handlers initialized'\n );\n }\n\n setChannelManager(channelManager: ChannelManager): void {\n this.inboundLoop.setChannelManager(channelManager);\n }\n\n /**\n * Apply config after save or hot reload so the default model updates without restarting the gateway.\n */\n applyAgentDefaultsFromConfig(config: Config): void {\n this.config.config = config;\n const ref = getAgentDefaultModelRef(config);\n this.config.model = ref;\n this.modelManager.updateFromConfig(config);\n this.agentManager.updateAgentDefaults(config);\n this.commandHandler.updateAgentConfig(config);\n }\n\n getSkillCatalog(): SkillCatalogEntry[] {\n return this.agentManager.getSkillCatalog();\n }\n\n getSkillCatalogSnapshot(): SkillCatalogSnapshot {\n return this.agentManager.getSkillCatalogSnapshot();\n }\n\n getAgentSkillAvailability(agentId: string): AgentSkillAvailabilityPayload {\n return this.agentManager.getAgentSkillAvailability(agentId);\n }\n\n getCapabilityCatalog(sessionKey?: string): AgentCapabilityCatalogEntry[] {\n return this.agentManager.getCapabilityCatalogForSession(sessionKey);\n }\n\n getSkillMarkdownSource(skillName: string): SkillMarkdownPreviewPayload | null {\n return this.agentManager.getSkillMarkdownSource(skillName);\n }\n\n refreshSkillsAfterDiskChange(): void {\n this.agentManager.refreshSkillsAfterDiskChange();\n }\n\n async installSkillFromSource(opts: SkillInstallToolOptions): Promise<SkillInstallToolResult> {\n const target = normalizeSkillInstallTarget(opts.target);\n const workspace =\n opts.workspace?.trim() ||\n (opts.sessionKey ? this.agentManager.getResolvedWorkspaceForSession(opts.sessionKey) : undefined) ||\n this.workspaceDir;\n const result = await pullSkillFromSource(opts.source, {\n ref: opts.ref,\n subpath: opts.path,\n skillId: opts.skillId,\n force: opts.force ?? false,\n installRoot: target === 'workspace' ? resolveWorkspaceSkillsDir(workspace) : undefined,\n lockPath: target === 'workspace' ? resolveWorkspaceSkillsLockPath(workspace) : undefined,\n strictScan: opts.strictScan ?? false,\n });\n this.refreshSkillsAfterDiskChange();\n return { ...result, target };\n }\n\n refreshSkillsAfterSkillConfigChange(): void {\n this.agentManager.refreshSkillsAfterSkillConfigChange();\n }\n\n getModelForSession(sessionKey: string): string {\n return this.modelManager.getModelForSession(sessionKey);\n }\n\n async switchModelForSession(sessionKey: string, modelId: string): Promise<boolean> {\n const ok = await this.modelManager.switchModelForSession(sessionKey, modelId);\n if (!ok) return false;\n await this.sessionConfigStore.update(sessionKey, { modelOverride: modelId });\n const result = this.agentManager.setModelForSession(sessionKey, modelId);\n if (result) {\n this.sessionTracker.touchSession(sessionKey);\n }\n return true;\n }\n\n /**\n * Clears per-session model override so the next turn uses the configured agent default\n * (e.g. cron isolated job with no explicit model).\n */\n async resetSessionModelToAgentDefault(sessionKey: string): Promise<void> {\n await this.sessionConfig.clearModelOverride(sessionKey);\n }\n\n setStreamHandle(handle: StreamHandle): void {\n this.streamManager.setHandle(handle);\n this.feedbackCoordinator.setStreamHandle(handle);\n }\n\n clearStreamHandle(): void {\n this.streamManager.clearHandle();\n this.feedbackCoordinator.endTask();\n }\n\n /** Last assistant visible plain text for a session (e.g. after a webchat stream). */\n getLastAssistantPlainText(sessionKey: string): string {\n return (\n this.sessionState.getLastAssistantText(sessionKey) ??\n this.agentManager.getLastAssistantContent(sessionKey) ??\n ''\n );\n }\n\n beginInboundTurn(sessionKey: string): void {\n this.sessionState.beginInboundTurn(sessionKey);\n }\n\n endInboundTurn(sessionKey: string): void {\n this.sessionState.endInboundTurn(sessionKey);\n }\n\n getInboundTurnDepth(sessionKey: string): number {\n return this.sessionState.getInboundTurnDepth(sessionKey);\n }\n\n async start(): Promise<void> {\n await this.sessionConfigStore.initialize();\n await this.hookHandler.trigger('gateway_start', { port: 0, host: 'cli' });\n log.debug('Agent service started');\n void this.inboundLoop.start().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage: em }, `Inbound loop failed: ${em}`);\n });\n }\n\n stop(): Promise<void> {\n this.inboundLoop.stop();\n this.agentManager.dispose();\n this.dispose();\n\n this.hookHandler.trigger('gateway_stop', { reason: 'stopped' });\n log.debug('Agent service stopped');\n return Promise.resolve();\n }\n\n async reconcileDreamingNow(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Persist agent messages with the same sanitizer + transcript hygiene as AgentOrchestrator.\n * Uses persistence hygiene so `thinking` blocks remain on disk for the web UI (LLM load path still drops them).\n */\n private notifySessionTitleUpdated(sessionKey: string, name: string): void {\n this.onSessionMetadataUpdated?.(sessionKey, { name });\n }\n\n /** Fire-and-forget provisional title from first user text (webchat sidebar). */\n enqueueProvisionalSessionTitle(sessionKey: string, userText: string): void {\n void (async () => {\n try {\n await maybeSetProvisionalSessionTitle(\n this.sessionStore,\n sessionKey,\n userText,\n (sk, name) => this.notifySessionTitleUpdated(sk, name),\n );\n } catch (err) {\n log.warn({ err, sessionKey }, 'Provisional session title failed');\n }\n })();\n }\n\n /**\n * Fire-and-forget LLM refine after turn persist; skips user-locked and finalized LLM titles.\n */\n private enqueueMaybeAutoTitleAfterPersist(sessionKey: string): void {\n void (async () => {\n try {\n let modelRef =\n getAgentDefaultModelRef(this.config.config ?? ({} as Config)) ?? this.config.model;\n if (!modelRef?.trim()) {\n try {\n modelRef = this.modelManager.getModelForSession(sessionKey);\n } catch {\n modelRef = undefined;\n }\n }\n await maybeRefineSessionTitleWithLlm(\n this.sessionStore,\n sessionKey,\n modelRef?.trim() || undefined,\n (sk, name) => this.notifySessionTitleUpdated(sk, name),\n );\n } catch (err) {\n log.warn({ err, sessionKey }, 'Auto session title failed');\n }\n })();\n }\n\n private async resolveSessionEndpoint(sessionKey: string): Promise<{ channel: string; chatId: string }> {\n const metadata = await this.sessionStore.getMetadata(sessionKey).catch(() => null);\n const channel = metadata?.routing?.source?.trim() || metadata?.sourceChannel?.trim() || 'cli';\n const chatId = metadata?.routing?.peerId?.trim() || metadata?.sourceChatId?.trim() || 'main';\n return { channel, chatId };\n }\n\n private initSessionContext(\n sessionKey: string,\n channel: string,\n chatId: string,\n senderId = '',\n ): SessionContext {\n const context: SessionContext = {\n sessionKey,\n channel,\n chatId,\n senderId,\n isGroup: false,\n };\n\n this.contextMiddleware.onRequest({\n sessionKey,\n userId: context.senderId,\n channel,\n chatId,\n });\n\n // Direct turn entry points (one-shot + streaming generator) cannot wrap their\n // body in `sessionContextManager.runWith(ctx, fn)` cleanly — the streaming\n // path is an async generator, and both flows already use a try/finally for\n // side-effect cleanup. We use `enter` so the context is visible via ALS for\n // every async resource launched after this call returns. The context is\n // overwritten or cleared by the next direct turn (each direct turn calls\n // `initSessionContext` first), so there is no cross-session leak in practice.\n this.sessionContextManager.enter(context);\n this.feedbackCoordinator.setContext(context);\n this.agentManager.getOrCreateAgent(sessionKey);\n this.setupSessionEventHandling(sessionKey);\n\n return context;\n }\n\n /**\n * Persist inbound file attachments to the global media store.\n */\n async prepareInboundAttachments(\n _sessionKey: string,\n attachments?: InboundAttachmentInput[],\n ): Promise<MediaRef[] | undefined> {\n return persistInboundAttachments(attachments, {\n resolveUri: async (uri) => {\n const target = parseNoteAttachmentTarget(uri);\n if (!target) return null;\n const notesService = (this as unknown as {\n notesServiceInstance?: {\n getAttachmentPath(noteId: string, attachmentId: string): Promise<{\n filePath: string;\n mimeType?: string;\n fileName?: string;\n } | null>;\n };\n }).notesServiceInstance;\n if (!notesService) return null;\n const located = await notesService.getAttachmentPath(target.noteId, target.attachmentId);\n if (!located) return null;\n const buffer = await readFile(located.filePath);\n return {\n buffer,\n path: located.filePath,\n mimeType: located.mimeType,\n name: located.fileName,\n size: buffer.byteLength,\n };\n },\n });\n }\n\n private endDirectRequestContext(): void {\n // `sessionContextManager` is ALS-backed: the context for the current async\n // chain drops automatically when the chain unwinds. The feedback\n // coordinator + context middleware still use singleton state, so clear\n // them explicitly here.\n this.feedbackCoordinator.clearContext();\n this.contextMiddleware.onResponse();\n }\n\n /**\n * Reset a session's transcript and drop the in-memory agent so the next turn\n * reloads from disk. Combines two collaborators (sessionStore + agentManager)\n * so it stays on `AgentService`; pure sessionStore reads should use\n * `agentService.sessionStore.*` directly.\n */\n async clearSessionMessages(key: string): Promise<void> {\n await this.sessionStore.saveMessages(key, []);\n this.agentManager.removeAgent(key);\n }\n\n /**\n * Reset session transcript (archive + new session id) and evict in-memory agent state.\n * Preserves the session key and persisted per-session overrides.\n */\n async resetSession(\n key: string,\n ): Promise<{ sessionId: string; previousSessionId: string } | null> {\n const { abortEmbeddedRun } = await import('./embedded/runs.js');\n const { retireSessionMcpRuntimeForSessionKey } = await import('./mcp/bundle-mcp-tools.js');\n await abortEmbeddedRun(key);\n const outcome = await this.sessionStore.reset(key);\n if (!outcome) {\n return null;\n }\n this.agentManager.removeAgent(key);\n await retireSessionMcpRuntimeForSessionKey({ sessionKey: key, reason: 'session-reset' });\n return outcome;\n }\n\n /**\n * Drop in-memory agent so the next turn reloads transcript from disk (e.g. after checkpoint restore).\n */\n evictSessionAgent(sessionKey: string): void {\n this.agentManager.removeAgent(sessionKey);\n }\n\n /**\n * Load session working directory override into AgentManager, ensure directory exists.\n * Call before AgentManager.getOrCreateAgent for this session.\n */\n /** Workspace root for UI file tree / editor (same as agent tools after hydration). */\n async getEffectiveWorkspacePathForSession(sessionKey: string): Promise<string> {\n await this.sessionHydrator.workspace(sessionKey);\n const cfg = this.config.config!;\n const sc = await this.sessionConfigStore.get(sessionKey);\n return effectiveWorkspacePathForSession(cfg, sessionKey, sc, getProjectForSession(sessionKey));\n }\n\n /**\n * Best-effort timezone resolution for webchat envelope timestamps.\n * Reads the global user profile and extracts a `Timezone:` line.\n */\n resolveUserTimezoneForSession(sessionKey: string): string | undefined {\n void sessionKey;\n try {\n const cfg = this.effectiveAppConfig();\n if (!cfg) return undefined;\n const userPath = resolveUserProfilePath();\n if (!existsSync(userPath)) return undefined;\n const raw = readFileSync(userPath, 'utf-8');\n const match = raw.match(/Timezone:\\s*(.+)/i);\n const tz = match?.[1]?.trim();\n return tz || undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Setup event handling for a specific session\n */\n private setupSessionEventHandling(sessionKey: string): void {\n if (this.sessionState.hasSessionEventUnsubscriber(sessionKey)) {\n return;\n }\n\n const unsubscribe = this.agentManager.subscribeToSession(sessionKey, (event) => {\n this.handleSessionEvent(sessionKey, event);\n });\n\n if (unsubscribe) {\n this.sessionState.setSessionEventUnsubscriber(sessionKey, unsubscribe);\n }\n }\n\n /**\n * Handle events from a specific session's agent\n */\n private handleSessionEvent(sessionKey: string, event: AgentEvent): void {\n const currentContext = this.sessionContextManager.getContext();\n if (!currentContext) {\n // Inbound `finally` clears context before trailing agent `message_update` events finish — ignore (not a bug).\n return;\n }\n\n if (currentContext.sessionKey !== sessionKey) {\n // Event from a different session — still process with current context where applicable\n this.agentEventHandler.handle(event, currentContext);\n return;\n }\n\n // Handle streaming updates for the current session\n if (event.type === 'message_update') {\n const msgEvent = event as Extract<AgentEvent, { type: 'message_update' }>;\n if (msgEvent.message?.role === 'assistant') {\n const content = msgEvent.message.content;\n const text = Array.isArray(content)\n ? extractTextContent(content as Array<{ type: string; text?: string }>)\n : String(content);\n\n this.streamManager.update(text);\n }\n }\n\n this.agentEventHandler.handle(event, currentContext);\n }\n\n private async checkAndCompact(sessionKey: string, messages: AgentMessage[]): Promise<void> {\n const contextWindow = this.getContextWindowForSession(sessionKey);\n const prep = this.sessionStore.prepareCompaction(sessionKey, messages, contextWindow);\n if (!prep.needsCompaction) return;\n\n log.info({ sessionKey, reason: prep.stats?.reason, usagePercent: prep.stats?.usagePercent }, 'Session needs compaction');\n\n const result = await this.sessionStore.compact(sessionKey, messages, contextWindow, undefined, false);\n await this.hookHandler.trigger('after_compaction', {\n messageCount: messages.length,\n tokenCount: result.tokensBefore,\n compactedCount: messages.length - result.firstKeptIndex,\n });\n log.info({ sessionKey, tokensBefore: result.tokensBefore, tokensAfter: result.tokensAfter }, 'Session compacted');\n }\n\n private getContextWindowForSession(sessionKey: string): number {\n try {\n return this.modelManager.getResolvedModelForSession(sessionKey).contextWindow ?? 128000;\n } catch (err) {\n log.warn({ err, sessionKey }, 'Failed to resolve session context window; using default');\n return 128000;\n }\n }\n\n private dispose(): void {\n this.sessionTracker.dispose();\n this.sessionState.disposeAll();\n this.agentManager.dispose();\n this.workflowProgressBrokerHandle?.dispose();\n this.workflowProgressBrokerHandle = null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAE2E;aAkBzB;kBAmExB;YAIkC;AAW5D,MAAM,MAAM,aAAa,eAAe;AAExC,SAAS,qBAAqB,SAA+B;CAC3D,MAAM,UAAW,QAAkC;AACnD,KAAI,OAAO,YAAY,SAAU,QAAO;AACxC,KAAI,CAAC,MAAM,QAAQ,QAAQ,CAAE,QAAO;AACpC,QAAO,QACJ,KAAK,UAAU;AACd,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;EAChD,MAAM,MAAM;AACZ,SAAO,IAAI,SAAS,UAAU,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;GACxE,CACD,KAAK,GAAG;;AAGb,IAAa,eAAb,MAA0B;;;;;;CAMxB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;;;;;;CAMA;CACA;;;;;;CAMA;;;;;;CAMA;;;;;;CAMA;;;;;;CAMA;;;;;;CAMA;CACA;CACA;CACA;CACA;CACA,+BAAoE;CACpE;CACA;;;;;;;CAQA,eAAuB,IAAI,iBAAiB;;CAG5C;CACA;CAEA,qBAAiD;EAC/C,MAAM,OAAO,KAAK,OAAO;AACzB,SAAO,OAAO,qBAAqB,KAAK,GAAG,KAAA;;CAG7C,YAAY,KAAiB,QAA4B;AACvD,OAAK,SAAS;AACd,OAAK,2BAA2B,OAAO;AACvC,OAAK,6BAA6B,OAAO;AACzC,OAAK,UAAU,SAAS,KAAK,KAAK;AAClC,OAAK,eAAe,OAAO;AAE3B,OAAK,iBAAiB,IAAI,gBAAgB;AAC1C,OAAK,eAAe,IAAI,aAAa;GACnC,cAAc,OAAO;GACrB,QAAQ,OAAO;GAChB,CAAC;AAEF,sBAAoB;AACpB,MAAI,MAAM,6BAA6B;AAEvC,OAAK,eAAe,OAAO,gBAAgB,KAAK,oBAAoB;AACpE,6BAA2B,WAAW;AAC/B,QAAK,aAAa,6BAA6B,OAAO,CAAC,OAAO,QAAQ;AACzE,QAAI,KACF;KAAE;KAAK,YAAY,OAAO;KAAY,EACtC,+BACD;KACD;GACF,MAAM,KAAK,OAAO,YAAY,MAAM;AACpC,OAAI,GACF,MAAK,6BAA6B,GAAG;IAEvC;EACF,MAAM,iBAAiB,KAAK,OAAO;AACnC,MAAI,CAAC,eACH,OAAM,IAAI,MAAM,wDAAwD;EAG1E,MAAM,mBAAmB,oBAAoB,gBAD1B,sBAAsB,eAC8B,CAAC;AACxE,OAAK,qBAAqB,IAAI,mBAAmB,iBAAiB;AAElE,OAAK,aAAa,KAAK,kBAAkB;AACzC,OAAK,cAAc,IAAI,YAAY;GACjC,YAAY,KAAK;GACjB,SAAS,KAAK;GACd,IAAI,aAAa;AAAE,WAAO,KAAK,gBAAgB;;GAChD,CAAC;AAEF,OAAK,kBAAkB,KAAK,uBAAuB;AACnD,OAAK,8BAA8B;AAEnC,OAAK,mBAAmB,IAAI,kBAAkB;AAC9C,OAAK,6BAA6B;AAElC,OAAK,gBAAgB,IAAI,eAAe;AACxC,OAAK,wBAAwB,IAAI,uBAAuB;AACxD,OAAK,sBAAsB,IAAI,oBAAoB;GACjD,iBAAiB,KAAK;GACtB;GACD,CAAC;AAEF,OAAK,eAAe,IAAI,aAAa;GACnC,WAAW,OAAO;GAClB,OAAO,OAAO;GACd,QAAQ,OAAO;GACf,mBAAmB,OAAO;GAC1B,YAAY,KAAK;GACjB;GACA,yBAAyB,KAAK,sBAAsB,YAAY;GAChE,uBAAuB,KAAK;GAC5B,uBAAuB,KAAK;GAC5B,eAAe,OAAO;GACtB,gBAAgB,OAAO;GACvB,cAAc,OAAO;GACrB,gBAAgB,OAAO;GACvB,sBAAsB,OAAO;GAC7B,iBAAiB,OAAO;GACxB,mBAAmB,OAAO;GAC1B,oBAAoB,OAAO;GAC3B,uBAAuB,OAAO;GAC9B,iBAAiB,OAAO;GACxB,yBAAyB,SAAS,KAAK,uBAAuB,KAAK;GACnE,oBAAoB,OAAO;GAC3B,6BAA6B,YAAY,YAAY,CACnD,KAAK,qBAAqB,8BAA8B,YAAY,QAAQ,EAC5E,KAAK,gBAAgB,uBAAuB,WAAW,CACxD,CAAC,OAAO,QAAQ,CAAC,KAAK,OAAO;GAC/B,CAAC;AAEF,OAAK,oBAAoB,IAAI,kBAAkB;GAC7C,iBAAiB,KAAK;GACtB,cAAc,KAAK;GACnB,gBAAgB,KAAK;GACrB,kBAAkB,KAAK;GACvB,kBAAkB,KAAK;GACvB,sBAAsB,KAAK;GAC3B,gBAAgB,KAAK;GACrB,mBAAmB,KAAK;GACxB,qBAAqB,KAAK;GAC1B,iBAAiB,KAAK;GACvB,CAAC;AAMF,OAAK,+BAA+B,2BAA2B,CAAC,SAC9D,KAAK,kBACN;AAKD,OAAK,kBAAkB,IAAI,gBAAgB;GACzC,oBAAoB,KAAK;GACzB,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,iBAAiB,KAAK,oBAAoB;GAC3C,CAAC;AAEF,OAAK,oBAAoB,IAAI,kBAAkB;GAC7C,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,qBAAqB,KAAK;GAC1B,oBAAoB,KAAK;GACzB,iBAAiB,KAAK;GACtB,iBAAiB,KAAK,oBAAoB;GAC1C,0BAA0B,KAAA;GAC1B,+BAA+B,eAC7B,KAAK,aAAa,6BAA6B,WAAW;GAC5D,eAAe,KAAK;GACpB,6BAA6B,eAC3B,KAAK,aAAa,+BAA+B,WAAW;GAC9D,wCAAwC,eACtC,oBAAoB,KAAK,OAAO,QAAS,sBAAsB,YAAY,KAAK,OAAO,OAAQ,CAAC;GAClG,mBAAmB,eAAuB,KAAK,kCAAkC,WAAW;GAC5F,wBAAwB,YAAY,UAAU;IAC5C,MAAM,MAAM,KAAK,sBAAsB,YAAY;AACnD,QAAI,CAAC,OAAO,IAAI,eAAe,WAC7B;AAEF,QAAI,MAAM,SAAS,oBAAoB,MAAM,QAAQ,SAAS,aAAa;KACzE,MAAM,OAAO,qBAAqB,MAAM,QAAQ;AAChD,UAAK,cAAc,OAAO,KAAK;;;GAGnC,yBAAyB,YAAY,SAAS;AAC5C,QAAI,KACF,MAAK,aAAa,qBAAqB,YAAY,KAAK;AAE1D,SAAK,aAAa,wBAAwB,WAAW;;GAExD,CAAC;AAEF,OAAK,gBAAgB,IAAI,eAAe;AACxC,OAAK,iBAAiB,IAAI,eAAe;GACvC,QAAQ,OAAO;GACf;GACA,cAAc,KAAK;GACnB,oBAAoB,KAAK;GACzB,kCAAkC,YAAY,KAAK,gBAAgB,oBAAoB,QAAQ;GAC/F,uBAAuB,OAAO;GAC9B,4BAA4B,YAAoB,UAAsB;AACpE,SAAK,aAAa,iBAAiB,YAAY,MAAuB;;GAExE,uBAAuB,KAAK,kBAAkB,iBAAiB;GAC/D,wBAAwB,YAAoB,YAC1C,KAAK,sBAAsB,YAAY,QAAQ;GACjD,yBAAyB,eAAuB;AAC9C,SAAK,aAAa,YAAY,WAAW;;GAE3C,eAAe,eAAuB,KAAK,aAAa,WAAW;GACnE,kBAAkB,OAAO,eAAuB;AAC9C,UAAM,KAAK,cAAc,OAAO;AAChC,SAAK,kBAAkB,MAAM,WAAW;;GAE1C,oBAAoB,KAAK,8BAA8B;GACvD,yBAAyB,SAAS,KAAK,uBAAuB,KAAK;GACnE,iBAAiB,YAAY,YAAY,KAAK,iBAAiB,QAAQ,YAAY,QAAQ;GAC3F,WAAW,YAAY,aAAa,KAAK,iBAAiB,SAAS,YAAY,SAAS;GACxF,0BAA0B,YAAY,SAAS,KAAK,iBAAiB,OAAO,YAAY,KAAK;GAC9F,CAAC;AAEF,OAAK,0BAA0B,IAAI,wBACjC,KAAK,cACL,KAAK,gBACL,KAAK,iBACN;AAED,OAAK,kBAAkB,IAAI,sBAAsB;GAC/C;GACA,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,iBAAiB,KAAK,oBAAoB;GAC1C,iCAAiC,OAAO,KAAK,aAAa,+BAA+B,GAAG;GAC5F,0BAA0B,KAAK;GAC/B,qBAAqB,OAAO;GAC5B,gCAAgC,IAAI,SAAS,KAAK,eAAe,8BAA8B,IAAI,KAAK;GACzG,CAAC;AAEF,OAAK,gBAAgB,IAAI,qBAAqB;GAC5C,cAAc,KAAK;GACnB,oBAAoB,KAAK;GACzB,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,iBAAiB,KAAK,oBAAoB;GAC3C,CAAC;AAEF,OAAK,mBAAmB,IAAI,iBAAiB;GAC3C,cAAc,KAAK;GACnB,oBAAoB,KAAK;GACzB,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,iBAAiB,KAAK;GACtB,iBAAiB,KAAK,oBAAoB;GAC1C,mBAAmB,eAAe,KAAK,2BAA2B,WAAW;GAC9E,CAAC;AAEF,OAAK,sBAAsB,IAAI,oBAAoB;GACjD;GACA,aAAa,KAAK;GAClB,eAAe,KAAK;GACpB,iBAAiB,KAAK,oBAAoB;GAC1C,4BAA4B,OAAO,KAAK,0BAA0B,GAAG;GACrE,4BAA4B,YAAY,KAAK,gBAAgB,YAAY,QAAQ;GAClF,CAAC;AAEF,OAAK,iBAAiB,IAAI,eAAe;GACvC;GACA,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,oBAAoB,KAAK;GACzB,cAAc,KAAK;GACnB,gBAAgB,KAAK;GACrB,iBAAiB,KAAK,oBAAoB;GAC1C,qBAAqB;IACnB,MAAM,IAAI,KAAK,OAAO;AACtB,QAAI,CAAC,EAAG,OAAM,IAAI,MAAM,sCAAsC;AAC9D,WAAO;;GAET,yBAAyB,OAAO,KAAK,uBAAuB,GAAG;GAC/D,qBAAqB,IAAI,SAAS,WAAW,KAAK,mBAAmB,IAAI,SAAS,OAAO;GACzF,iBAAiB,KAAK;GACtB,4BAA4B,IAAI,QAAQ,KAAK,0BAA0B,IAAI,IAAI;GAC/E,oCAAoC,OAAO,KAAK,kCAAkC,GAAG;GACrF,iCAAiC,IAAI,SAAS,KAAK,+BAA+B,IAAI,KAAK;GAC3F,+BAA+B,KAAK,yBAAyB;GAC7D,4BAA4B,KAAK;GACjC,eAAe,OAAO,KAAK,aAAa,GAAG;GAC3C,uBAAuB,OAAO;GAC/B,CAAC;AAEF,OAAK,cAAc,IAAI,YAAY;GACjC;GACA,SAAS,KAAK;GACd;GACA,aAAa,KAAK;GAClB,eAAe,KAAK;GACpB,gBAAgB,KAAK;GACrB,uBAAuB,KAAK;GAC5B,qBAAqB,KAAK;GAC1B,cAAc,KAAK;GACnB,yBAAyB,KAAK;GAC9B,mBAAmB,KAAK;GACxB,qBAAqB,KAAK;GAC1B,eAAe,KAAK;GACpB,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,4BAA4B,OAAO,KAAK,0BAA0B,GAAG;GACrE,iBAAiB,KAAK;GACtB,4BAA4B,OAAO,KAAK,0BAA0B,GAAG;GACrE,kBAAkB,IAAI,SAAS,KAAK,gBAAgB,IAAI,KAAK;GAC7D,oCAAoC,OAAO,KAAK,kCAAkC,GAAG;GACrF,iBAAiB,KAAK,oBAAoB;GAC1C,eAAe,OAAO,KAAK,aAAa,GAAG;GAC3C,kBAAkB,WAAW,KAAK,gBAAgB,OAAO;GAC1D,CAAC;AAKF,MAAI,CAAC,CADyB,CAAC,QAAQ,IAAI,sBAChB;AACzB,WAAQ,GAAG,gBAAgB,KAAK,SAAS,CAAC;AAC1C,WAAQ,GAAG,iBAAiB,KAAK,SAAS,CAAC;;AAG7C,MAAI,KAAK,2BAA2B;;CAGtC,qBAA2C;EACzC,MAAM,eAAsC;GAC1C,aAAa;GACb,oBAAoB;GACpB,wBAAwB;GACzB;EACD,MAAM,mBAA8C;GAClD,SAAS;GACT,MAAM;GACN,eAAe;GACf,kBAAkB;GAClB,0BAA0B;GAC1B,oBAAoB;GACpB,gBAAgB;GAChB,iBAAiB;GAClB;EACD,MAAM,SAAS,KAAK,OAAO;AAC3B,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,8DAA8D;AAEhF,SAAO,IAAI,aACT,EACE,QAAQ,QACT,EACD,cACA,iBACD;;CAGH,mBAA4D;AAC1D,MAAI,CAAC,KAAK,OAAO,kBAAmB,QAAO,KAAA;AAE3C,SAAO,IAAI,oBAAoB,KAAK,OAAO,mBAAmB;GAC5D,aAAa;GACb,QAAQ;IACN,OAAO,QAAgB,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE,IAAI;IACpD,OAAO,QAAgB,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE,IAAI;IACpD,QAAQ,QAAgB,IAAI,MAAM,EAAE,MAAM,MAAM,EAAE,IAAI;IACvD;GACF,CAAC;;CAGJ,wBAAyD;AACvD,SAAO,IAAI,wBAAwB;GACjC,OAAO;GACP,cAAc;GACd,oBAAoB;GACpB,kBAAkB;GAClB,qBAAqB;GACrB,qBAAqB;GACtB,CAAC;;CAGJ,+BAA6C;AAC3C,OAAK,eAAe,IAAI,iBAAiB;GACvC,oBAAoB;GACpB,kBAAkB;GAClB,gBAAgB;GACjB,CAAC;AAEF,OAAK,uBAAuB,IAAI,qBAAqB;GACnD,iBAAiB;GACjB,0BAA0B;GAC1B,yBAAyB;GACzB,qBAAqB;GACtB,CAAC;AACF,OAAK,kBAAkB,IAAI,iBAAiB;AAE5C,OAAK,iBAAiB,IAAI,eAAe;GACvC,oBAAoB;GACpB,eAAe;GACf,WAAW;GACZ,CAAC;AAEF,OAAK,iBAAiB,IAAI,eAAe;GACvC,SAAS;GACT,qBAAqB;GACrB,qBAAqB;GACtB,CAAC;AAEF,OAAK,oBAAoB,IAAI,kBAAkB;GAC7C,SAAS;GACT,mBAAmB;GACnB,uBAAuB;GACvB,qBAAqB;GACrB,kBAAkB,OAAU;GAC7B,CAAC;AAEF,OAAK,mBAAmB,IAAI,iBAAiB;GAC3C,SAAS;GACT,qBAAqB;GACrB,kBAAkB;GAClB,aAAa;GACb,cAAc;GACd,WAAW;GACZ,CAAC;AAEF,OAAK,sBAAsB,IAAI,oBAAoB;GACjD,SAAS;GACT,mBAAmB;GACnB,YAAY;GACb,CAAC;AAGF,OAAK,oBAAoB,IAAI,mBAAmB;;CAGlD,8BAA4C;AAC1C,OAAK,iBAAiB,GAAG,gBAAgB,IAAI,2BAA2B;GACtE,aAAa;GACb,WAAW;GACX,mBAAmB;GACnB,iBAAiB;GAClB,CAAC,CAAC;AAEH,MAAI,MACF,EAAE,UAAU,KAAK,iBAAiB,uBAAuB,EAAE,EAC3D,iCACD;;CAGH,kBAAkB,gBAAsC;AACtD,OAAK,YAAY,kBAAkB,eAAe;;;;;CAMpD,6BAA6B,QAAsB;AACjD,OAAK,OAAO,SAAS;EACrB,MAAM,MAAM,wBAAwB,OAAO;AAC3C,OAAK,OAAO,QAAQ;AACpB,OAAK,aAAa,iBAAiB,OAAO;AAC1C,OAAK,aAAa,oBAAoB,OAAO;AAC7C,OAAK,eAAe,kBAAkB,OAAO;;CAG/C,kBAAuC;AACrC,SAAO,KAAK,aAAa,iBAAiB;;CAG5C,0BAAgD;AAC9C,SAAO,KAAK,aAAa,yBAAyB;;CAGpD,0BAA0B,SAAgD;AACxE,SAAO,KAAK,aAAa,0BAA0B,QAAQ;;CAG7D,qBAAqB,YAAoD;AACvE,SAAO,KAAK,aAAa,+BAA+B,WAAW;;CAGrE,uBAAuB,WAAuD;AAC5E,SAAO,KAAK,aAAa,uBAAuB,UAAU;;CAG5D,+BAAqC;AACnC,OAAK,aAAa,8BAA8B;;CAGlD,MAAM,uBAAuB,MAAgE;EAC3F,MAAM,SAAS,4BAA4B,KAAK,OAAO;EACvD,MAAM,YACJ,KAAK,WAAW,MAAM,KACrB,KAAK,aAAa,KAAK,aAAa,+BAA+B,KAAK,WAAW,GAAG,KAAA,MACvF,KAAK;EACP,MAAM,SAAS,MAAM,oBAAoB,KAAK,QAAQ;GACpD,KAAK,KAAK;GACV,SAAS,KAAK;GACd,SAAS,KAAK;GACd,OAAO,KAAK,SAAS;GACrB,aAAa,WAAW,cAAc,0BAA0B,UAAU,GAAG,KAAA;GAC7E,UAAU,WAAW,cAAc,+BAA+B,UAAU,GAAG,KAAA;GAC/E,YAAY,KAAK,cAAc;GAChC,CAAC;AACF,OAAK,8BAA8B;AACnC,SAAO;GAAE,GAAG;GAAQ;GAAQ;;CAG9B,sCAA4C;AAC1C,OAAK,aAAa,qCAAqC;;CAGzD,mBAAmB,YAA4B;AAC7C,SAAO,KAAK,aAAa,mBAAmB,WAAW;;CAGzD,MAAM,sBAAsB,YAAoB,SAAmC;AAEjF,MAAI,CAAC,MADY,KAAK,aAAa,sBAAsB,YAAY,QAAQ,CACpE,QAAO;AAChB,QAAM,KAAK,mBAAmB,OAAO,YAAY,EAAE,eAAe,SAAS,CAAC;AAE5E,MADe,KAAK,aAAa,mBAAmB,YAAY,QACtD,CACR,MAAK,eAAe,aAAa,WAAW;AAE9C,SAAO;;;;;;CAOT,MAAM,gCAAgC,YAAmC;AACvE,QAAM,KAAK,cAAc,mBAAmB,WAAW;;CAGzD,gBAAgB,QAA4B;AAC1C,OAAK,cAAc,UAAU,OAAO;AACpC,OAAK,oBAAoB,gBAAgB,OAAO;;CAGlD,oBAA0B;AACxB,OAAK,cAAc,aAAa;AAChC,OAAK,oBAAoB,SAAS;;;CAIpC,0BAA0B,YAA4B;AACpD,SACE,KAAK,aAAa,qBAAqB,WAAW,IAClD,KAAK,aAAa,wBAAwB,WAAW,IACrD;;CAIJ,iBAAiB,YAA0B;AACzC,OAAK,aAAa,iBAAiB,WAAW;;CAGhD,eAAe,YAA0B;AACvC,OAAK,aAAa,eAAe,WAAW;;CAG9C,oBAAoB,YAA4B;AAC9C,SAAO,KAAK,aAAa,oBAAoB,WAAW;;CAG1D,MAAM,QAAuB;AAC3B,QAAM,KAAK,mBAAmB,YAAY;AAC1C,QAAM,KAAK,YAAY,QAAQ,iBAAiB;GAAE,MAAM;GAAG,MAAM;GAAO,CAAC;AACzE,MAAI,MAAM,wBAAwB;AAC7B,OAAK,YAAY,OAAO,CAAC,OAAO,QAAQ;GAC3C,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC3D,OAAI,MAAM;IAAE;IAAK,cAAc;IAAI,EAAE,wBAAwB,KAAK;IAClE;;CAGJ,OAAsB;AACpB,OAAK,YAAY,MAAM;AACvB,OAAK,aAAa,SAAS;AAC3B,OAAK,SAAS;AAEd,OAAK,YAAY,QAAQ,gBAAgB,EAAE,QAAQ,WAAW,CAAC;AAC/D,MAAI,MAAM,wBAAwB;AAClC,SAAO,QAAQ,SAAS;;CAG1B,MAAM,uBAAsC;AAC1C,SAAO,QAAQ,SAAS;;;;;;CAO1B,0BAAkC,YAAoB,MAAoB;AACxE,OAAK,2BAA2B,YAAY,EAAE,MAAM,CAAC;;;CAIvD,+BAA+B,YAAoB,UAAwB;AACzE,GAAM,YAAY;AAChB,OAAI;AACF,UAAM,gCACJ,KAAK,cACL,YACA,WACC,IAAI,SAAS,KAAK,0BAA0B,IAAI,KAAK,CACvD;YACM,KAAK;AACZ,QAAI,KAAK;KAAE;KAAK;KAAY,EAAE,mCAAmC;;MAEjE;;;;;CAMN,kCAA0C,YAA0B;AAClE,GAAM,YAAY;AAChB,OAAI;IACF,IAAI,WACF,wBAAwB,KAAK,OAAO,UAAW,EAAE,CAAY,IAAI,KAAK,OAAO;AAC/E,QAAI,CAAC,UAAU,MAAM,CACnB,KAAI;AACF,gBAAW,KAAK,aAAa,mBAAmB,WAAW;YACrD;AACN,gBAAW,KAAA;;AAGf,UAAM,+BACJ,KAAK,cACL,YACA,UAAU,MAAM,IAAI,KAAA,IACnB,IAAI,SAAS,KAAK,0BAA0B,IAAI,KAAK,CACvD;YACM,KAAK;AACZ,QAAI,KAAK;KAAE;KAAK;KAAY,EAAE,4BAA4B;;MAE1D;;CAGN,MAAc,uBAAuB,YAAkE;EACrG,MAAM,WAAW,MAAM,KAAK,aAAa,YAAY,WAAW,CAAC,YAAY,KAAK;AAGlF,SAAO;GAAE,SAFO,UAAU,SAAS,QAAQ,MAAM,IAAI,UAAU,eAAe,MAAM,IAAI;GAEtE,QADH,UAAU,SAAS,QAAQ,MAAM,IAAI,UAAU,cAAc,MAAM,IAAI;GAC5D;;CAG5B,mBACE,YACA,SACA,QACA,WAAW,IACK;EAChB,MAAM,UAA0B;GAC9B;GACA;GACA;GACA;GACA,SAAS;GACV;AAED,OAAK,kBAAkB,UAAU;GAC/B;GACA,QAAQ,QAAQ;GAChB;GACA;GACD,CAAC;AASF,OAAK,sBAAsB,MAAM,QAAQ;AACzC,OAAK,oBAAoB,WAAW,QAAQ;AAC5C,OAAK,aAAa,iBAAiB,WAAW;AAC9C,OAAK,0BAA0B,WAAW;AAE1C,SAAO;;;;;CAMT,MAAM,0BACJ,aACA,aACiC;AACjC,SAAO,0BAA0B,aAAa,EAC5C,YAAY,OAAO,QAAQ;GACzB,MAAM,SAAS,0BAA0B,IAAI;AAC7C,OAAI,CAAC,OAAQ,QAAO;GACpB,MAAM,eAAgB,KAQnB;AACH,OAAI,CAAC,aAAc,QAAO;GAC1B,MAAM,UAAU,MAAM,aAAa,kBAAkB,OAAO,QAAQ,OAAO,aAAa;AACxF,OAAI,CAAC,QAAS,QAAO;GACrB,MAAM,SAAS,MAAM,SAAS,QAAQ,SAAS;AAC/C,UAAO;IACL;IACA,MAAM,QAAQ;IACd,UAAU,QAAQ;IAClB,MAAM,QAAQ;IACd,MAAM,OAAO;IACd;KAEJ,CAAC;;CAGJ,0BAAwC;AAKtC,OAAK,oBAAoB,cAAc;AACvC,OAAK,kBAAkB,YAAY;;;;;;;;CASrC,MAAM,qBAAqB,KAA4B;AACrD,QAAM,KAAK,aAAa,aAAa,KAAK,EAAE,CAAC;AAC7C,OAAK,aAAa,YAAY,IAAI;;;;;;CAOpC,MAAM,aACJ,KACkE;EAClE,MAAM,EAAE,qBAAqB,MAAM,OAAO;EAC1C,MAAM,EAAE,yCAAyC,MAAM,OAAO;AAC9D,QAAM,iBAAiB,IAAI;EAC3B,MAAM,UAAU,MAAM,KAAK,aAAa,MAAM,IAAI;AAClD,MAAI,CAAC,QACH,QAAO;AAET,OAAK,aAAa,YAAY,IAAI;AAClC,QAAM,qCAAqC;GAAE,YAAY;GAAK,QAAQ;GAAiB,CAAC;AACxF,SAAO;;;;;CAMT,kBAAkB,YAA0B;AAC1C,OAAK,aAAa,YAAY,WAAW;;;;;;;CAQ3C,MAAM,oCAAoC,YAAqC;AAC7E,QAAM,KAAK,gBAAgB,UAAU,WAAW;EAChD,MAAM,MAAM,KAAK,OAAO;AAExB,SAAO,iCAAiC,KAAK,YAAY,MADxC,KAAK,mBAAmB,IAAI,WAAW,EACK,qBAAqB,WAAW,CAAC;;;;;;CAOhG,8BAA8B,YAAwC;AAEpE,MAAI;AAEF,OAAI,CADQ,KAAK,oBACT,CAAE,QAAO,KAAA;GACjB,MAAM,WAAW,wBAAwB;AACzC,OAAI,CAAC,WAAW,SAAS,CAAE,QAAO,KAAA;AAIlC,UAHY,aAAa,UAAU,QAClB,CAAC,MAAM,oBACR,GAAG,IAAI,MAAM,IAChB,KAAA;UACP;AACN;;;;;;CAOJ,0BAAkC,YAA0B;AAC1D,MAAI,KAAK,aAAa,4BAA4B,WAAW,CAC3D;EAGF,MAAM,cAAc,KAAK,aAAa,mBAAmB,aAAa,UAAU;AAC9E,QAAK,mBAAmB,YAAY,MAAM;IAC1C;AAEF,MAAI,YACF,MAAK,aAAa,4BAA4B,YAAY,YAAY;;;;;CAO1E,mBAA2B,YAAoB,OAAyB;EACtE,MAAM,iBAAiB,KAAK,sBAAsB,YAAY;AAC9D,MAAI,CAAC,eAEH;AAGF,MAAI,eAAe,eAAe,YAAY;AAE5C,QAAK,kBAAkB,OAAO,OAAO,eAAe;AACpD;;AAIF,MAAI,MAAM,SAAS,kBAAkB;GACnC,MAAM,WAAW;AACjB,OAAI,SAAS,SAAS,SAAS,aAAa;IAC1C,MAAM,UAAU,SAAS,QAAQ;IACjC,MAAM,OAAO,MAAM,QAAQ,QAAQ,GAC/B,mBAAmB,QAAkD,GACrE,OAAO,QAAQ;AAEnB,SAAK,cAAc,OAAO,KAAK;;;AAInC,OAAK,kBAAkB,OAAO,OAAO,eAAe;;CAGtD,MAAc,gBAAgB,YAAoB,UAAyC;EACzF,MAAM,gBAAgB,KAAK,2BAA2B,WAAW;EACjE,MAAM,OAAO,KAAK,aAAa,kBAAkB,YAAY,UAAU,cAAc;AACrF,MAAI,CAAC,KAAK,gBAAiB;AAE3B,MAAI,KAAK;GAAE;GAAY,QAAQ,KAAK,OAAO;GAAQ,cAAc,KAAK,OAAO;GAAc,EAAE,2BAA2B;EAExH,MAAM,SAAS,MAAM,KAAK,aAAa,QAAQ,YAAY,UAAU,eAAe,KAAA,GAAW,MAAM;AACrG,QAAM,KAAK,YAAY,QAAQ,oBAAoB;GACjD,cAAc,SAAS;GACvB,YAAY,OAAO;GACnB,gBAAgB,SAAS,SAAS,OAAO;GAC1C,CAAC;AACF,MAAI,KAAK;GAAE;GAAY,cAAc,OAAO;GAAc,aAAa,OAAO;GAAa,EAAE,oBAAoB;;CAGnH,2BAAmC,YAA4B;AAC7D,MAAI;AACF,UAAO,KAAK,aAAa,2BAA2B,WAAW,CAAC,iBAAiB;WAC1E,KAAK;AACZ,OAAI,KAAK;IAAE;IAAK;IAAY,EAAE,0DAA0D;AACxF,UAAO;;;CAIX,UAAwB;AACtB,OAAK,eAAe,SAAS;AAC7B,OAAK,aAAa,YAAY;AAC9B,OAAK,aAAa,SAAS;AAC3B,OAAK,8BAA8B,SAAS;AAC5C,OAAK,+BAA+B"}
|
|
@@ -19,9 +19,9 @@ export interface SkillsLockFile {
|
|
|
19
19
|
version: 1;
|
|
20
20
|
entries: Record<string, SkillHubLockEntry>;
|
|
21
21
|
}
|
|
22
|
-
export declare function loadSkillsLock(): SkillsLockFile;
|
|
23
|
-
export declare function saveSkillsLock(lock: SkillsLockFile): void;
|
|
22
|
+
export declare function loadSkillsLock(lockPath?: string): SkillsLockFile;
|
|
23
|
+
export declare function saveSkillsLock(lock: SkillsLockFile, lockPath?: string): void;
|
|
24
24
|
/** Record or replace hub metadata for a managed skill directory id (folder name under ~/.xopc/skills). */
|
|
25
|
-
export declare function recordSkillsHubInstall(skillId: string, meta: Pick<SkillHubLockEntry, 'kind' | 'source' | 'ref' | 'subpath'>, contentHash: string): void;
|
|
26
|
-
export declare function removeSkillsLockEntry(skillId: string): void;
|
|
27
|
-
export declare function getSkillsLockEntry(skillId: string): SkillHubLockEntry | undefined;
|
|
25
|
+
export declare function recordSkillsHubInstall(skillId: string, meta: Pick<SkillHubLockEntry, 'kind' | 'source' | 'ref' | 'subpath'>, contentHash: string, lockPath?: string): void;
|
|
26
|
+
export declare function removeSkillsLockEntry(skillId: string, lockPath?: string): void;
|
|
27
|
+
export declare function getSkillsLockEntry(skillId: string, lockPath?: string): SkillHubLockEntry | undefined;
|