@xopcai/xopc 0.0.126 → 0.0.127
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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-C70vgt60.js → agent-avatar-dicebear-CgGgO4OV.js} +1 -1
- package/dist/gateway/static/root/assets/agents-k59l7NMb.js +2 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-D9-HW6cy.js → app-management-settings-panel-DeQsTsyy.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-BHlahIDL.js → appearance-settings-BnHQW7Hv.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-BdJ1eQg3.js → apps-page-BG9b4abG.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-B-aoIt0w.js → archive-plugin-5NXT7Zqv.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-BD0G0FD5.js → attachment-load-DghwhYe0.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-iJRVI7aZ.js → automation-suggestion-card-DYmESgwl.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-De8mVutL.js +4 -0
- package/dist/gateway/static/root/assets/{binary-plugins-B0mNlVSz.js → binary-plugins-DSbPKdWX.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-CJY9Lu5G.js → block-editor-CoZ-_np-.js} +27 -27
- package/dist/gateway/static/root/assets/browser-settings-page-C_ZRa4LF.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-Bgc8ag55.js → capability-presets-api-hny224j-.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-settings-panel-B1nIi0TC.js → capability-presets-settings-panel-Drc0qLsw.js} +1 -1
- package/dist/gateway/static/root/assets/{channel-recipient-api-0sauApoC.js → channel-recipient-api-DAlOHxKJ.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-CA20zBV9.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-qMyMB5te.js +2 -0
- package/dist/gateway/static/root/assets/desktop-pet-XG6Vb_fZ.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-settings-npJ0Oh5p.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-sprite-DqZ6fbIj.js +1 -0
- package/dist/gateway/static/root/assets/directory-picker-path-field-BR2ItBuf.js +1 -0
- package/dist/gateway/static/root/assets/dreaming-settings-CkUZ997f.js +3 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-BbyczVZ3.js → extension-debug-page-Tl0f1rNv.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DLN1I3DR.js → extension-page-Ck7QuwE3.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-D9VGHmYJ.js → extension-settings-page-3RMhQtZ3.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-5V96dUz3.js → fetch-pg2ErdAk.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-BtIzObwp.js → gateway-config-api-DbG97ia2.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-B8cyqKk-.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-DEMad2wt.js → gateway-startup-retry-Yh8h3hkj.js} +1 -1
- package/dist/gateway/static/root/assets/{goal-create-dialog-4PcF6Sh6.js → goal-create-dialog-DmeW7Zz3.js} +1 -1
- package/dist/gateway/static/root/assets/goal-detail-page-DrTyGbVD.js +1 -0
- package/dist/gateway/static/root/assets/{goals-config-api-CVN7-pdM.js → goals-config-api-CxCWGIBB.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-DV1bJhIh.js +2 -0
- package/dist/gateway/static/root/assets/{goals-settings-IYCH3-Pi.js → goals-settings-9DrfOkP5.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-Cf_a2Bmz.js → heartbeat-settings-KaDpZrv0.js} +1 -1
- package/dist/gateway/static/root/assets/index-C2F0Clvm.js +256 -0
- package/dist/gateway/static/root/assets/index-COQSX5d_.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-lKTg6St7.js → keyboard-shortcuts-settings-CfQrt_Rb.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-C9dpiNTF.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-split-Bv4k6iGJ.js → markdown-split-BwiMdCf_.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-Bbi8g3N8.js → media-plugins-DhoizyZv.js} +1 -1
- package/dist/gateway/static/root/assets/messages-DmnbiNxU.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-zdCdmBmH.js +2 -0
- package/dist/gateway/static/root/assets/notes-page-MpGeFfPt.js +1 -0
- package/dist/gateway/static/root/assets/notes-workbench-DYCM3yd2.js +2 -0
- package/dist/gateway/static/root/assets/{page-tabs-Cp395RYD.js → page-tabs-DDC2t4Z8.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-DqV5OM4m.js → preview-open-alternatives-BTmk-XDM.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-Cu0ElRnS.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-DakNaUjZ.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-DYKjt7L9.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-BItmzAzI.js → save-bar-controls-DqU-yKIh.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-BQ8IjjCn.js → schedule-field-CUttkoSB.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-BRpD6x3d.js → schema-form-CVSQA9nI.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-CcGHDsz1.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-CuFdeKNu.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-BKSPQM4R.js → settings-collapsible-section-CK7ogS-P.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-BmyfthNQ.js → settings-form-section-Bugp38Fa.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-0AJoUvxi.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-layout-Bls5tfbH.js → settings-page-layout-DBRWwdhh.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-kQEsTGAs.js → setup-status-panel-D6a_hEk7.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-DbYZNAPb.js → share-preview-page-BtsuETKF.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-CcEFT7S9.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-BcN_RuTD.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-BbRZZZA5.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-DKDAojWM.js → system-settings-panel-DJ6YuLqR.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-BCCV4Kkp.js → typed-models-lib-B0T7N8Km.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-NBqg3iFY.js → use-channel-catalog-C6vrxdzZ.js} +1 -1
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-suQ2x-LB.js → user-profile-settings-panel-DZ0nKMgj.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-DPGVr3tS.js → utils-CehXcWAY.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-2QKflCx8.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-DfJnuWq1.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-BTkRVNAh.js → workflow-run-setup-panel-c4jeaohQ.js} +2 -2
- package/dist/gateway/static/root/assets/{workflow-start-dialog-DAImVtdO.js → workflow-start-dialog-ENRHjoqa.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-BJwwFTm_.js +18 -0
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-instance-gateway.d.ts +5 -0
- package/dist/src/agent/agent-manager.d.ts +14 -0
- package/dist/src/agent/agent-manager.js +111 -2
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/capabilities/definitions.d.ts +153 -0
- package/dist/src/agent/capabilities/definitions.js +156 -0
- package/dist/src/agent/capabilities/definitions.js.map +1 -0
- package/dist/src/agent/capabilities/index.d.ts +2 -0
- package/dist/src/agent/capabilities/index.js +3 -0
- package/dist/src/agent/capabilities/registry.d.ts +10 -0
- package/dist/src/agent/capabilities/registry.js +51 -0
- package/dist/src/agent/capabilities/registry.js.map +1 -0
- package/dist/src/agent/service/process-direct-one-shot.js +5 -3
- package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.js +4 -3
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service.d.ts +2 -0
- package/dist/src/agent/service.js +3 -0
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/skills/parse-skill-metadata.js +9 -1
- package/dist/src/agent/skills/parse-skill-metadata.js.map +1 -1
- package/dist/src/agent/skills/skill-manager.d.ts +4 -0
- package/dist/src/agent/skills/skill-manager.js +17 -0
- package/dist/src/agent/skills/skill-manager.js.map +1 -1
- package/dist/src/agent/skills/types.d.ts +5 -0
- package/dist/src/agent/tools/desktop-pet-tool.d.ts +24 -0
- package/dist/src/agent/tools/desktop-pet-tool.js +90 -0
- package/dist/src/agent/tools/desktop-pet-tool.js.map +1 -0
- package/dist/src/agent/tools/factory.d.ts +2 -0
- package/dist/src/agent/tools/factory.js +17 -0
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/index.d.ts +1 -0
- package/dist/src/agent/tools/index.js +2 -1
- package/dist/src/automations/draft/automation-draft-service.js +1 -1
- package/dist/src/cli/commands/agent.js +2 -2
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/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/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/index.js +1 -1
- package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
- package/dist/src/gateway/agent-builtin-tools.js +1 -0
- package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
- package/dist/src/gateway/hono/routes/commands-skills.js +7 -0
- package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +2 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/projects.js +28 -0
- package/dist/src/gateway/hono/routes/projects.js.map +1 -1
- package/dist/src/gateway/run-loop.js +1 -1
- package/dist/src/gateway/service/run-gateway-agent.js +6 -2
- package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
- package/dist/src/mcp/channel-bridge.js +1 -1
- package/dist/src/pets/factory.d.ts +25 -0
- package/dist/src/pets/factory.js +327 -0
- package/dist/src/pets/factory.js.map +1 -0
- package/dist/src/pets/manifest.d.ts +2 -0
- package/dist/src/pets/manifest.js +16 -0
- package/dist/src/pets/manifest.js.map +1 -0
- package/dist/src/pets/validator.d.ts +12 -0
- package/dist/src/pets/validator.js +175 -0
- package/dist/src/pets/validator.js.map +1 -0
- package/dist/src/projects/project-service.d.ts +2 -0
- package/dist/src/projects/project-service.js +6 -0
- package/dist/src/projects/project-service.js.map +1 -1
- package/dist/src/projects/project-store.d.ts +1 -0
- package/dist/src/projects/project-store.js +14 -7
- package/dist/src/projects/project-store.js.map +1 -1
- package/dist/src/projects/types.d.ts +2 -0
- package/dist/src/session/session-title.js +22 -2
- package/dist/src/session/session-title.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/025_project_pinned_at.sql +5 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/package.json +1 -1
- package/skills/creative/hatch-pet/SKILL.md +63 -0
- package/dist/gateway/static/root/assets/agents-DRYVahI-.js +0 -2
- package/dist/gateway/static/root/assets/automations-page-D8hYQbkl.js +0 -4
- package/dist/gateway/static/root/assets/browser-settings-page-CiWEO-Iy.js +0 -8
- package/dist/gateway/static/root/assets/channels-settings-BddPdqVo.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-Ba2GDhSE.js +0 -2
- package/dist/gateway/static/root/assets/directory-picker-path-field-CiEc4KmY.js +0 -1
- package/dist/gateway/static/root/assets/dist-By6Rh2ee.js +0 -1
- package/dist/gateway/static/root/assets/dreaming-settings-4bvpk5wr.js +0 -3
- package/dist/gateway/static/root/assets/gateway-settings-KFsGGDhg.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-B482PSaG.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-CUdgJuDn.js +0 -2
- package/dist/gateway/static/root/assets/index-CA2tR2vm.js +0 -256
- package/dist/gateway/static/root/assets/index-Ds3u0LOk.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-BCporcNH.js +0 -2
- package/dist/gateway/static/root/assets/messages-C22hxE1N.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-D68Pyow7.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-CuUMOiyv.js +0 -1
- package/dist/gateway/static/root/assets/notes-workbench-Dx2N5zTk.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-sB8AzZi3.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-LBnJqWK2.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-Dbj0o6SC.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-DhDZKspB.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BvXLMNL6.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-Cfm9UxAr.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-BtR4B0kn.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-CJNVOWoK.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-DcWDHrt3.js +0 -2
- package/dist/gateway/static/root/assets/voice-api-key-field-DaJASKFP.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-BrCZpAWh.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-vyJ8wSXm.js +0 -18
|
@@ -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 { 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 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';\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,4 +1,11 @@
|
|
|
1
1
|
//#region src/agent/skills/parse-skill-metadata.ts
|
|
2
|
+
function stringList(value) {
|
|
3
|
+
if (Array.isArray(value)) {
|
|
4
|
+
const out = value.map((x) => String(x).trim()).filter(Boolean);
|
|
5
|
+
return out.length ? out : void 0;
|
|
6
|
+
}
|
|
7
|
+
if (typeof value === "string" && value.trim()) return [value.trim()];
|
|
8
|
+
}
|
|
2
9
|
/**
|
|
3
10
|
* Maps SKILL.md YAML frontmatter to {@link SkillMetadata} (Hermes / xopc shapes).
|
|
4
11
|
*/
|
|
@@ -17,7 +24,8 @@ function parseSkillMetadata(frontmatter) {
|
|
|
17
24
|
emoji: xopcMeta.emoji,
|
|
18
25
|
requires: xopcMeta.requires,
|
|
19
26
|
install: xopcMeta.install,
|
|
20
|
-
os: xopcMeta.os
|
|
27
|
+
os: xopcMeta.os,
|
|
28
|
+
activatesCapabilities: stringList(xopcMeta.activates_capabilities)
|
|
21
29
|
};
|
|
22
30
|
return metadata;
|
|
23
31
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-skill-metadata.js","names":[],"sources":["../../../../src/agent/skills/parse-skill-metadata.ts"],"sourcesContent":["import type { SkillInstallSpec, SkillMetadata, SkillRequires } from './types.js';\n\n/**\n * Maps SKILL.md YAML frontmatter to {@link SkillMetadata} (Hermes / xopc shapes).\n */\nexport function parseSkillMetadata(frontmatter: Record<string, unknown>): SkillMetadata {\n const meta = frontmatter.metadata as Record<string, unknown> | undefined;\n const xopcMeta = meta?.xopc as Record<string, unknown> | undefined;\n\n const metadata: SkillMetadata = {\n name: (frontmatter.name as string) || '',\n description: (frontmatter.description as string) || '',\n emoji: (xopcMeta?.emoji as string) || (frontmatter.emoji as string) || undefined,\n homepage: (frontmatter.homepage as string) || undefined,\n os:\n (xopcMeta?.os as Array<'darwin' | 'linux' | 'win32'>) ||\n (frontmatter.os as Array<'darwin' | 'linux' | 'win32'>) ||\n undefined,\n requires:\n (xopcMeta?.requires as SkillRequires) || (frontmatter.requires as SkillRequires) || undefined,\n install:\n (xopcMeta?.install as SkillInstallSpec[]) || (frontmatter.install as SkillInstallSpec[]) || undefined,\n };\n\n if (xopcMeta) {\n metadata.xopc = {\n emoji: xopcMeta.emoji as string | undefined,\n requires: xopcMeta.requires as SkillRequires | undefined,\n install: xopcMeta.install as SkillInstallSpec[] | undefined,\n os: xopcMeta.os as Array<'darwin' | 'linux' | 'win32'> | undefined,\n };\n }\n\n return metadata;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"parse-skill-metadata.js","names":[],"sources":["../../../../src/agent/skills/parse-skill-metadata.ts"],"sourcesContent":["import type { SkillInstallSpec, SkillMetadata, SkillRequires } from './types.js';\n\nfunction stringList(value: unknown): string[] | undefined {\n if (Array.isArray(value)) {\n const out = value.map((x) => String(x).trim()).filter(Boolean);\n return out.length ? out : undefined;\n }\n if (typeof value === 'string' && value.trim()) {\n return [value.trim()];\n }\n return undefined;\n}\n\n/**\n * Maps SKILL.md YAML frontmatter to {@link SkillMetadata} (Hermes / xopc shapes).\n */\nexport function parseSkillMetadata(frontmatter: Record<string, unknown>): SkillMetadata {\n const meta = frontmatter.metadata as Record<string, unknown> | undefined;\n const xopcMeta = meta?.xopc as Record<string, unknown> | undefined;\n\n const metadata: SkillMetadata = {\n name: (frontmatter.name as string) || '',\n description: (frontmatter.description as string) || '',\n emoji: (xopcMeta?.emoji as string) || (frontmatter.emoji as string) || undefined,\n homepage: (frontmatter.homepage as string) || undefined,\n os:\n (xopcMeta?.os as Array<'darwin' | 'linux' | 'win32'>) ||\n (frontmatter.os as Array<'darwin' | 'linux' | 'win32'>) ||\n undefined,\n requires:\n (xopcMeta?.requires as SkillRequires) || (frontmatter.requires as SkillRequires) || undefined,\n install:\n (xopcMeta?.install as SkillInstallSpec[]) || (frontmatter.install as SkillInstallSpec[]) || undefined,\n };\n\n if (xopcMeta) {\n metadata.xopc = {\n emoji: xopcMeta.emoji as string | undefined,\n requires: xopcMeta.requires as SkillRequires | undefined,\n install: xopcMeta.install as SkillInstallSpec[] | undefined,\n os: xopcMeta.os as Array<'darwin' | 'linux' | 'win32'> | undefined,\n activatesCapabilities: stringList(xopcMeta.activates_capabilities),\n };\n }\n\n return metadata;\n}\n"],"mappings":";AAEA,SAAS,WAAW,OAAsC;AACxD,KAAI,MAAM,QAAQ,MAAM,EAAE;EACxB,MAAM,MAAM,MAAM,KAAK,MAAM,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,QAAQ;AAC9D,SAAO,IAAI,SAAS,MAAM,KAAA;;AAE5B,KAAI,OAAO,UAAU,YAAY,MAAM,MAAM,CAC3C,QAAO,CAAC,MAAM,MAAM,CAAC;;;;;AAQzB,SAAgB,mBAAmB,aAAqD;CAEtF,MAAM,WADO,YAAY,UACF;CAEvB,MAAM,WAA0B;EAC9B,MAAO,YAAY,QAAmB;EACtC,aAAc,YAAY,eAA0B;EACpD,OAAQ,UAAU,SAAqB,YAAY,SAAoB,KAAA;EACvE,UAAW,YAAY,YAAuB,KAAA;EAC9C,IACG,UAAU,MACV,YAAY,MACb,KAAA;EACF,UACG,UAAU,YAA+B,YAAY,YAA8B,KAAA;EACtF,SACG,UAAU,WAAmC,YAAY,WAAkC,KAAA;EAC/F;AAED,KAAI,SACF,UAAS,OAAO;EACd,OAAO,SAAS;EAChB,UAAU,SAAS;EACnB,SAAS,SAAS;EAClB,IAAI,SAAS;EACb,uBAAuB,WAAW,SAAS,uBAAuB;EACnE;AAGH,QAAO"}
|
|
@@ -78,6 +78,10 @@ export declare class SkillManager {
|
|
|
78
78
|
skillAllowlist?: string[];
|
|
79
79
|
registeredToolNames?: string[];
|
|
80
80
|
}): Skill[];
|
|
81
|
+
getActivatedCapabilitiesForText(text: string, options?: {
|
|
82
|
+
skillAllowlist?: string[];
|
|
83
|
+
registeredToolNames?: string[];
|
|
84
|
+
}): string[];
|
|
81
85
|
/**
|
|
82
86
|
* Find a skill by name
|
|
83
87
|
*/
|
|
@@ -14,6 +14,7 @@ import { createSkillLoader } from "./index.js";
|
|
|
14
14
|
init_paths();
|
|
15
15
|
init_logger();
|
|
16
16
|
const log = createLogger("SkillManager");
|
|
17
|
+
const SKILL_TOKEN_RE = /\/skill:([A-Za-z0-9][A-Za-z0-9._-]*)/g;
|
|
17
18
|
var SkillManager = class {
|
|
18
19
|
skillPrompt = "";
|
|
19
20
|
skills = [];
|
|
@@ -187,6 +188,22 @@ var SkillManager = class {
|
|
|
187
188
|
getEnabledSkillsForAgentSession(options) {
|
|
188
189
|
return this.selectSkillsForAgentIndexing(options);
|
|
189
190
|
}
|
|
191
|
+
getActivatedCapabilitiesForText(text, options) {
|
|
192
|
+
if (!text.includes("/skill:")) return [];
|
|
193
|
+
const selected = /* @__PURE__ */ new Set();
|
|
194
|
+
for (const match of text.matchAll(SKILL_TOKEN_RE)) if (match[1]) selected.add(match[1]);
|
|
195
|
+
if (selected.size === 0) return [];
|
|
196
|
+
const visible = this.selectSkillsForAgentIndexing(options);
|
|
197
|
+
const activeCapabilities = /* @__PURE__ */ new Set();
|
|
198
|
+
for (const skill of visible) {
|
|
199
|
+
if (!selected.has(skill.name)) continue;
|
|
200
|
+
for (const capabilityName of skill.metadata.xopc?.activatesCapabilities ?? []) {
|
|
201
|
+
const trimmed = capabilityName.trim();
|
|
202
|
+
if (trimmed) activeCapabilities.add(trimmed);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return [...activeCapabilities];
|
|
206
|
+
}
|
|
190
207
|
/**
|
|
191
208
|
* Find a skill by name
|
|
192
209
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-manager.js","names":[],"sources":["../../../../src/agent/skills/skill-manager.ts"],"sourcesContent":["/**\n * Skill Manager - Manages skills loading and expansion\n *\n * Handles skill initialization, reloading, and command expansion.\n */\n\nimport { createSkillLoader } from './index.js';\nimport { resolveBundledSkillsDir, resolveStateDir } from '../../config/paths.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { createSkillConfigManager, isSkillEnabled } from './config.js';\nimport { formatSkillsForPrompt, selectSkillsVisibleInPrompt } from './format-skills-prompt.js';\nimport type {\n LoadSkillsResult,\n Skill,\n SkillDiagnostic,\n SkillRuntimeStatus,\n} from './types.js';\n\nconst log = createLogger('SkillManager');\n\nexport interface SkillLoadResult {\n skills: Skill[];\n prompt: string;\n diagnostics: SkillDiagnostic[];\n}\n\nexport class SkillManager {\n private skillPrompt: string = '';\n private skills: Skill[] = [];\n private skillLoader = createSkillLoader();\n private workspace: string;\n private bundledSkillsDir: string;\n private version = 0;\n private loadedAt = 0;\n private reloadInProgress = false;\n private reloadPending = false;\n private lastReloadStartedAt: number | undefined;\n private lastReloadFinishedAt: number | undefined;\n private lastReloadReason: SkillRuntimeStatus['lastReloadReason'];\n private lastReloadOk: boolean | undefined;\n private lastReloadError: string | undefined;\n\n constructor(workspace: string, bundledSkillsDir?: string) {\n this.workspace = workspace;\n this.bundledSkillsDir = bundledSkillsDir || resolveBundledSkillsDir();\n this.initialize();\n }\n\n /**\n * Initialize skills from workspace and bundled directories\n */\n private initialize(): void {\n this.runReload('initial', () => this.skillLoader.init(this.workspace, this.bundledSkillsDir));\n }\n\n /**\n * Recompute skill XML prompt from current skills.json (no disk rescan).\n */\n refreshPromptFromConfig(): void {\n this.skillLoader.refreshPromptFromConfig();\n this.skillPrompt = this.skillLoader.getPrompt();\n this.version += 1;\n this.loadedAt = Date.now();\n this.lastReloadReason = 'config';\n this.lastReloadOk = true;\n this.lastReloadError = undefined;\n }\n\n /**\n * Reload skills from disk\n */\n reload(): void {\n this.runReload('disk', () => this.skillLoader.reload());\n }\n\n private runReload(reason: NonNullable<SkillRuntimeStatus['lastReloadReason']>, load: () => LoadSkillsResult): void {\n if (this.reloadInProgress) {\n this.reloadPending = true;\n return;\n }\n\n do {\n this.reloadPending = false;\n this.reloadInProgress = true;\n this.lastReloadStartedAt = Date.now();\n this.lastReloadReason = reason;\n try {\n const result = load();\n this.applyLoadResult(result);\n this.lastReloadOk = true;\n this.lastReloadError = undefined;\n log.info({ count: result.skills.length, version: this.version }, 'Skills reloaded');\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n this.lastReloadOk = false;\n this.lastReloadError = errorMessage;\n log.error({ err, errorMessage }, `Skills reload failed: ${errorMessage}`);\n } finally {\n this.lastReloadFinishedAt = Date.now();\n this.reloadInProgress = false;\n }\n } while (this.reloadPending);\n }\n\n private applyLoadResult(result: LoadSkillsResult): void {\n this.skillPrompt = result.prompt;\n this.skills = result.skills;\n this.version += 1;\n this.loadedAt = Date.now();\n\n for (const diag of result.diagnostics) {\n if (diag.type === 'collision') {\n log.warn({ skill: diag.skillName, path: diag.path, message: diag.message }, 'Skill collision');\n } else if (diag.type === 'warning') {\n log.warn({ skill: diag.skillName, path: diag.path, message: diag.message }, 'Skill warning');\n } else if (diag.type === 'error') {\n log.error({ skill: diag.skillName, path: diag.path, message: diag.message }, 'Skill error');\n }\n }\n }\n\n /**\n * Get the skill prompt to append to system prompt\n */\n getPrompt(): string {\n return this.skillPrompt;\n }\n\n /**\n * `<available_skills>` XML with optional per-agent allowlist and/or tool-based gating.\n * When both arguments are omitted, returns the cached prompt from disk load (no tool gating).\n */\n getPromptForSkillAllowlist(\n allowlist: string[] | undefined,\n registeredToolNames?: string[],\n ): string {\n if (allowlist === undefined && registeredToolNames === undefined) {\n return this.getPrompt();\n }\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n return formatSkillsForPrompt(this.getSkills(), skillsConfig, {\n skillAllowlist: allowlist,\n registeredToolNames,\n });\n }\n\n /** Skills visible for prompts and skills_list given the same indexing options as {@link getPromptForSkillAllowlist}. */\n selectSkillsForAgentIndexing(options?: {\n skillAllowlist?: string[];\n registeredToolNames?: string[];\n }): Skill[] {\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n return selectSkillsVisibleInPrompt(this.getSkills(), skillsConfig, options);\n }\n\n /**\n * Get all loaded skills\n */\n getSkills(): Skill[] {\n return [...this.skills];\n }\n\n getDiagnostics(): SkillDiagnostic[] {\n return this.skillLoader.getDiagnostics();\n }\n\n getVersion(): string {\n return String(this.version);\n }\n\n getLoadedAt(): number {\n return this.loadedAt;\n }\n\n getStatus(): SkillRuntimeStatus {\n return {\n version: this.getVersion(),\n loadedAt: this.loadedAt,\n reloadInProgress: this.reloadInProgress,\n reloadPending: this.reloadPending,\n ...(this.lastReloadStartedAt !== undefined ? { lastReloadStartedAt: this.lastReloadStartedAt } : {}),\n ...(this.lastReloadFinishedAt !== undefined ? { lastReloadFinishedAt: this.lastReloadFinishedAt } : {}),\n ...(this.lastReloadReason !== undefined ? { lastReloadReason: this.lastReloadReason } : {}),\n ...(this.lastReloadOk !== undefined ? { lastReloadOk: this.lastReloadOk } : {}),\n ...(this.lastReloadError !== undefined ? { lastReloadError: this.lastReloadError } : {}),\n };\n }\n\n /**\n * Get skill names\n */\n getSkillNames(): string[] {\n return this.skills.map((s) => s.name);\n }\n\n /** Names of skills included in the agent system prompt (`<available_skills>`). */\n getSkillNamesForPrompt(): string[] {\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n return this.getSkills()\n .filter((s) => !s.disableModelInvocation && isSkillEnabled(s, skillsConfig))\n .map((s) => s.name);\n }\n\n /**\n * Skills that are enabled for model discovery (respects skills.json and requirement checks).\n */\n getEnabledSkills(): Skill[] {\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n return this.getSkills().filter(\n (s) => !s.disableModelInvocation && isSkillEnabled(s, skillsConfig),\n );\n }\n\n /**\n * Like {@link getEnabledSkills} plus optional allowlist and tool gating (matches `<available_skills>`).\n */\n getEnabledSkillsForAgentSession(options?: {\n skillAllowlist?: string[];\n registeredToolNames?: string[];\n }): Skill[] {\n return this.selectSkillsForAgentIndexing(options);\n }\n\n /**\n * Find a skill by name\n */\n findSkill(name: string): Skill | undefined {\n return this.skills.find(s => s.name === name);\n }\n\n /**\n * Check if a skill exists\n */\n hasSkill(name: string): boolean {\n return this.skills.some(s => s.name === name);\n }\n\n /**\n * Expand a skill command into a full skill block\n */\n expandCommand(text: string, options?: { skillAllowlist?: string[]; registeredToolNames?: string[] }): string {\n if (!text.startsWith('/skill:')) {\n return text;\n }\n\n const { skillName, args } = this.parseSkillCommand(text);\n const visible = this.selectSkillsForAgentIndexing(options);\n const skill = visible.find((s) => s.name === skillName);\n\n if (!skill) {\n log.warn({ skillName }, 'Skill not visible for expansion');\n return text;\n }\n\n return this.buildSkillBlock(skill, args);\n }\n\n /**\n * Parse a /skill: command\n */\n private parseSkillCommand(text: string): { skillName: string; args?: string } {\n // Format: /skill:name args...\n const withoutPrefix = text.slice(7); // Remove '/skill:'\n const spaceIndex = withoutPrefix.indexOf(' ');\n\n if (spaceIndex === -1) {\n return { skillName: withoutPrefix.trim() };\n }\n\n return {\n skillName: withoutPrefix.slice(0, spaceIndex).trim(),\n args: withoutPrefix.slice(spaceIndex + 1).trim(),\n };\n }\n\n /**\n * Build a skill block for inclusion in the prompt\n */\n private buildSkillBlock(skill: Skill, args?: string): string {\n let block = `\\n\\n## Skill: ${skill.name}\\n\\n`;\n \n if (skill.description) {\n block += `${skill.description}\\n\\n`;\n }\n\n // Include raw skill content (SKILL.md content)\n if (skill.content) {\n block += `${skill.content}\\n\\n`;\n }\n\n if (args) {\n block += `**Arguments**: ${args}\\n\\n`;\n }\n\n return block;\n }\n}\n"],"mappings":";;;;;;;;;;;;;YAOiF;aAC5B;AAUrD,MAAM,MAAM,aAAa,eAAe;AAQxC,IAAa,eAAb,MAA0B;CACxB,cAA8B;CAC9B,SAA0B,EAAE;CAC5B,cAAsB,mBAAmB;CACzC;CACA;CACA,UAAkB;CAClB,WAAmB;CACnB,mBAA2B;CAC3B,gBAAwB;CACxB;CACA;CACA;CACA;CACA;CAEA,YAAY,WAAmB,kBAA2B;AACxD,OAAK,YAAY;AACjB,OAAK,mBAAmB,oBAAoB,yBAAyB;AACrE,OAAK,YAAY;;;;;CAMnB,aAA2B;AACzB,OAAK,UAAU,iBAAiB,KAAK,YAAY,KAAK,KAAK,WAAW,KAAK,iBAAiB,CAAC;;;;;CAM/F,0BAAgC;AAC9B,OAAK,YAAY,yBAAyB;AAC1C,OAAK,cAAc,KAAK,YAAY,WAAW;AAC/C,OAAK,WAAW;AAChB,OAAK,WAAW,KAAK,KAAK;AAC1B,OAAK,mBAAmB;AACxB,OAAK,eAAe;AACpB,OAAK,kBAAkB,KAAA;;;;;CAMzB,SAAe;AACb,OAAK,UAAU,cAAc,KAAK,YAAY,QAAQ,CAAC;;CAGzD,UAAkB,QAA6D,MAAoC;AACjH,MAAI,KAAK,kBAAkB;AACzB,QAAK,gBAAgB;AACrB;;AAGF,KAAG;AACD,QAAK,gBAAgB;AACrB,QAAK,mBAAmB;AACxB,QAAK,sBAAsB,KAAK,KAAK;AACrC,QAAK,mBAAmB;AACxB,OAAI;IACF,MAAM,SAAS,MAAM;AACrB,SAAK,gBAAgB,OAAO;AAC5B,SAAK,eAAe;AACpB,SAAK,kBAAkB,KAAA;AACvB,QAAI,KAAK;KAAE,OAAO,OAAO,OAAO;KAAQ,SAAS,KAAK;KAAS,EAAE,kBAAkB;YAC5E,KAAK;IACZ,MAAM,eAAe,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AACrE,SAAK,eAAe;AACpB,SAAK,kBAAkB;AACvB,QAAI,MAAM;KAAE;KAAK;KAAc,EAAE,yBAAyB,eAAe;aACjE;AACR,SAAK,uBAAuB,KAAK,KAAK;AACtC,SAAK,mBAAmB;;WAEnB,KAAK;;CAGhB,gBAAwB,QAAgC;AACtD,OAAK,cAAc,OAAO;AAC1B,OAAK,SAAS,OAAO;AACrB,OAAK,WAAW;AAChB,OAAK,WAAW,KAAK,KAAK;AAE1B,OAAK,MAAM,QAAQ,OAAO,YACxB,KAAI,KAAK,SAAS,YAChB,KAAI,KAAK;GAAE,OAAO,KAAK;GAAW,MAAM,KAAK;GAAM,SAAS,KAAK;GAAS,EAAE,kBAAkB;WACrF,KAAK,SAAS,UACvB,KAAI,KAAK;GAAE,OAAO,KAAK;GAAW,MAAM,KAAK;GAAM,SAAS,KAAK;GAAS,EAAE,gBAAgB;WACnF,KAAK,SAAS,QACvB,KAAI,MAAM;GAAE,OAAO,KAAK;GAAW,MAAM,KAAK;GAAM,SAAS,KAAK;GAAS,EAAE,cAAc;;;;;CAQjG,YAAoB;AAClB,SAAO,KAAK;;;;;;CAOd,2BACE,WACA,qBACQ;AACR,MAAI,cAAc,KAAA,KAAa,wBAAwB,KAAA,EACrD,QAAO,KAAK,WAAW;EAEzB,MAAM,eAAe,yBAAyB,iBAAiB,CAAC,CAAC,MAAM;AACvE,SAAO,sBAAsB,KAAK,WAAW,EAAE,cAAc;GAC3D,gBAAgB;GAChB;GACD,CAAC;;;CAIJ,6BAA6B,SAGjB;EACV,MAAM,eAAe,yBAAyB,iBAAiB,CAAC,CAAC,MAAM;AACvE,SAAO,4BAA4B,KAAK,WAAW,EAAE,cAAc,QAAQ;;;;;CAM7E,YAAqB;AACnB,SAAO,CAAC,GAAG,KAAK,OAAO;;CAGzB,iBAAoC;AAClC,SAAO,KAAK,YAAY,gBAAgB;;CAG1C,aAAqB;AACnB,SAAO,OAAO,KAAK,QAAQ;;CAG7B,cAAsB;AACpB,SAAO,KAAK;;CAGd,YAAgC;AAC9B,SAAO;GACL,SAAS,KAAK,YAAY;GAC1B,UAAU,KAAK;GACf,kBAAkB,KAAK;GACvB,eAAe,KAAK;GACpB,GAAI,KAAK,wBAAwB,KAAA,IAAY,EAAE,qBAAqB,KAAK,qBAAqB,GAAG,EAAE;GACnG,GAAI,KAAK,yBAAyB,KAAA,IAAY,EAAE,sBAAsB,KAAK,sBAAsB,GAAG,EAAE;GACtG,GAAI,KAAK,qBAAqB,KAAA,IAAY,EAAE,kBAAkB,KAAK,kBAAkB,GAAG,EAAE;GAC1F,GAAI,KAAK,iBAAiB,KAAA,IAAY,EAAE,cAAc,KAAK,cAAc,GAAG,EAAE;GAC9E,GAAI,KAAK,oBAAoB,KAAA,IAAY,EAAE,iBAAiB,KAAK,iBAAiB,GAAG,EAAE;GACxF;;;;;CAMH,gBAA0B;AACxB,SAAO,KAAK,OAAO,KAAK,MAAM,EAAE,KAAK;;;CAIvC,yBAAmC;EACjC,MAAM,eAAe,yBAAyB,iBAAiB,CAAC,CAAC,MAAM;AACvE,SAAO,KAAK,WAAW,CACpB,QAAQ,MAAM,CAAC,EAAE,0BAA0B,eAAe,GAAG,aAAa,CAAC,CAC3E,KAAK,MAAM,EAAE,KAAK;;;;;CAMvB,mBAA4B;EAC1B,MAAM,eAAe,yBAAyB,iBAAiB,CAAC,CAAC,MAAM;AACvE,SAAO,KAAK,WAAW,CAAC,QACrB,MAAM,CAAC,EAAE,0BAA0B,eAAe,GAAG,aAAa,CACpE;;;;;CAMH,gCAAgC,SAGpB;AACV,SAAO,KAAK,6BAA6B,QAAQ;;;;;CAMnD,UAAU,MAAiC;AACzC,SAAO,KAAK,OAAO,MAAK,MAAK,EAAE,SAAS,KAAK;;;;;CAM/C,SAAS,MAAuB;AAC9B,SAAO,KAAK,OAAO,MAAK,MAAK,EAAE,SAAS,KAAK;;;;;CAM/C,cAAc,MAAc,SAAiF;AAC3G,MAAI,CAAC,KAAK,WAAW,UAAU,CAC7B,QAAO;EAGT,MAAM,EAAE,WAAW,SAAS,KAAK,kBAAkB,KAAK;EAExD,MAAM,QADU,KAAK,6BAA6B,QAC7B,CAAC,MAAM,MAAM,EAAE,SAAS,UAAU;AAEvD,MAAI,CAAC,OAAO;AACV,OAAI,KAAK,EAAE,WAAW,EAAE,kCAAkC;AAC1D,UAAO;;AAGT,SAAO,KAAK,gBAAgB,OAAO,KAAK;;;;;CAM1C,kBAA0B,MAAoD;EAE5E,MAAM,gBAAgB,KAAK,MAAM,EAAE;EACnC,MAAM,aAAa,cAAc,QAAQ,IAAI;AAE7C,MAAI,eAAe,GACjB,QAAO,EAAE,WAAW,cAAc,MAAM,EAAE;AAG5C,SAAO;GACL,WAAW,cAAc,MAAM,GAAG,WAAW,CAAC,MAAM;GACpD,MAAM,cAAc,MAAM,aAAa,EAAE,CAAC,MAAM;GACjD;;;;;CAMH,gBAAwB,OAAc,MAAuB;EAC3D,IAAI,QAAQ,iBAAiB,MAAM,KAAK;AAExC,MAAI,MAAM,YACR,UAAS,GAAG,MAAM,YAAY;AAIhC,MAAI,MAAM,QACR,UAAS,GAAG,MAAM,QAAQ;AAG5B,MAAI,KACF,UAAS,kBAAkB,KAAK;AAGlC,SAAO"}
|
|
1
|
+
{"version":3,"file":"skill-manager.js","names":[],"sources":["../../../../src/agent/skills/skill-manager.ts"],"sourcesContent":["/**\n * Skill Manager - Manages skills loading and expansion\n *\n * Handles skill initialization, reloading, and command expansion.\n */\n\nimport { createSkillLoader } from './index.js';\nimport { resolveBundledSkillsDir, resolveStateDir } from '../../config/paths.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { createSkillConfigManager, isSkillEnabled } from './config.js';\nimport { formatSkillsForPrompt, selectSkillsVisibleInPrompt } from './format-skills-prompt.js';\nimport type {\n LoadSkillsResult,\n Skill,\n SkillDiagnostic,\n SkillRuntimeStatus,\n} from './types.js';\n\nconst log = createLogger('SkillManager');\nconst SKILL_TOKEN_RE = /\\/skill:([A-Za-z0-9][A-Za-z0-9._-]*)/g;\n\nexport interface SkillLoadResult {\n skills: Skill[];\n prompt: string;\n diagnostics: SkillDiagnostic[];\n}\n\nexport class SkillManager {\n private skillPrompt: string = '';\n private skills: Skill[] = [];\n private skillLoader = createSkillLoader();\n private workspace: string;\n private bundledSkillsDir: string;\n private version = 0;\n private loadedAt = 0;\n private reloadInProgress = false;\n private reloadPending = false;\n private lastReloadStartedAt: number | undefined;\n private lastReloadFinishedAt: number | undefined;\n private lastReloadReason: SkillRuntimeStatus['lastReloadReason'];\n private lastReloadOk: boolean | undefined;\n private lastReloadError: string | undefined;\n\n constructor(workspace: string, bundledSkillsDir?: string) {\n this.workspace = workspace;\n this.bundledSkillsDir = bundledSkillsDir || resolveBundledSkillsDir();\n this.initialize();\n }\n\n /**\n * Initialize skills from workspace and bundled directories\n */\n private initialize(): void {\n this.runReload('initial', () => this.skillLoader.init(this.workspace, this.bundledSkillsDir));\n }\n\n /**\n * Recompute skill XML prompt from current skills.json (no disk rescan).\n */\n refreshPromptFromConfig(): void {\n this.skillLoader.refreshPromptFromConfig();\n this.skillPrompt = this.skillLoader.getPrompt();\n this.version += 1;\n this.loadedAt = Date.now();\n this.lastReloadReason = 'config';\n this.lastReloadOk = true;\n this.lastReloadError = undefined;\n }\n\n /**\n * Reload skills from disk\n */\n reload(): void {\n this.runReload('disk', () => this.skillLoader.reload());\n }\n\n private runReload(reason: NonNullable<SkillRuntimeStatus['lastReloadReason']>, load: () => LoadSkillsResult): void {\n if (this.reloadInProgress) {\n this.reloadPending = true;\n return;\n }\n\n do {\n this.reloadPending = false;\n this.reloadInProgress = true;\n this.lastReloadStartedAt = Date.now();\n this.lastReloadReason = reason;\n try {\n const result = load();\n this.applyLoadResult(result);\n this.lastReloadOk = true;\n this.lastReloadError = undefined;\n log.info({ count: result.skills.length, version: this.version }, 'Skills reloaded');\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n this.lastReloadOk = false;\n this.lastReloadError = errorMessage;\n log.error({ err, errorMessage }, `Skills reload failed: ${errorMessage}`);\n } finally {\n this.lastReloadFinishedAt = Date.now();\n this.reloadInProgress = false;\n }\n } while (this.reloadPending);\n }\n\n private applyLoadResult(result: LoadSkillsResult): void {\n this.skillPrompt = result.prompt;\n this.skills = result.skills;\n this.version += 1;\n this.loadedAt = Date.now();\n\n for (const diag of result.diagnostics) {\n if (diag.type === 'collision') {\n log.warn({ skill: diag.skillName, path: diag.path, message: diag.message }, 'Skill collision');\n } else if (diag.type === 'warning') {\n log.warn({ skill: diag.skillName, path: diag.path, message: diag.message }, 'Skill warning');\n } else if (diag.type === 'error') {\n log.error({ skill: diag.skillName, path: diag.path, message: diag.message }, 'Skill error');\n }\n }\n }\n\n /**\n * Get the skill prompt to append to system prompt\n */\n getPrompt(): string {\n return this.skillPrompt;\n }\n\n /**\n * `<available_skills>` XML with optional per-agent allowlist and/or tool-based gating.\n * When both arguments are omitted, returns the cached prompt from disk load (no tool gating).\n */\n getPromptForSkillAllowlist(\n allowlist: string[] | undefined,\n registeredToolNames?: string[],\n ): string {\n if (allowlist === undefined && registeredToolNames === undefined) {\n return this.getPrompt();\n }\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n return formatSkillsForPrompt(this.getSkills(), skillsConfig, {\n skillAllowlist: allowlist,\n registeredToolNames,\n });\n }\n\n /** Skills visible for prompts and skills_list given the same indexing options as {@link getPromptForSkillAllowlist}. */\n selectSkillsForAgentIndexing(options?: {\n skillAllowlist?: string[];\n registeredToolNames?: string[];\n }): Skill[] {\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n return selectSkillsVisibleInPrompt(this.getSkills(), skillsConfig, options);\n }\n\n /**\n * Get all loaded skills\n */\n getSkills(): Skill[] {\n return [...this.skills];\n }\n\n getDiagnostics(): SkillDiagnostic[] {\n return this.skillLoader.getDiagnostics();\n }\n\n getVersion(): string {\n return String(this.version);\n }\n\n getLoadedAt(): number {\n return this.loadedAt;\n }\n\n getStatus(): SkillRuntimeStatus {\n return {\n version: this.getVersion(),\n loadedAt: this.loadedAt,\n reloadInProgress: this.reloadInProgress,\n reloadPending: this.reloadPending,\n ...(this.lastReloadStartedAt !== undefined ? { lastReloadStartedAt: this.lastReloadStartedAt } : {}),\n ...(this.lastReloadFinishedAt !== undefined ? { lastReloadFinishedAt: this.lastReloadFinishedAt } : {}),\n ...(this.lastReloadReason !== undefined ? { lastReloadReason: this.lastReloadReason } : {}),\n ...(this.lastReloadOk !== undefined ? { lastReloadOk: this.lastReloadOk } : {}),\n ...(this.lastReloadError !== undefined ? { lastReloadError: this.lastReloadError } : {}),\n };\n }\n\n /**\n * Get skill names\n */\n getSkillNames(): string[] {\n return this.skills.map((s) => s.name);\n }\n\n /** Names of skills included in the agent system prompt (`<available_skills>`). */\n getSkillNamesForPrompt(): string[] {\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n return this.getSkills()\n .filter((s) => !s.disableModelInvocation && isSkillEnabled(s, skillsConfig))\n .map((s) => s.name);\n }\n\n /**\n * Skills that are enabled for model discovery (respects skills.json and requirement checks).\n */\n getEnabledSkills(): Skill[] {\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n return this.getSkills().filter(\n (s) => !s.disableModelInvocation && isSkillEnabled(s, skillsConfig),\n );\n }\n\n /**\n * Like {@link getEnabledSkills} plus optional allowlist and tool gating (matches `<available_skills>`).\n */\n getEnabledSkillsForAgentSession(options?: {\n skillAllowlist?: string[];\n registeredToolNames?: string[];\n }): Skill[] {\n return this.selectSkillsForAgentIndexing(options);\n }\n\n getActivatedCapabilitiesForText(\n text: string,\n options?: { skillAllowlist?: string[]; registeredToolNames?: string[] },\n ): string[] {\n if (!text.includes('/skill:')) return [];\n const selected = new Set<string>();\n for (const match of text.matchAll(SKILL_TOKEN_RE)) {\n if (match[1]) selected.add(match[1]);\n }\n if (selected.size === 0) return [];\n\n const visible = this.selectSkillsForAgentIndexing(options);\n const activeCapabilities = new Set<string>();\n for (const skill of visible) {\n if (!selected.has(skill.name)) continue;\n for (const capabilityName of skill.metadata.xopc?.activatesCapabilities ?? []) {\n const trimmed = capabilityName.trim();\n if (trimmed) activeCapabilities.add(trimmed);\n }\n }\n return [...activeCapabilities];\n }\n\n /**\n * Find a skill by name\n */\n findSkill(name: string): Skill | undefined {\n return this.skills.find(s => s.name === name);\n }\n\n /**\n * Check if a skill exists\n */\n hasSkill(name: string): boolean {\n return this.skills.some(s => s.name === name);\n }\n\n /**\n * Expand a skill command into a full skill block\n */\n expandCommand(text: string, options?: { skillAllowlist?: string[]; registeredToolNames?: string[] }): string {\n if (!text.startsWith('/skill:')) {\n return text;\n }\n\n const { skillName, args } = this.parseSkillCommand(text);\n const visible = this.selectSkillsForAgentIndexing(options);\n const skill = visible.find((s) => s.name === skillName);\n\n if (!skill) {\n log.warn({ skillName }, 'Skill not visible for expansion');\n return text;\n }\n\n return this.buildSkillBlock(skill, args);\n }\n\n /**\n * Parse a /skill: command\n */\n private parseSkillCommand(text: string): { skillName: string; args?: string } {\n // Format: /skill:name args...\n const withoutPrefix = text.slice(7); // Remove '/skill:'\n const spaceIndex = withoutPrefix.indexOf(' ');\n\n if (spaceIndex === -1) {\n return { skillName: withoutPrefix.trim() };\n }\n\n return {\n skillName: withoutPrefix.slice(0, spaceIndex).trim(),\n args: withoutPrefix.slice(spaceIndex + 1).trim(),\n };\n }\n\n /**\n * Build a skill block for inclusion in the prompt\n */\n private buildSkillBlock(skill: Skill, args?: string): string {\n let block = `\\n\\n## Skill: ${skill.name}\\n\\n`;\n \n if (skill.description) {\n block += `${skill.description}\\n\\n`;\n }\n\n // Include raw skill content (SKILL.md content)\n if (skill.content) {\n block += `${skill.content}\\n\\n`;\n }\n\n if (args) {\n block += `**Arguments**: ${args}\\n\\n`;\n }\n\n return block;\n }\n}\n"],"mappings":"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"}
|
|
@@ -60,6 +60,11 @@ export interface SkillMetadata {
|
|
|
60
60
|
requires?: SkillRequires;
|
|
61
61
|
install?: SkillInstallSpec[];
|
|
62
62
|
os?: Array<'darwin' | 'linux' | 'win32'>;
|
|
63
|
+
/**
|
|
64
|
+
* Lazy capability packs to inject only when this skill is explicitly selected for a turn.
|
|
65
|
+
* Parsed from `metadata.xopc.activates_capabilities`.
|
|
66
|
+
*/
|
|
67
|
+
activatesCapabilities?: string[];
|
|
63
68
|
};
|
|
64
69
|
}
|
|
65
70
|
export interface SkillConfig {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AgentToolWithMetadata } from './metadata.js';
|
|
2
|
+
declare const DesktopPetCreateSchema: import("@sinclair/typebox/build/cjs/index.js").TObject<{
|
|
3
|
+
mode: import("@sinclair/typebox/build/cjs/index.js").TOptional<import("@sinclair/typebox/build/cjs/index.js").TUnion<[import("@sinclair/typebox/build/cjs/index.js").TLiteral<"create">, import("@sinclair/typebox/build/cjs/index.js").TLiteral<"update">]>>;
|
|
4
|
+
petId: import("@sinclair/typebox/build/cjs/index.js").TOptional<import("@sinclair/typebox/build/cjs/index.js").TString>;
|
|
5
|
+
prompt: import("@sinclair/typebox/build/cjs/index.js").TString;
|
|
6
|
+
name: import("@sinclair/typebox/build/cjs/index.js").TOptional<import("@sinclair/typebox/build/cjs/index.js").TString>;
|
|
7
|
+
description: import("@sinclair/typebox/build/cjs/index.js").TOptional<import("@sinclair/typebox/build/cjs/index.js").TString>;
|
|
8
|
+
overwrite: import("@sinclair/typebox/build/cjs/index.js").TOptional<import("@sinclair/typebox/build/cjs/index.js").TBoolean>;
|
|
9
|
+
}>;
|
|
10
|
+
export type DesktopPetCreateDetails = {
|
|
11
|
+
ok: true;
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
dir: string;
|
|
15
|
+
manifestPath: string;
|
|
16
|
+
thumbnailPath: string;
|
|
17
|
+
spritesheetPath: string;
|
|
18
|
+
mode: 'create' | 'update';
|
|
19
|
+
} | {
|
|
20
|
+
ok: false;
|
|
21
|
+
error: string;
|
|
22
|
+
};
|
|
23
|
+
export declare function createDesktopPetTool(): AgentToolWithMetadata<typeof DesktopPetCreateSchema, DesktopPetCreateDetails>;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createDesktopPetPackage } from "../../pets/factory.js";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
|
+
//#region src/agent/tools/desktop-pet-tool.ts
|
|
4
|
+
const DesktopPetCreateSchema = Type.Object({
|
|
5
|
+
mode: Type.Optional(Type.Union([Type.Literal("create"), Type.Literal("update")], { description: "create installs a new pet package. update overwrites an existing custom pet package identified by petId." })),
|
|
6
|
+
petId: Type.Optional(Type.String({ description: "Existing custom pet id to update. Accepts either \"custom:<id>\" from Settings or the folder id under ~/.xopc/pets." })),
|
|
7
|
+
prompt: Type.String({ description: "Full description of the desired pet character, visual style, personality, colors, motifs, and requested changes." }),
|
|
8
|
+
name: Type.Optional(Type.String({ description: "Optional display name for the pet." })),
|
|
9
|
+
description: Type.Optional(Type.String({ description: "Optional short description shown in Settings." })),
|
|
10
|
+
overwrite: Type.Optional(Type.Boolean({ description: "When true, reuse the folder derived from the pet name. Default false creates a unique folder if one exists." }))
|
|
11
|
+
});
|
|
12
|
+
function createDesktopPetTool() {
|
|
13
|
+
return {
|
|
14
|
+
name: "create_desktop_pet",
|
|
15
|
+
label: "Create Pet",
|
|
16
|
+
description: "Create or update a custom animated desktop pet from a natural-language description. Packages are written under ~/.xopc/pets and can be selected in Settings > Pet. Use mode=update with petId when iterating on an existing custom pet.",
|
|
17
|
+
parameters: DesktopPetCreateSchema,
|
|
18
|
+
mutatesWorkspace: false,
|
|
19
|
+
mutationScope: "external",
|
|
20
|
+
supportsParallel: false,
|
|
21
|
+
idempotent: false,
|
|
22
|
+
requiresExclusiveWorkspaceLock: false,
|
|
23
|
+
finalGuardRelevant: false,
|
|
24
|
+
async execute(_toolCallId, params) {
|
|
25
|
+
const prompt = typeof params.prompt === "string" ? params.prompt.trim() : "";
|
|
26
|
+
if (!prompt) return {
|
|
27
|
+
content: [{
|
|
28
|
+
type: "text",
|
|
29
|
+
text: "prompt is required."
|
|
30
|
+
}],
|
|
31
|
+
details: {
|
|
32
|
+
ok: false,
|
|
33
|
+
error: "prompt is required"
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const mode = params.mode === "update" ? "update" : "create";
|
|
37
|
+
const petId = typeof params.petId === "string" ? params.petId.trim() : "";
|
|
38
|
+
if (mode === "update" && !petId) return {
|
|
39
|
+
content: [{
|
|
40
|
+
type: "text",
|
|
41
|
+
text: "petId is required when mode is update."
|
|
42
|
+
}],
|
|
43
|
+
details: {
|
|
44
|
+
ok: false,
|
|
45
|
+
error: "petId is required when mode is update"
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
try {
|
|
49
|
+
const result = await createDesktopPetPackage({
|
|
50
|
+
...mode === "update" ? { id: petId } : {},
|
|
51
|
+
prompt,
|
|
52
|
+
...typeof params.name === "string" && params.name.trim() ? { name: params.name.trim() } : {},
|
|
53
|
+
...typeof params.description === "string" && params.description.trim() ? { description: params.description.trim() } : {},
|
|
54
|
+
overwrite: mode === "update" ? true : params.overwrite === true
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
content: [{
|
|
58
|
+
type: "text",
|
|
59
|
+
text: [
|
|
60
|
+
`${mode === "update" ? "Updated" : "Created"} desktop pet "${result.name}".`,
|
|
61
|
+
`Installed at: ${result.dir}`,
|
|
62
|
+
"Open Settings > Pet, refresh if needed, and select the new custom pet."
|
|
63
|
+
].join("\n")
|
|
64
|
+
}],
|
|
65
|
+
details: {
|
|
66
|
+
ok: true,
|
|
67
|
+
mode,
|
|
68
|
+
...result
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
} catch (e) {
|
|
72
|
+
const error = e instanceof Error ? e.message : String(e);
|
|
73
|
+
return {
|
|
74
|
+
content: [{
|
|
75
|
+
type: "text",
|
|
76
|
+
text: `Failed to create desktop pet: ${error}`
|
|
77
|
+
}],
|
|
78
|
+
details: {
|
|
79
|
+
ok: false,
|
|
80
|
+
error
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
export { createDesktopPetTool };
|
|
89
|
+
|
|
90
|
+
//# sourceMappingURL=desktop-pet-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"desktop-pet-tool.js","names":[],"sources":["../../../../src/agent/tools/desktop-pet-tool.ts"],"sourcesContent":["import { Type } from '@sinclair/typebox';\nimport type { AgentToolResult } from '@earendil-works/pi-agent-core';\n\nimport { createDesktopPetPackage } from '../../pets/factory.js';\nimport type { AgentToolWithMetadata } from './metadata.js';\n\nconst DesktopPetCreateSchema = Type.Object({\n mode: Type.Optional(\n Type.Union([Type.Literal('create'), Type.Literal('update')], {\n description:\n 'create installs a new pet package. update overwrites an existing custom pet package identified by petId.',\n }),\n ),\n petId: Type.Optional(\n Type.String({\n description:\n 'Existing custom pet id to update. Accepts either \"custom:<id>\" from Settings or the folder id under ~/.xopc/pets.',\n }),\n ),\n prompt: Type.String({\n description:\n 'Full description of the desired pet character, visual style, personality, colors, motifs, and requested changes.',\n }),\n name: Type.Optional(Type.String({ description: 'Optional display name for the pet.' })),\n description: Type.Optional(Type.String({ description: 'Optional short description shown in Settings.' })),\n overwrite: Type.Optional(\n Type.Boolean({\n description:\n 'When true, reuse the folder derived from the pet name. Default false creates a unique folder if one exists.',\n }),\n ),\n});\n\nexport type DesktopPetCreateDetails =\n | {\n ok: true;\n id: string;\n name: string;\n dir: string;\n manifestPath: string;\n thumbnailPath: string;\n spritesheetPath: string;\n mode: 'create' | 'update';\n }\n | {\n ok: false;\n error: string;\n };\n\nexport function createDesktopPetTool(): AgentToolWithMetadata<typeof DesktopPetCreateSchema, DesktopPetCreateDetails> {\n return {\n name: 'create_desktop_pet',\n label: 'Create Pet',\n description:\n 'Create or update a custom animated desktop pet from a natural-language description. Packages are written under ~/.xopc/pets and can be selected in Settings > Pet. Use mode=update with petId when iterating on an existing custom pet.',\n parameters: DesktopPetCreateSchema,\n mutatesWorkspace: false,\n mutationScope: 'external',\n supportsParallel: false,\n idempotent: false,\n requiresExclusiveWorkspaceLock: false,\n finalGuardRelevant: false,\n async execute(\n _toolCallId: string,\n params: Record<string, unknown>,\n ): Promise<AgentToolResult<DesktopPetCreateDetails>> {\n const prompt = typeof params.prompt === 'string' ? params.prompt.trim() : '';\n if (!prompt) {\n return {\n content: [{ type: 'text', text: 'prompt is required.' }],\n details: { ok: false, error: 'prompt is required' },\n };\n }\n const mode = params.mode === 'update' ? 'update' : 'create';\n const petId = typeof params.petId === 'string' ? params.petId.trim() : '';\n if (mode === 'update' && !petId) {\n return {\n content: [{ type: 'text', text: 'petId is required when mode is update.' }],\n details: { ok: false, error: 'petId is required when mode is update' },\n };\n }\n\n try {\n const result = await createDesktopPetPackage({\n ...(mode === 'update' ? { id: petId } : {}),\n prompt,\n ...(typeof params.name === 'string' && params.name.trim() ? { name: params.name.trim() } : {}),\n ...(typeof params.description === 'string' && params.description.trim()\n ? { description: params.description.trim() }\n : {}),\n overwrite: mode === 'update' ? true : params.overwrite === true,\n });\n return {\n content: [\n {\n type: 'text',\n text: [\n `${mode === 'update' ? 'Updated' : 'Created'} desktop pet \"${result.name}\".`,\n `Installed at: ${result.dir}`,\n 'Open Settings > Pet, refresh if needed, and select the new custom pet.',\n ].join('\\n'),\n },\n ],\n details: { ok: true, mode, ...result },\n };\n } catch (e) {\n const error = e instanceof Error ? e.message : String(e);\n return {\n content: [{ type: 'text', text: `Failed to create desktop pet: ${error}` }],\n details: { ok: false, error },\n };\n }\n },\n };\n}\n"],"mappings":";;;AAMA,MAAM,yBAAyB,KAAK,OAAO;CACzC,MAAM,KAAK,SACT,KAAK,MAAM,CAAC,KAAK,QAAQ,SAAS,EAAE,KAAK,QAAQ,SAAS,CAAC,EAAE,EAC3D,aACE,4GACH,CAAC,CACH;CACD,OAAO,KAAK,SACV,KAAK,OAAO,EACV,aACE,uHACH,CAAC,CACH;CACD,QAAQ,KAAK,OAAO,EAClB,aACE,oHACH,CAAC;CACF,MAAM,KAAK,SAAS,KAAK,OAAO,EAAE,aAAa,sCAAsC,CAAC,CAAC;CACvF,aAAa,KAAK,SAAS,KAAK,OAAO,EAAE,aAAa,iDAAiD,CAAC,CAAC;CACzG,WAAW,KAAK,SACd,KAAK,QAAQ,EACX,aACE,+GACH,CAAC,CACH;CACF,CAAC;AAkBF,SAAgB,uBAAsG;AACpH,QAAO;EACL,MAAM;EACN,OAAO;EACP,aACE;EACF,YAAY;EACZ,kBAAkB;EAClB,eAAe;EACf,kBAAkB;EAClB,YAAY;EACZ,gCAAgC;EAChC,oBAAoB;EACpB,MAAM,QACJ,aACA,QACmD;GACnD,MAAM,SAAS,OAAO,OAAO,WAAW,WAAW,OAAO,OAAO,MAAM,GAAG;AAC1E,OAAI,CAAC,OACH,QAAO;IACL,SAAS,CAAC;KAAE,MAAM;KAAQ,MAAM;KAAuB,CAAC;IACxD,SAAS;KAAE,IAAI;KAAO,OAAO;KAAsB;IACpD;GAEH,MAAM,OAAO,OAAO,SAAS,WAAW,WAAW;GACnD,MAAM,QAAQ,OAAO,OAAO,UAAU,WAAW,OAAO,MAAM,MAAM,GAAG;AACvE,OAAI,SAAS,YAAY,CAAC,MACxB,QAAO;IACL,SAAS,CAAC;KAAE,MAAM;KAAQ,MAAM;KAA0C,CAAC;IAC3E,SAAS;KAAE,IAAI;KAAO,OAAO;KAAyC;IACvE;AAGH,OAAI;IACF,MAAM,SAAS,MAAM,wBAAwB;KAC3C,GAAI,SAAS,WAAW,EAAE,IAAI,OAAO,GAAG,EAAE;KAC1C;KACA,GAAI,OAAO,OAAO,SAAS,YAAY,OAAO,KAAK,MAAM,GAAG,EAAE,MAAM,OAAO,KAAK,MAAM,EAAE,GAAG,EAAE;KAC7F,GAAI,OAAO,OAAO,gBAAgB,YAAY,OAAO,YAAY,MAAM,GACnE,EAAE,aAAa,OAAO,YAAY,MAAM,EAAE,GAC1C,EAAE;KACN,WAAW,SAAS,WAAW,OAAO,OAAO,cAAc;KAC5D,CAAC;AACF,WAAO;KACL,SAAS,CACP;MACE,MAAM;MACN,MAAM;OACJ,GAAG,SAAS,WAAW,YAAY,UAAU,gBAAgB,OAAO,KAAK;OACzE,iBAAiB,OAAO;OACxB;OACD,CAAC,KAAK,KAAK;MACb,CACF;KACD,SAAS;MAAE,IAAI;MAAM;MAAM,GAAG;MAAQ;KACvC;YACM,GAAG;IACV,MAAM,QAAQ,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;AACxD,WAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,iCAAiC;MAAS,CAAC;KAC3E,SAAS;MAAE,IAAI;MAAO;MAAO;KAC9B;;;EAGN"}
|
|
@@ -99,5 +99,7 @@ export declare class AgentToolsFactory {
|
|
|
99
99
|
/** Drop the tab for a session when its agent instance is removed. */
|
|
100
100
|
closeBrowserPageForSession(sessionKey: string): Promise<void>;
|
|
101
101
|
createCoreTools(options?: CreateCoreToolsOptions): AgentTool<any, any>[];
|
|
102
|
+
createCapabilityTools(capabilityNames: readonly string[], options?: Pick<CreateCoreToolsOptions, 'disabledTools'>): AgentTool<any, any>[];
|
|
103
|
+
getLazyCapabilityToolNames(): string[];
|
|
102
104
|
createAllTools(coreOptions?: CreateCoreToolsOptions): AgentTool<any, any>[];
|
|
103
105
|
}
|
|
@@ -37,6 +37,7 @@ import { createXopcUseTool } from "./xopc-use-tool.js";
|
|
|
37
37
|
import { createSkillViewTool, createSkillsListTool } from "./skills-tools.js";
|
|
38
38
|
import { createSkillManageTool } from "./skill-manage-tool.js";
|
|
39
39
|
import { createToolManualTool } from "./tool-manual-tool.js";
|
|
40
|
+
import { createDesktopPetTool } from "./desktop-pet-tool.js";
|
|
40
41
|
import { createImageTool } from "./image-tool.js";
|
|
41
42
|
import { createImageGenerateTool } from "./image-generate-tool.js";
|
|
42
43
|
import "./index.js";
|
|
@@ -47,6 +48,8 @@ import { wrapToolsWithProtection } from "./executor.js";
|
|
|
47
48
|
import { mergeTtsConfigFromAppConfig } from "../../voice/tts/merge-config.js";
|
|
48
49
|
import { createTextToSpeechTool } from "./tts-tool.js";
|
|
49
50
|
import { createGoalEvidenceRecorder } from "./goal-evidence-recorder.js";
|
|
51
|
+
import { getAgentCapabilityToolNames } from "../capabilities/registry.js";
|
|
52
|
+
import "../capabilities/index.js";
|
|
50
53
|
//#region src/agent/tools/factory.ts
|
|
51
54
|
init_logger();
|
|
52
55
|
const log = createLogger("AgentToolsFactory");
|
|
@@ -285,6 +288,20 @@ var AgentToolsFactory = class {
|
|
|
285
288
|
...optionalTools
|
|
286
289
|
], disabled);
|
|
287
290
|
}
|
|
291
|
+
createCapabilityTools(capabilityNames, options) {
|
|
292
|
+
const disabled = options?.disabledTools;
|
|
293
|
+
const raw = [];
|
|
294
|
+
const toolNames = /* @__PURE__ */ new Set();
|
|
295
|
+
for (const toolName of getAgentCapabilityToolNames(capabilityNames)) {
|
|
296
|
+
if (disabled?.has(toolName) || toolNames.has(toolName)) continue;
|
|
297
|
+
toolNames.add(toolName);
|
|
298
|
+
if (toolName === "create_desktop_pet") raw.push(createDesktopPetTool());
|
|
299
|
+
}
|
|
300
|
+
return wrapToolsWithProtection(raw, this.deps.toolExecutorConfig);
|
|
301
|
+
}
|
|
302
|
+
getLazyCapabilityToolNames() {
|
|
303
|
+
return ["create_desktop_pet"];
|
|
304
|
+
}
|
|
288
305
|
createAllTools(coreOptions) {
|
|
289
306
|
const coreTools = this.createCoreTools(coreOptions);
|
|
290
307
|
const disableExtensions = coreOptions?.disabledTools?.has("extensions");
|