@xopcai/xopc 0.0.117 → 0.0.119
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -24
- package/README.zh-CN.md +46 -42
- 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-BBLhJ2C6.js → agent-avatar-dicebear-CU8J6zou.js} +1 -1
- package/dist/gateway/static/root/assets/agents-B0hkpLhP.js +2 -0
- package/dist/gateway/static/root/assets/app-management-settings-panel-B9vfYoEr.js +1 -0
- package/dist/gateway/static/root/assets/appearance-settings-ClOzIO24.js +1 -0
- package/dist/gateway/static/root/assets/{apps-page-DdDx0V4z.js → apps-page-DU0dz9Zo.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-DZsVqMhU.js → archive-plugin-BkVIgZzA.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-CIol6JWX.js → attachment-load-CaCMWh1D.js} +1 -1
- package/dist/gateway/static/root/assets/automation-suggestion-card-DhGDpKP3.js +1 -0
- package/dist/gateway/static/root/assets/automations-page-CTedroVo.js +1 -0
- package/dist/gateway/static/root/assets/{binary-plugins-B3UauMfp.js → binary-plugins-B4TO_bsn.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-2HJoFchH.js → block-editor-DJRWwWNI.js} +6 -6
- package/dist/gateway/static/root/assets/browser-settings-page-FCDwOOBK.js +8 -0
- package/dist/gateway/static/root/assets/capability-presets-api-Befi89CL.js +1 -0
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-_G8eb2vJ.js +1 -0
- package/dist/gateway/static/root/assets/channel-recipient-api-BNSuJb5f.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-C3d5JBJX.js +1 -0
- package/dist/gateway/static/root/assets/cn-BQ_8x0bT.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-yaUNvt5u.js +2 -0
- package/dist/gateway/static/root/assets/cron-expression-B3B1eqnr.js +2 -0
- package/dist/gateway/static/root/assets/dreaming-settings-DNrdg1Ve.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-DX5WRE_t.js → extension-debug-page-DYh402XV.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-Buah7Bw-.js → extension-page-Cr_TizrN.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DuroLVnf.js → extension-settings-page-BU1KoKRj.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-B5zdl27C.js → fetch-Y7AEX4H3.js} +1 -1
- package/dist/gateway/static/root/assets/format-cron-label-arqgGlVV.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-config-api-gT4ZhE7h.js → gateway-config-api-RbMmAOpX.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-C79wkXef.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-B3kL8KGY.js → gateway-startup-retry-DiD_Tx4N.js} +1 -1
- package/dist/gateway/static/root/assets/goal-detail-page-9paXuHdo.js +1 -0
- package/dist/gateway/static/root/assets/{goals-config-api-AR-fVh0P.js → goals-config-api-DqxVi0p5.js} +1 -1
- package/dist/gateway/static/root/assets/{goals-page-Cpb21ezg.js → goals-page-CS0R-Af2.js} +2 -2
- package/dist/gateway/static/root/assets/goals-settings-CSuzZ8Ae.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-DXPaYJm3.js +1 -0
- package/dist/gateway/static/root/assets/index-BoKafJIZ.js +191 -0
- package/dist/gateway/static/root/assets/index-CArhCcYn.css +1 -0
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-Dvg53qnd.js +1 -0
- package/dist/gateway/static/root/assets/{logs-page-DefCgF4o.js → logs-page-DLXPcksQ.js} +2 -2
- package/dist/gateway/static/root/assets/{markdown-split-ChbR9LGO.js → markdown-split-CpHxbtWJ.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-B82oP80n.js → media-plugins-SFPQRSxq.js} +1 -1
- package/dist/gateway/static/root/assets/messages-CrDHEbss.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-BDF2iyRk.js +2 -0
- package/dist/gateway/static/root/assets/note-detail-page-CSb_hwig.js +2 -0
- package/dist/gateway/static/root/assets/{note-image-lightbox-CDknITM3.js → note-image-lightbox-BUYURRgn.js} +1 -1
- package/dist/gateway/static/root/assets/notes-page-BsIBSFyZ.js +1 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-DV3xQsWB.js → preview-open-alternatives-BHz9iWYA.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CwzSovcS.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-BODCXnvY.js → save-bar-controls-CefkOlRg.js} +1 -1
- package/dist/gateway/static/root/assets/schedule-field-CmTRoOcE.js +1 -0
- package/dist/gateway/static/root/assets/{schema-form-qtfEI6QM.js → schema-form-FMKv1vGu.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-PHmX-NmA.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BRAeKJN4.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-BHZKjVcS.js → settings-collapsible-section-BouO6JTi.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-5i35D-J1.js → settings-form-section-D-4lBl6X.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-KKrbFjy8.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-layout-BJ4Gsef5.js +1 -0
- package/dist/gateway/static/root/assets/setup-status-panel-CrUjHPQo.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-B0H63tDv.js → share-preview-page-BDy2btRN.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-B18OBmH5.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-C28-2uKQ.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-Bm2ebkwl.js +2 -0
- package/dist/gateway/static/root/assets/system-settings-panel-CRngrlnZ.js +1 -0
- package/dist/gateway/static/root/assets/typed-models-lib-BltBc0b-.js +1 -0
- package/dist/gateway/static/root/assets/{use-channel-catalog-BCjP_VHb.js → use-channel-catalog-CjN-51e6.js} +1 -1
- package/dist/gateway/static/root/assets/user-profile-settings-panel-DJ1dKwZy.js +1 -0
- package/dist/gateway/static/root/assets/utils-B5M5-YvK.js +1 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-CeEevm7z.js +1 -0
- package/dist/gateway/static/root/assets/{voice-player-store-BlF9V05n.js → voice-player-store-B7ykUlHM.js} +1 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-C4PzUQjB.js +3 -0
- package/dist/gateway/static/root/assets/workflows-page-BL84_-Ky.js +18 -0
- package/dist/gateway/static/root/index.html +6 -7
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +4 -5
- package/dist/src/agent/agent-manager.js +6 -9
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/agent-scope.js +0 -2
- package/dist/src/agent/agent-scope.js.map +1 -1
- package/dist/src/agent/background-review/prompts.d.ts +2 -2
- package/dist/src/agent/background-review/prompts.js +2 -2
- package/dist/src/agent/background-review/prompts.js.map +1 -1
- package/dist/src/agent/background-review/settings.d.ts +1 -1
- package/dist/src/agent/background-review/settings.js +7 -8
- package/dist/src/agent/background-review/settings.js.map +1 -1
- package/dist/src/agent/bootstrap/bootstrap-cache.d.ts +1 -0
- package/dist/src/agent/bootstrap/bootstrap-cache.js +4 -3
- package/dist/src/agent/bootstrap/bootstrap-cache.js.map +1 -1
- package/dist/src/agent/bootstrap/bootstrap-files.d.ts +4 -0
- package/dist/src/agent/bootstrap/bootstrap-files.js +4 -3
- package/dist/src/agent/bootstrap/bootstrap-files.js.map +1 -1
- package/dist/src/agent/bootstrap/filter-bootstrap-files.js +4 -2
- package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
- package/dist/src/agent/bootstrap/load-bootstrap-files.d.ts +2 -1
- package/dist/src/agent/bootstrap/load-bootstrap-files.js +17 -16
- package/dist/src/agent/bootstrap/load-bootstrap-files.js.map +1 -1
- package/dist/src/agent/bootstrap/types.d.ts +1 -1
- package/dist/src/agent/context/workspace-templates/AGENTS.md +1 -1
- package/dist/src/agent/context/workspace.d.ts +4 -2
- package/dist/src/agent/context/workspace.js +5 -4
- package/dist/src/agent/context/workspace.js.map +1 -1
- package/dist/src/agent/embedded/run-for-session.js +106 -15
- package/dist/src/agent/embedded/run-for-session.js.map +1 -1
- package/dist/src/agent/fallback/candidates.d.ts +0 -8
- package/dist/src/agent/fallback/candidates.js +10 -11
- package/dist/src/agent/fallback/candidates.js.map +1 -1
- package/dist/src/agent/index.js +1 -1
- package/dist/src/agent/lifecycle/handlers/compaction.d.ts +1 -3
- package/dist/src/agent/lifecycle/handlers/compaction.js +1 -16
- package/dist/src/agent/lifecycle/handlers/compaction.js.map +1 -1
- package/dist/src/agent/lifecycle/progress.d.ts +0 -1
- package/dist/src/agent/lifecycle/progress.js +1 -6
- package/dist/src/agent/lifecycle/progress.js.map +1 -1
- package/dist/src/agent/lifecycle/timeout-wrapper.d.ts +1 -2
- package/dist/src/agent/lifecycle/timeout-wrapper.js +1 -7
- package/dist/src/agent/lifecycle/timeout-wrapper.js.map +1 -1
- package/dist/src/agent/memory/builtin-memory-store.d.ts +4 -2
- package/dist/src/agent/memory/builtin-memory-store.js +11 -6
- package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
- package/dist/src/agent/memory/builtin-provider.js +36 -7
- package/dist/src/agent/memory/builtin-provider.js.map +1 -1
- package/dist/src/agent/memory/candidate-extractor.d.ts +6 -0
- package/dist/src/agent/memory/candidate-extractor.js +60 -0
- package/dist/src/agent/memory/candidate-extractor.js.map +1 -0
- package/dist/src/agent/memory/manager.d.ts +2 -1
- package/dist/src/agent/memory/manager.js +27 -3
- package/dist/src/agent/memory/manager.js.map +1 -1
- package/dist/src/agent/memory/memory-config.js +3 -0
- package/dist/src/agent/memory/memory-config.js.map +1 -1
- package/dist/src/agent/memory/prefetch-coordinator.js.map +1 -1
- package/dist/src/agent/memory/types.d.ts +27 -4
- package/dist/src/agent/middleware/self-verify.d.ts +0 -1
- package/dist/src/agent/middleware/self-verify.js +0 -2
- package/dist/src/agent/middleware/self-verify.js.map +1 -1
- package/dist/src/agent/models/index.d.ts +1 -1
- package/dist/src/agent/models/manager.d.ts +4 -20
- package/dist/src/agent/models/manager.js +9 -126
- package/dist/src/agent/models/manager.js.map +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.d.ts +0 -4
- package/dist/src/agent/orchestration/agent-orchestrator.js +1 -9
- package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
- package/dist/src/agent/prompt/memory/index.d.ts +4 -2
- package/dist/src/agent/prompt/memory/index.js +18 -12
- package/dist/src/agent/prompt/memory/index.js.map +1 -1
- package/dist/src/agent/prompt/sections/memory-skills.js +1 -1
- package/dist/src/agent/prompt/sections/memory-skills.js.map +1 -1
- package/dist/src/agent/prompt/sections/tooling.js +2 -2
- package/dist/src/agent/prompt/sections/tooling.js.map +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.d.ts +0 -1
- package/dist/src/agent/prompt/service-prompt-builder.js +4 -31
- package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
- package/dist/src/agent/prompt/subagent-context.js +1 -1
- package/dist/src/agent/prompt/subagent-context.js.map +1 -1
- package/dist/src/agent/prompt/types.d.ts +1 -1
- package/dist/src/agent/reply/post-compaction-context.js +1 -3
- package/dist/src/agent/reply/post-compaction-context.js.map +1 -1
- package/dist/src/agent/reply/startup-context.js +1 -5
- package/dist/src/agent/reply/startup-context.js.map +1 -1
- package/dist/src/agent/service.d.ts +4 -12
- package/dist/src/agent/service.js +10 -48
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/service.types.d.ts +2 -2
- package/dist/src/agent/session/session-config-service.d.ts +11 -11
- package/dist/src/agent/session/session-config-service.js +14 -14
- package/dist/src/agent/session/session-config-service.js.map +1 -1
- package/dist/src/agent/session/session-inspector.js +3 -2
- package/dist/src/agent/session/session-inspector.js.map +1 -1
- package/dist/src/agent/starter-agents.js +12 -12
- package/dist/src/agent/starter-agents.js.map +1 -1
- package/dist/src/agent/tools/automation-tool.d.ts +14 -0
- package/dist/src/agent/tools/automation-tool.js +102 -0
- package/dist/src/agent/tools/automation-tool.js.map +1 -0
- package/dist/src/agent/tools/curated-memory-tool.js +57 -2
- package/dist/src/agent/tools/curated-memory-tool.js.map +1 -1
- package/dist/src/agent/tools/delegate-tool.js +1 -1
- package/dist/src/agent/tools/delegate-tool.js.map +1 -1
- package/dist/src/agent/tools/factory.d.ts +3 -3
- package/dist/src/agent/tools/factory.js +2 -2
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/index.d.ts +1 -1
- package/dist/src/agent/tools/index.js +2 -2
- package/dist/src/agent/tools/web-extract.js +6 -8
- package/dist/src/agent/tools/web-extract.js.map +1 -1
- package/dist/src/agent-manifest/index.js +2 -2
- package/dist/src/agent-manifest/resolver.d.ts +4 -2
- package/dist/src/agent-manifest/resolver.js +6 -3
- package/dist/src/agent-manifest/resolver.js.map +1 -1
- package/dist/src/agent-manifest/schema.d.ts +238 -0
- package/dist/src/agent-manifest/schema.js +16 -5
- package/dist/src/agent-manifest/schema.js.map +1 -1
- package/dist/src/agent-manifest/validator.d.ts +3 -2
- package/dist/src/agent-manifest/validator.js +2 -1
- package/dist/src/agent-manifest/validator.js.map +1 -1
- package/dist/src/ai-assist/text-assist.d.ts +1 -1
- package/dist/src/ai-assist/text-assist.js +56 -0
- package/dist/src/ai-assist/text-assist.js.map +1 -1
- package/dist/src/auth/profiles/store.js.map +1 -1
- package/dist/src/automations/api/routes.d.ts +3 -0
- package/dist/src/automations/api/routes.js +193 -0
- package/dist/src/automations/api/routes.js.map +1 -0
- package/dist/src/automations/domain/index.d.ts +3 -0
- package/dist/src/automations/domain/index.js +3 -0
- package/dist/src/automations/domain/schedule.d.ts +4 -0
- package/dist/src/automations/domain/schedule.js +37 -0
- package/dist/src/automations/domain/schedule.js.map +1 -0
- package/dist/src/automations/domain/types.d.ts +160 -0
- package/dist/src/automations/domain/validation.d.ts +370 -0
- package/dist/src/automations/domain/validation.js +149 -0
- package/dist/src/automations/domain/validation.js.map +1 -0
- package/dist/src/automations/draft/automation-draft-prompt.d.ts +26 -0
- package/dist/src/automations/draft/automation-draft-prompt.js +127 -0
- package/dist/src/automations/draft/automation-draft-prompt.js.map +1 -0
- package/dist/src/automations/draft/automation-draft-service.d.ts +12 -0
- package/dist/src/automations/draft/automation-draft-service.js +135 -0
- package/dist/src/automations/draft/automation-draft-service.js.map +1 -0
- package/dist/src/automations/draft/automation-draft-validator.d.ts +99 -0
- package/dist/src/automations/draft/automation-draft-validator.js +105 -0
- package/dist/src/automations/draft/automation-draft-validator.js.map +1 -0
- package/dist/src/automations/draft/automation-draft.types.d.ts +40 -0
- package/dist/src/automations/draft/index.d.ts +3 -0
- package/dist/src/automations/draft/index.js +3 -0
- package/dist/src/automations/index.d.ts +3 -0
- package/dist/src/automations/index.js +8 -0
- package/dist/src/automations/product-events.d.ts +5 -0
- package/dist/src/automations/product-events.js +21 -0
- package/dist/src/automations/product-events.js.map +1 -0
- package/dist/src/automations/service/action-executor.d.ts +9 -0
- package/dist/src/automations/service/action-executor.js +157 -0
- package/dist/src/automations/service/action-executor.js.map +1 -0
- package/dist/src/automations/service/automation-service.d.ts +54 -0
- package/dist/src/automations/service/automation-service.js +404 -0
- package/dist/src/automations/service/automation-service.js.map +1 -0
- package/dist/src/automations/service/index.d.ts +2 -0
- package/dist/src/automations/service/index.js +3 -0
- package/dist/src/automations/storage/automation-repository.d.ts +6 -0
- package/dist/src/automations/storage/automation-repository.js +61 -0
- package/dist/src/automations/storage/automation-repository.js.map +1 -0
- package/dist/src/automations/storage/automation-run-repository.d.ts +17 -0
- package/dist/src/automations/storage/automation-run-repository.js +130 -0
- package/dist/src/automations/storage/automation-run-repository.js.map +1 -0
- package/dist/src/automations/storage/index.d.ts +2 -0
- package/dist/src/automations/storage/index.js +3 -0
- package/dist/src/channels/pipeline.d.ts +1 -1
- package/dist/src/channels/pipeline.js.map +1 -1
- package/dist/src/channels/plugin-types.d.ts +3 -0
- package/dist/src/chat-commands/agent-edit.js +0 -1
- package/dist/src/chat-commands/agent-edit.js.map +1 -1
- package/dist/src/chat-commands/builtins/model.js +2 -2
- package/dist/src/chat-commands/builtins/model.js.map +1 -1
- package/dist/src/chat-commands/builtins/thinking.js.map +1 -1
- package/dist/src/cli/command-catalog.js +0 -39
- package/dist/src/cli/command-catalog.js.map +1 -1
- package/dist/src/cli/command-loaders.js +0 -1
- package/dist/src/cli/command-loaders.js.map +1 -1
- package/dist/src/cli/command-manifest.d.ts +1 -1
- package/dist/src/cli/command-manifest.js +1 -5
- package/dist/src/cli/command-manifest.js.map +1 -1
- package/dist/src/cli/commands/agents.js +7 -6
- package/dist/src/cli/commands/agents.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/doctor/checks/{cron-health.d.ts → migrations.d.ts} +1 -1
- package/dist/src/cli/commands/doctor/checks/migrations.js +40 -0
- package/dist/src/cli/commands/doctor/checks/migrations.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -0
- package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +0 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -1
- package/dist/src/cli/commands/doctor/flow.js +2 -2
- package/dist/src/cli/commands/doctor/flow.js.map +1 -1
- package/dist/src/cli/commands/gateway/call.js +2 -2
- package/dist/src/cli/commands/gateway/call.js.map +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/gateway/run-foreground.js +4 -2
- package/dist/src/cli/commands/gateway/run-foreground.js.map +1 -1
- package/dist/src/cli/commands/init.js +33 -14
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/models.js +13 -31
- package/dist/src/cli/commands/models.js.map +1 -1
- package/dist/src/cli/commands/onboard/model.js +17 -10
- package/dist/src/cli/commands/onboard/model.js.map +1 -1
- package/dist/src/cli/commands/onboard.js +2 -2
- package/dist/src/cli/commands/onboard.js.map +1 -1
- package/dist/src/cli/commands/tui.js +26 -4
- package/dist/src/cli/commands/tui.js.map +1 -1
- package/dist/src/cli/index.d.ts +1 -0
- package/dist/src/cli/index.js +10 -5
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/registry.js +1 -1
- package/dist/src/cli/registry.js.map +1 -1
- package/dist/src/cli/templates.d.ts +1 -1
- package/dist/src/cli/templates.js +1 -11
- package/dist/src/cli/templates.js.map +1 -1
- package/dist/src/cli/utils/init-workspace-core.js +2 -0
- package/dist/src/cli/utils/init-workspace-core.js.map +1 -1
- package/dist/src/commands/agents.config.d.ts +9 -0
- package/dist/src/commands/agents.config.js +42 -12
- package/dist/src/commands/agents.config.js.map +1 -1
- package/dist/src/config/agent-profile.js +5 -3
- package/dist/src/config/agent-profile.js.map +1 -1
- package/dist/src/config/agent-typed-models.js +3 -5
- package/dist/src/config/agent-typed-models.js.map +1 -1
- package/dist/src/config/config-surface.d.ts +3 -0
- package/dist/src/config/index.d.ts +0 -1
- package/dist/src/config/index.js +3 -3
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/config/paths.d.ts +8 -10
- package/dist/src/config/paths.js +16 -16
- package/dist/src/config/paths.js.map +1 -1
- package/dist/src/config/profile.js +0 -1
- package/dist/src/config/profile.js.map +1 -1
- package/dist/src/config/rules.js +0 -5
- package/dist/src/config/rules.js.map +1 -1
- package/dist/src/config/schema.d.ts +45 -44
- package/dist/src/config/schema.js +68 -52
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/config/web-patch.d.ts +0 -12
- package/dist/src/config/web-patch.js +2 -29
- package/dist/src/config/web-patch.js.map +1 -1
- package/dist/src/extensions/api.d.ts +2 -0
- package/dist/src/extensions/api.js +10 -0
- package/dist/src/extensions/api.js.map +1 -1
- package/dist/src/extensions/extension-registry-impl.d.ts +4 -1
- package/dist/src/extensions/extension-registry-impl.js +8 -0
- package/dist/src/extensions/extension-registry-impl.js.map +1 -1
- package/dist/src/extensions/loader.d.ts +3 -3
- package/dist/src/extensions/loader.js +3 -3
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/sdk/testing.d.ts +2 -0
- package/dist/src/extensions/sdk/testing.js +7 -0
- package/dist/src/extensions/sdk/testing.js.map +1 -1
- package/dist/src/extensions/types/core.d.ts +10 -1
- package/dist/src/extensions/types/index.d.ts +1 -1
- package/dist/src/extensions/types/{phase4.d.ts → registration.d.ts} +2 -2
- package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
- package/dist/src/gateway/agent-builtin-tools.js +1 -1
- package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
- package/dist/src/gateway/agents-admin.d.ts +8 -0
- package/dist/src/gateway/agents-admin.js +63 -34
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/capability-presets-admin.d.ts +1 -0
- package/dist/src/gateway/capability-presets-admin.js +19 -10
- package/dist/src/gateway/capability-presets-admin.js.map +1 -1
- package/dist/src/gateway/chat-stream/mapper.js +9 -2
- package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
- package/dist/src/gateway/dreaming-automation-reconciler.d.ts +11 -0
- package/dist/src/gateway/dreaming-automation-reconciler.js +105 -0
- package/dist/src/gateway/dreaming-automation-reconciler.js.map +1 -0
- package/dist/src/gateway/global-defaults-admin.d.ts +36 -0
- package/dist/src/gateway/global-defaults-admin.js +146 -0
- package/dist/src/gateway/global-defaults-admin.js.map +1 -0
- package/dist/src/gateway/heartbeat/service.d.ts +0 -2
- package/dist/src/gateway/heartbeat/service.js +0 -7
- package/dist/src/gateway/heartbeat/service.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +5 -6
- package/dist/src/gateway/hono/lib/config-payload.js +2 -2
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/routes/agents.js +33 -1
- package/dist/src/gateway/hono/routes/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/commands-skills.js +4 -5
- package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.d.ts +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +12 -6
- package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
- package/dist/src/gateway/hono/routes/dreaming.js +27 -37
- package/dist/src/gateway/hono/routes/dreaming.js.map +1 -1
- package/dist/src/gateway/hono/routes/global-defaults.d.ts +3 -0
- package/dist/src/gateway/hono/routes/global-defaults.js +61 -0
- package/dist/src/gateway/hono/routes/global-defaults.js.map +1 -0
- package/dist/src/gateway/hono/routes/home.d.ts +2 -2
- package/dist/src/gateway/hono/routes/home.js +34 -25
- package/dist/src/gateway/hono/routes/home.js.map +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +21 -9
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/memory.js +75 -4
- package/dist/src/gateway/hono/routes/memory.js.map +1 -1
- package/dist/src/gateway/hono/routes/migrations.d.ts +3 -0
- package/dist/src/gateway/hono/routes/migrations.js +35 -0
- package/dist/src/gateway/hono/routes/migrations.js.map +1 -0
- package/dist/src/gateway/hono/routes/models.js +1 -1
- package/dist/src/gateway/hono/routes/public-gateway.js +2 -1
- package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
- package/dist/src/gateway/hono/routes/sessions.js +14 -0
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/run-loop.js +2 -0
- package/dist/src/gateway/run-loop.js.map +1 -1
- package/dist/src/gateway/security/operator-scopes.js +5 -1
- package/dist/src/gateway/security/operator-scopes.js.map +1 -1
- package/dist/src/gateway/service/config-coordinator.d.ts +4 -4
- package/dist/src/gateway/service/config-coordinator.js +14 -8
- package/dist/src/gateway/service/config-coordinator.js.map +1 -1
- package/dist/src/gateway/service/marketplace-service.d.ts +4 -4
- package/dist/src/gateway/service/marketplace-service.js +11 -7
- package/dist/src/gateway/service/marketplace-service.js.map +1 -1
- package/dist/src/gateway/service/sessions-api.d.ts +2 -1
- package/dist/src/gateway/service/sessions-api.js +6 -1
- package/dist/src/gateway/service/sessions-api.js.map +1 -1
- package/dist/src/gateway/service.d.ts +7 -3
- package/dist/src/gateway/service.js +61 -27
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/goals/goal-service.js +45 -4
- package/dist/src/goals/goal-service.js.map +1 -1
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/migrations/config/default-capability-preset.d.ts +10 -0
- package/dist/src/migrations/config/default-capability-preset.js +135 -0
- package/dist/src/migrations/config/default-capability-preset.js.map +1 -0
- package/dist/src/migrations/index.d.ts +4 -0
- package/dist/src/migrations/index.js +4 -0
- package/dist/src/migrations/registry.d.ts +8 -0
- package/dist/src/migrations/registry.js +18 -0
- package/dist/src/migrations/registry.js.map +1 -0
- package/dist/src/migrations/runner.d.ts +17 -0
- package/dist/src/migrations/runner.js +191 -0
- package/dist/src/migrations/runner.js.map +1 -0
- package/dist/src/migrations/types.d.ts +34 -0
- package/dist/src/notes/service.js +27 -2
- package/dist/src/notes/service.js.map +1 -1
- package/dist/src/providers/index.d.ts +4 -5
- package/dist/src/providers/index.js +16 -16
- package/dist/src/providers/index.js.map +1 -1
- package/dist/src/providers/model-registry.d.ts +0 -1
- package/dist/src/providers/model-registry.js +0 -3
- package/dist/src/providers/model-registry.js.map +1 -1
- package/dist/src/routing/resolve-tui-session-key.d.ts +3 -0
- package/dist/src/routing/resolve-tui-session-key.js +14 -3
- package/dist/src/routing/resolve-tui-session-key.js.map +1 -1
- package/dist/src/session/session-workspace.js +2 -7
- package/dist/src/session/session-workspace.js.map +1 -1
- package/dist/src/session/store.js +0 -5
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/session/transcript-outline.d.ts +47 -0
- package/dist/src/session/transcript-outline.js +481 -0
- package/dist/src/session/transcript-outline.js.map +1 -0
- package/dist/src/storage/sqlite/index.d.ts +1 -3
- package/dist/src/storage/sqlite/index.js +2 -4
- package/dist/src/storage/sqlite/memory-index-repository.d.ts +1 -0
- package/dist/src/storage/sqlite/memory-index-repository.js +15 -14
- package/dist/src/storage/sqlite/memory-index-repository.js.map +1 -1
- package/dist/src/storage/sqlite/memory-records-repository.d.ts +44 -1
- package/dist/src/storage/sqlite/memory-records-repository.js +164 -20
- package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/014_memory_record_governance.sql +10 -0
- package/dist/src/storage/sqlite/migrations/015_memory_trace_feedback.sql +4 -0
- package/dist/src/storage/sqlite/migrations/016_automations.sql +47 -0
- package/dist/src/storage/sqlite/migrations/017_automation_run_events.sql +14 -0
- package/dist/src/storage/sqlite/migrations/018_automation_safety.sql +1 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/storage/sqlite/schema.sql +56 -46
- package/dist/src/tui/backends/embedded-backend.d.ts +9 -0
- package/dist/src/tui/backends/embedded-backend.js +55 -27
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +4 -0
- package/dist/src/tui/backends/gateway-sse-backend.js +30 -3
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/components/tui-header.d.ts +0 -1
- package/dist/src/tui/components/tui-header.js +3 -8
- package/dist/src/tui/components/tui-header.js.map +1 -1
- package/dist/src/tui/tui-backend.d.ts +4 -0
- package/dist/src/tui/tui-commands.d.ts +5 -0
- package/dist/src/tui/tui-commands.js +43 -0
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/tui-startup-resources.js +2 -1
- package/dist/src/tui/tui-startup-resources.js.map +1 -1
- package/dist/src/tui/tui-startup-text.js +12 -12
- package/dist/src/tui/tui-startup-text.js.map +1 -1
- package/dist/src/tui/tui-transcript-tree.d.ts +1 -1
- package/dist/src/tui/tui-transcript-tree.js +17 -257
- package/dist/src/tui/tui-transcript-tree.js.map +1 -1
- package/dist/src/tui/tui-types.d.ts +2 -0
- package/dist/src/tui/tui-types.js.map +1 -1
- package/dist/src/tui/tui.js +11 -3
- package/dist/src/tui/tui.js.map +1 -1
- package/dist/src/workflows/domain/index.js +1 -1
- package/dist/src/workflows/domain/run.d.ts +7 -7
- package/dist/src/workflows/domain/run.js.map +1 -1
- package/dist/src/workflows/index.js +2 -2
- package/dist/src/workflows/service/workflow-run-service.d.ts +2 -0
- package/dist/src/workflows/service/workflow-run-service.js +31 -9
- package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
- package/dist/src/workflows/service/workflow-run-service.types.d.ts +1 -1
- package/package.json +4 -2
- package/dist/gateway/static/root/assets/agents-C6xgagZd.js +0 -71
- package/dist/gateway/static/root/assets/app-management-settings-panel-CBpLONvG.js +0 -1
- package/dist/gateway/static/root/assets/appearance-settings-CGcc54yj.js +0 -1
- package/dist/gateway/static/root/assets/browser-settings-page-CUOO-PQk.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-api-XQoMTI0A.js +0 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-CfqF-kGr.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-BYyRzsex.js +0 -1
- package/dist/gateway/static/root/assets/cn-JdwAhgNB.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-DIvEz4NX.js +0 -2
- package/dist/gateway/static/root/assets/createLucideIcon-DPHK1VkS.js +0 -1
- package/dist/gateway/static/root/assets/cron-api-R41aQCX9.js +0 -1
- package/dist/gateway/static/root/assets/cron-dreaming-jobs-nCMWCf0p.js +0 -1
- package/dist/gateway/static/root/assets/cron-page-D97nFqQD.js +0 -3
- package/dist/gateway/static/root/assets/dreaming-settings-Bceaw1PG.js +0 -1
- package/dist/gateway/static/root/assets/format-cron-label-BsotZDO5.js +0 -2
- package/dist/gateway/static/root/assets/format-interval-label-Dn_QKIqb.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-iISWAZRm.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-DCUROLMQ.js +0 -1
- package/dist/gateway/static/root/assets/goals-settings-CPAuFwoD.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-config-api-D7bKFEeO.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-CC69vNx0.js +0 -1
- package/dist/gateway/static/root/assets/index-CwhK8qCh.js +0 -122
- package/dist/gateway/static/root/assets/index-CyfhARL-.css +0 -1
- package/dist/gateway/static/root/assets/interval-presets-0DtsnxE0.js +0 -1
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-BPREtN8B.js +0 -1
- package/dist/gateway/static/root/assets/messages-Cf1O9JB0.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-DmKiIihT.js +0 -2
- package/dist/gateway/static/root/assets/note-detail-page-CxxfLRJZ.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-X2slSx7V.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BvmUWqgu.js +0 -1
- package/dist/gateway/static/root/assets/schedule-field-CgJSCD3B.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-B0yESuuL.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-q2M8RKOo.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-CGozabLD.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-FoOSe6nU.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-Cy5zWyYQ.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DaZNFro9.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-CnwtUzvw.js +0 -2
- package/dist/gateway/static/root/assets/system-settings-panel-DXs2VmBt.js +0 -1
- package/dist/gateway/static/root/assets/typed-models-lib-4gPuydz_.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-DvKiOglO.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-CAVZcONQ.js +0 -18
- package/dist/src/agent/context/workspace-templates/USER.md +0 -24
- package/dist/src/agent/fallback/error.d.ts +0 -22
- package/dist/src/agent/fallback/error.js +0 -32
- package/dist/src/agent/fallback/error.js.map +0 -1
- package/dist/src/agent/fallback/index.d.ts +0 -5
- package/dist/src/agent/fallback/index.js +0 -9
- package/dist/src/agent/fallback/index.js.map +0 -1
- package/dist/src/agent/orchestration/run-agent-turn-with-fallbacks.d.ts +0 -29
- package/dist/src/agent/orchestration/run-agent-turn-with-fallbacks.js +0 -76
- package/dist/src/agent/orchestration/run-agent-turn-with-fallbacks.js.map +0 -1
- package/dist/src/agent/service/reconcile-dreaming-cron.d.ts +0 -6
- package/dist/src/agent/service/reconcile-dreaming-cron.js +0 -84
- package/dist/src/agent/service/reconcile-dreaming-cron.js.map +0 -1
- package/dist/src/agent/tools/cronjob-tool.d.ts +0 -29
- package/dist/src/agent/tools/cronjob-tool.js +0 -274
- package/dist/src/agent/tools/cronjob-tool.js.map +0 -1
- package/dist/src/cli/commands/cron-cli.d.ts +0 -2
- package/dist/src/cli/commands/cron-cli.js +0 -15
- package/dist/src/cli/commands/cron-cli.js.map +0 -1
- package/dist/src/cli/commands/cron.d.ts +0 -4
- package/dist/src/cli/commands/cron.js +0 -188
- package/dist/src/cli/commands/cron.js.map +0 -1
- package/dist/src/cli/commands/doctor/checks/cron-health.js +0 -35
- package/dist/src/cli/commands/doctor/checks/cron-health.js.map +0 -1
- package/dist/src/cron/execution-types.d.ts +0 -42
- package/dist/src/cron/executor.d.ts +0 -67
- package/dist/src/cron/executor.js +0 -669
- package/dist/src/cron/executor.js.map +0 -1
- package/dist/src/cron/index.d.ts +0 -7
- package/dist/src/cron/index.js +0 -7
- package/dist/src/cron/job-content.d.ts +0 -5
- package/dist/src/cron/job-content.js +0 -15
- package/dist/src/cron/job-content.js.map +0 -1
- package/dist/src/cron/run-log-store.d.ts +0 -12
- package/dist/src/cron/run-log-store.js +0 -58
- package/dist/src/cron/run-log-store.js.map +0 -1
- package/dist/src/cron/schedule.d.ts +0 -10
- package/dist/src/cron/schedule.js +0 -85
- package/dist/src/cron/schedule.js.map +0 -1
- package/dist/src/cron/service.d.ts +0 -58
- package/dist/src/cron/service.js +0 -419
- package/dist/src/cron/service.js.map +0 -1
- package/dist/src/cron/types.d.ts +0 -183
- package/dist/src/cron/validation.d.ts +0 -500
- package/dist/src/cron/validation.js +0 -192
- package/dist/src/cron/validation.js.map +0 -1
- package/dist/src/cron/workflow-run-completion.d.ts +0 -23
- package/dist/src/cron/workflow-run-completion.js +0 -72
- package/dist/src/cron/workflow-run-completion.js.map +0 -1
- package/dist/src/extensions/types/phase4.js +0 -1
- package/dist/src/gateway/hono/routes/cron.d.ts +0 -3
- package/dist/src/gateway/hono/routes/cron.js +0 -109
- package/dist/src/gateway/hono/routes/cron.js.map +0 -1
- package/dist/src/storage/sqlite/cron-job-repository.d.ts +0 -6
- package/dist/src/storage/sqlite/cron-job-repository.js +0 -70
- package/dist/src/storage/sqlite/cron-job-repository.js.map +0 -1
- package/dist/src/storage/sqlite/cron-run-repository.d.ts +0 -5
- package/dist/src/storage/sqlite/cron-run-repository.js +0 -96
- package/dist/src/storage/sqlite/cron-run-repository.js.map +0 -1
- /package/dist/src/{cron → automations/domain}/types.js +0 -0
- /package/dist/src/{config/defaults.d.ts → automations/draft/automation-draft.types.js} +0 -0
- /package/dist/src/{config/defaults.js → extensions/types/registration.js} +0 -0
- /package/dist/src/{cron/execution-types.js → migrations/types.js} +0 -0
|
@@ -3,21 +3,9 @@ import { init_logger } from "../../../utils/logger.js";
|
|
|
3
3
|
//#region src/agent/lifecycle/handlers/compaction.ts
|
|
4
4
|
init_logger();
|
|
5
5
|
const logger = createLogger("Agent:LifecycleCompaction");
|
|
6
|
-
const DEFAULT_CONFIG = {
|
|
7
|
-
minMessages: 20,
|
|
8
|
-
maxTokens: 8e3,
|
|
9
|
-
preserveReasoning: true,
|
|
10
|
-
accumulateUsage: true
|
|
11
|
-
};
|
|
12
6
|
var CompactionLifecycleHandler = class {
|
|
13
7
|
name = "CompactionLifecycleHandler";
|
|
14
|
-
|
|
15
|
-
constructor(config) {
|
|
16
|
-
this.config = {
|
|
17
|
-
...DEFAULT_CONFIG,
|
|
18
|
-
...config
|
|
19
|
-
};
|
|
20
|
-
}
|
|
8
|
+
constructor(_config) {}
|
|
21
9
|
async handle(event, _context) {
|
|
22
10
|
const { sessionKey } = event;
|
|
23
11
|
logger.debug({
|
|
@@ -25,9 +13,6 @@ var CompactionLifecycleHandler = class {
|
|
|
25
13
|
messageCount: event.payload?.usage?.total
|
|
26
14
|
}, "Compaction lifecycle event received");
|
|
27
15
|
}
|
|
28
|
-
shouldCompact(messageCount) {
|
|
29
|
-
return messageCount >= this.config.minMessages;
|
|
30
|
-
}
|
|
31
16
|
};
|
|
32
17
|
//#endregion
|
|
33
18
|
export { CompactionLifecycleHandler };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction.js","names":[],"sources":["../../../../../src/agent/lifecycle/handlers/compaction.ts"],"sourcesContent":["import { createLogger } from '../../../utils/logger.js';\nimport type { AgentContext } from '../../service.types.js';\nimport type {\n LifecycleHandler,\n LifecycleEventData,\n LLMResponsePayload,\n} from '../types.js';\n\nconst logger = createLogger('Agent:LifecycleCompaction');\n\nexport interface CompactionHandlerConfig {\n minMessages: number;\n maxTokens: number;\n preserveReasoning: boolean;\n accumulateUsage: boolean;\n}\n\
|
|
1
|
+
{"version":3,"file":"compaction.js","names":[],"sources":["../../../../../src/agent/lifecycle/handlers/compaction.ts"],"sourcesContent":["import { createLogger } from '../../../utils/logger.js';\nimport type { AgentContext } from '../../service.types.js';\nimport type {\n LifecycleHandler,\n LifecycleEventData,\n LLMResponsePayload,\n} from '../types.js';\n\nconst logger = createLogger('Agent:LifecycleCompaction');\n\nexport interface CompactionHandlerConfig {\n minMessages: number;\n maxTokens: number;\n preserveReasoning: boolean;\n accumulateUsage: boolean;\n}\n\nexport class CompactionLifecycleHandler\n implements LifecycleHandler<LLMResponsePayload>\n{\n readonly name = 'CompactionLifecycleHandler';\n\n constructor(_config?: Partial<CompactionHandlerConfig>) {}\n\n async handle(\n event: LifecycleEventData<LLMResponsePayload>,\n _context: AgentContext\n ): Promise<void> {\n const { sessionKey } = event;\n\n // Compaction logic is handled by SessionStore, this handler just logs for now\n logger.debug({ sessionKey, messageCount: event.payload?.usage?.total }, 'Compaction lifecycle event received');\n }\n\n}\n"],"mappings":";;;aAAwD;AAQxD,MAAM,SAAS,aAAa,4BAA4B;AASxD,IAAa,6BAAb,MAEA;CACE,OAAgB;CAEhB,YAAY,SAA4C;CAExD,MAAM,OACJ,OACA,UACe;EACf,MAAM,EAAE,eAAe;AAGvB,SAAO,MAAM;GAAE;GAAY,cAAc,MAAM,SAAS,OAAO;GAAO,EAAE,sCAAsC"}
|
|
@@ -48,7 +48,6 @@ export declare class ProgressFeedbackManager {
|
|
|
48
48
|
private currentStage;
|
|
49
49
|
private currentTool;
|
|
50
50
|
private taskStartTime;
|
|
51
|
-
private lastHeartbeatTime;
|
|
52
51
|
private heartbeatTimer?;
|
|
53
52
|
private streamActive;
|
|
54
53
|
constructor(config?: Partial<ProgressFeedbackConfig>);
|
|
@@ -86,7 +86,6 @@ var ProgressFeedbackManager = class {
|
|
|
86
86
|
currentStage = "idle";
|
|
87
87
|
currentTool = null;
|
|
88
88
|
taskStartTime = 0;
|
|
89
|
-
lastHeartbeatTime = 0;
|
|
90
89
|
heartbeatTimer;
|
|
91
90
|
streamActive = false;
|
|
92
91
|
constructor(config = {}) {
|
|
@@ -103,7 +102,6 @@ var ProgressFeedbackManager = class {
|
|
|
103
102
|
}
|
|
104
103
|
startTask() {
|
|
105
104
|
this.taskStartTime = Date.now();
|
|
106
|
-
this.lastHeartbeatTime = Date.now();
|
|
107
105
|
if (this.config.heartbeatEnabled) this.startHeartbeat();
|
|
108
106
|
}
|
|
109
107
|
endTask() {
|
|
@@ -117,10 +115,7 @@ var ProgressFeedbackManager = class {
|
|
|
117
115
|
this.stopHeartbeat();
|
|
118
116
|
this.heartbeatTimer = setInterval(() => {
|
|
119
117
|
const elapsed = Date.now() - this.taskStartTime;
|
|
120
|
-
if (elapsed >= this.config.longTaskThresholdMs)
|
|
121
|
-
this.callbacks.onHeartbeat?.(elapsed, this.currentStage);
|
|
122
|
-
this.lastHeartbeatTime = Date.now();
|
|
123
|
-
}
|
|
118
|
+
if (elapsed >= this.config.longTaskThresholdMs) this.callbacks.onHeartbeat?.(elapsed, this.currentStage);
|
|
124
119
|
}, this.config.heartbeatIntervalMs);
|
|
125
120
|
}
|
|
126
121
|
stopHeartbeat() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.js","names":[],"sources":["../../../../src/agent/lifecycle/progress.ts"],"sourcesContent":["/**\n * Progress Feedback Manager\n * \n * Manages real-time progress feedback for long-running agent tasks.\n * Provides:\n * - Tool/end notifications execution start\n * - Thinking indicators\n * - Progress stage updates\n * - Stream integration for real-time display\n */\n\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('ProgressFeedback');\n\n// Progress stages for different operations\nexport type ProgressStage = \n | 'thinking' // AI is thinking/reasoning\n | 'searching' // Web search\n | 'reading' // Reading files\n | 'writing' // Writing files \n | 'executing' // Running shell commands\n | 'analyzing' // Analyzing data\n | 'idle'; // No active operation\n\nexport interface ProgressUpdate {\n stage: ProgressStage;\n detail?: string;\n toolName?: string;\n toolArgs?: Record<string, unknown>;\n timestamp: number;\n}\n\nexport interface ProgressFeedbackConfig {\n // Feedback level: minimal (errors only), normal (key events), verbose (all updates)\n level: 'minimal' | 'normal' | 'verbose';\n // Whether to show thinking indicators\n showThinking: boolean;\n // Whether to stream tool progress updates\n streamToolProgress: boolean;\n // Whether to send periodic heartbeats for long tasks\n heartbeatEnabled: boolean;\n // Heartbeat interval in ms\n heartbeatIntervalMs: number;\n // Threshold for long-running task in ms\n longTaskThresholdMs: number;\n // Enhanced feedback options\n showErrorAccumulation: boolean; // Show tool error accumulation (default: true)\n showRequestLimitWarning: boolean; // Show request limit warnings (default: true)\n}\n\nconst DEFAULT_CONFIG: ProgressFeedbackConfig = {\n level: 'normal',\n showThinking: true,\n streamToolProgress: true,\n heartbeatEnabled: true,\n heartbeatIntervalMs: 20000, // 20 seconds\n longTaskThresholdMs: 30000, // 30 seconds\n showErrorAccumulation: true,\n showRequestLimitWarning: true,\n};\n\n// Tool category mapping for better feedback\nconst TOOL_STAGE_MAP: Record<string, ProgressStage> = {\n // Reading tools\n 'read': 'reading',\n 'read_file': 'reading',\n 'read_multiple': 'reading',\n 'glob': 'reading',\n 'find': 'reading',\n 'list_dir': 'reading',\n 'ls': 'reading',\n 'memory_get': 'reading',\n 'curated_memory': 'writing',\n\n // Searching tools\n 'grep': 'searching',\n 'web_search': 'searching',\n 'search': 'searching',\n 'memory_search': 'searching',\n 'session_search': 'searching',\n 'find_files': 'searching',\n \n // Web fetching\n 'web_fetch': 'reading',\n 'fetch': 'reading',\n 'curl': 'reading',\n 'wget': 'reading',\n \n // Writing tools\n 'write': 'writing',\n 'write_file': 'writing',\n 'write_multiple': 'writing',\n 'edit': 'writing',\n 'patch': 'writing',\n 'create': 'writing',\n 'mkdir': 'writing',\n 'delete': 'writing',\n 'remove': 'writing',\n \n // Executing tools\n 'bash': 'executing',\n 'shell': 'executing',\n 'exec': 'executing',\n 'run': 'executing',\n 'command': 'executing',\n 'node': 'executing',\n 'python': 'executing',\n 'npm': 'executing',\n 'pnpm': 'executing',\n 'git': 'executing',\n 'docker': 'executing',\n 'kubectl': 'executing',\n \n // Message/communication\n 'message': 'idle',\n 'send': 'idle',\n 'reply': 'idle',\n \n // Analysis tools\n 'analyze': 'analyzing',\n 'compare': 'analyzing',\n 'diff': 'analyzing',\n 'summarize': 'analyzing',\n};\n\n// Emoji mapping for stages\nconst STAGE_EMOJI: Record<ProgressStage, string> = {\n 'thinking': '🤔',\n 'searching': '🔍',\n 'reading': '📖',\n 'writing': '✍️',\n 'executing': '⚙️',\n 'analyzing': '📊',\n 'idle': '💬',\n};\n\n// Stage labels in English\nconst STAGE_LABEL: Record<ProgressStage, string> = {\n 'thinking': 'Thinking',\n 'searching': 'Searching',\n 'reading': 'Reading',\n 'writing': 'Writing',\n 'executing': 'Executing',\n 'analyzing': 'Analyzing',\n 'idle': 'Ready',\n};\n\nexport interface ProgressMessage {\n type: 'start' | 'update' | 'complete' | 'error' | 'warning' | 'thinking';\n stage: ProgressStage;\n message: string;\n detail?: string;\n toolName?: string;\n toolArgs?: Record<string, unknown>;\n}\n\nexport interface ProgressCallbacks {\n onProgress?: (msg: ProgressMessage) => void;\n onStreamStart?: (toolName: string, toolArgs: Record<string, unknown>) => void;\n onStreamUpdate?: (toolName: string, partialResult: string) => void;\n onStreamEnd?: (toolName: string, result: string, isError: boolean) => void;\n onThinking?: (thinking: string) => void;\n onHeartbeat?: (elapsedMs: number, currentStage: ProgressStage) => void;\n}\n\nexport class ProgressFeedbackManager {\n private config: ProgressFeedbackConfig;\n private callbacks: ProgressCallbacks = {};\n private currentStage: ProgressStage = 'idle';\n private currentTool: string | null = null;\n private taskStartTime: number = 0;\n private lastHeartbeatTime: number = 0;\n private heartbeatTimer?: ReturnType<typeof setInterval>;\n private streamActive: boolean = false;\n\n constructor(config: Partial<ProgressFeedbackConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n setCallbacks(callbacks: ProgressCallbacks): void {\n this.callbacks = { ...this.callbacks, ...callbacks };\n }\n\n startTask(): void {\n this.taskStartTime = Date.now();\n this.lastHeartbeatTime = Date.now();\n \n if (this.config.heartbeatEnabled) {\n this.startHeartbeat();\n }\n }\n\n endTask(): void {\n this.stopHeartbeat();\n this.currentStage = 'idle';\n this.currentTool = null;\n this.taskStartTime = 0;\n this.streamActive = false;\n }\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n this.heartbeatTimer = setInterval(() => {\n const elapsed = Date.now() - this.taskStartTime;\n if (elapsed >= this.config.longTaskThresholdMs) {\n this.callbacks.onHeartbeat?.(elapsed, this.currentStage);\n this.lastHeartbeatTime = Date.now();\n }\n }, this.config.heartbeatIntervalMs);\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = undefined;\n }\n }\n\n private mapToolToStage(toolName: string): ProgressStage {\n const normalizedName = toolName.toLowerCase();\n const normalizedNoUnderscore = normalizedName.replace(/_/g, '');\n \n // Try both versions: with and without underscores\n const toCheck = [normalizedName, normalizedNoUnderscore];\n \n // Sort keys by length (longest first) to match more specific keys first\n const sortedKeys = Object.keys(TOOL_STAGE_MAP).sort((a, b) => b.length - a.length);\n \n for (const name of toCheck) {\n for (const key of sortedKeys) {\n if (name.includes(key)) {\n return TOOL_STAGE_MAP[key];\n }\n }\n }\n return 'executing';\n }\n\n private formatToolName(toolName: string): string {\n // Convert snake_case to Title Case\n return toolName\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, c => c.toUpperCase());\n }\n\n // Handle tool execution start\n onToolStart(toolName: string, toolArgs: Record<string, unknown> = {}): void {\n if (this.config.level === 'minimal') return;\n\n this.currentTool = toolName;\n this.currentStage = this.mapToolToStage(toolName);\n this.streamActive = true;\n\n const formattedName = this.formatToolName(toolName);\n const emoji = STAGE_EMOJI[this.currentStage];\n const label = STAGE_LABEL[this.currentStage];\n\n // Build detail message\n let detail = '';\n if (toolArgs.path) {\n detail = `📁 ${toolArgs.path}`;\n } else if (toolArgs.query) {\n detail = `🔎 ${toolArgs.query}`;\n } else if (toolArgs.command) {\n const cmd = String(toolArgs.command);\n detail = `⚡ ${cmd.length > 50 ? cmd.slice(0, 50) + '...' : cmd}`;\n }\n\n const message: ProgressMessage = {\n type: 'start',\n stage: this.currentStage,\n message: `${emoji} ${label}: ${formattedName}`,\n detail: detail || undefined,\n toolName,\n toolArgs,\n };\n\n if (this.config.level === 'verbose' || this.config.streamToolProgress) {\n this.callbacks.onProgress?.(message);\n }\n this.callbacks.onStreamStart?.(toolName, toolArgs);\n\n log.debug({ toolName, stage: this.currentStage }, 'Tool execution started');\n }\n\n // Handle tool execution update (streaming)\n onToolUpdate(toolName: string, partialResult: unknown): void {\n if (this.config.level === 'minimal') return;\n if (!this.streamActive || !this.config.streamToolProgress) return;\n\n // Format partial result for display\n let displayResult = '';\n if (typeof partialResult === 'string') {\n displayResult = partialResult.slice(0, 200); // Truncate for display\n } else if (partialResult && typeof partialResult === 'object') {\n const result = partialResult as Record<string, unknown>;\n if (result.output) {\n displayResult = String(result.output).slice(0, 200);\n } else if (result.content) {\n displayResult = String(result.content).slice(0, 200);\n } else if (result.text) {\n displayResult = String(result.text).slice(0, 200);\n }\n }\n\n this.callbacks.onStreamUpdate?.(toolName, displayResult);\n }\n\n // Handle tool execution end\n onToolEnd(toolName: string, result: unknown, isError: boolean = false): void {\n if (this.config.level === 'minimal' && !isError) return;\n\n const wasActive = this.streamActive;\n this.streamActive = false;\n\n // Extract result summary\n let resultSummary = '';\n if (result && typeof result === 'object') {\n const res = result as Record<string, unknown>;\n if (res.content && Array.isArray(res.content)) {\n const textContent = res.content.find((c: unknown) => \n typeof c === 'object' && c !== null && (c as Record<string, unknown>).type === 'text'\n );\n if (textContent) {\n resultSummary = String((textContent as Record<string, unknown>).text || '').slice(0, 100);\n }\n }\n }\n\n const message: ProgressMessage = {\n type: isError ? 'error' : 'complete',\n stage: this.currentStage,\n message: isError \n ? `❌ Tool failed: ${this.formatToolName(toolName)}`\n : `✅ Done: ${this.formatToolName(toolName)}`,\n toolName,\n };\n\n if (wasActive) {\n this.callbacks.onStreamEnd?.(toolName, resultSummary, isError);\n }\n \n if (isError || this.config.level !== 'minimal') {\n this.callbacks.onProgress?.(message);\n }\n\n // Reset current tool if it matches\n if (this.currentTool === toolName) {\n this.currentTool = null;\n this.currentStage = 'idle';\n }\n\n log.debug({ toolName, isError }, 'Tool execution ended');\n }\n\n // Handle thinking/reasoning\n onThinking(thinking: string): void {\n if (!this.config.showThinking || this.config.level === 'minimal') return;\n\n const displayThinking = thinking.length > 100 \n ? thinking.slice(0, 100) + '...'\n : thinking;\n\n this.callbacks.onThinking?.(displayThinking);\n }\n\n // Handle turn start\n onTurnStart(): void {\n if (this.config.level === 'minimal') return;\n \n this.currentStage = 'thinking';\n this.taskStartTime = Date.now();\n \n if (this.config.heartbeatEnabled && !this.heartbeatTimer) {\n this.startHeartbeat();\n }\n }\n\n // Handle message/response start\n onMessageStart(): void {\n // Could be used to show \"AI is typing...\" indicator\n }\n\n // Handle message/response update (streaming tokens)\n onMessageUpdate(_content: string): void {\n // Could be used to show live response\n }\n\n // Handle agent end\n onAgentEnd(): void {\n this.endTask();\n }\n\n // Get current state\n getCurrentState(): { stage: ProgressStage; tool: string | null; elapsedMs: number } {\n return {\n stage: this.currentStage,\n tool: this.currentTool,\n elapsedMs: this.taskStartTime ? Date.now() - this.taskStartTime : 0,\n };\n }\n\n // Update config\n setConfig(config: Partial<ProgressFeedbackConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n // Get stage emoji\n getStageEmoji(stage: ProgressStage): string {\n return STAGE_EMOJI[stage] || '💬';\n }\n\n // Get stage label\n getStageLabel(stage: ProgressStage): string {\n return STAGE_LABEL[stage] || 'Unknown';\n }\n\n /**\n * Notify about accumulated tool errors\n * Enhanced feedback for tool failures\n */\n onToolErrorAccumulated(\n toolName: string,\n failureCount: number,\n maxFailures: number,\n remainingAttempts?: number\n ): void {\n if (!this.config.showErrorAccumulation) return;\n if (this.config.level === 'minimal') return;\n\n const remaining = remainingAttempts ?? (maxFailures - failureCount);\n const percentage = (failureCount / maxFailures) * 100;\n \n let message: string;\n let type: 'warning' | 'error' = percentage >= 100 ? 'error' : 'warning';\n\n if (percentage >= 100) {\n message = `❌ ${this.formatToolName(toolName)} has reached maximum failures (${maxFailures}/${maxFailures})`;\n } else if (percentage >= 50) {\n message = `⚠️ ${this.formatToolName(toolName)} failed ${failureCount}/${maxFailures} times`;\n } else {\n message = `⚠️ ${this.formatToolName(toolName)} failed ${failureCount} time${failureCount > 1 ? 's' : ''}`;\n }\n\n const progressMsg: ProgressMessage = {\n type,\n stage: 'executing',\n message,\n detail: remaining > 0 \n ? `Remaining attempts: ${remaining}` \n : 'Stopping tool execution',\n toolName,\n };\n\n this.callbacks.onProgress?.(progressMsg);\n\n const atCap = maxFailures > 0 && failureCount >= maxFailures;\n const summary = atCap\n ? `Tool \"${toolName}\" reached max failures (${failureCount}/${maxFailures})`\n : `Tool \"${toolName}\" failures ${failureCount}/${maxFailures} (${remaining} attempt(s) left)`;\n if (atCap) {\n log.error({ toolName, failureCount, maxFailures, remaining }, summary);\n } else {\n log.warn({ toolName, failureCount, maxFailures, remaining }, summary);\n }\n }\n\n /**\n * Notify about request limit status\n * Enhanced feedback for approaching/ reaching request limits\n */\n onRequestLimitStatus(\n count: number,\n limit: number,\n remaining: number,\n isWarning: boolean,\n shouldStop: boolean\n ): void {\n if (!this.config.showRequestLimitWarning) return;\n if (this.config.level === 'minimal') return;\n if (!isWarning && !shouldStop) return;\n\n const percentage = (count / limit) * 100;\n \n let message: string;\n let type: 'warning' | 'error' = shouldStop ? 'error' : 'warning';\n\n if (shouldStop) {\n message = `❌ Request limit reached (${count}/${limit})`;\n } else if (percentage >= 90) {\n message = `⚠️ Request limit warning (${count}/${limit})`;\n } else {\n message = `⚠️ Approaching request limit (${count}/${limit})`;\n }\n\n const progressMsg: ProgressMessage = {\n type,\n stage: 'thinking',\n message,\n detail: remaining > 0 \n ? `Remaining requests: ${remaining}` \n : 'Stopping execution',\n };\n\n this.callbacks.onProgress?.(progressMsg);\n\n const summary = shouldStop\n ? `LLM request limit reached (${count}/${limit}); stopping turn`\n : `LLM request budget ${count}/${limit} (${remaining} left)`;\n if (shouldStop) {\n log.error({ count, limit, remaining, shouldStop }, summary);\n } else {\n log.warn({ count, limit, remaining, shouldStop }, summary);\n }\n }\n}\n\n// Singleton instance with default config\nexport const progressFeedbackManager = new ProgressFeedbackManager();\n\n// Utility function to format progress messages for Telegram\nexport function formatProgressMessage(msg: ProgressMessage, parseMode: 'Markdown' | 'HTML' = 'HTML'): string {\n const emoji = STAGE_EMOJI[msg.stage];\n \n if (parseMode === 'HTML') {\n let html = `<b>${emoji} ${msg.message}</b>`;\n if (msg.detail) {\n html += `\\n${msg.detail}`;\n }\n return html;\n } else {\n let md = `**${emoji} ${msg.message}**`;\n if (msg.detail) {\n md += `\\n${msg.detail}`;\n }\n return md;\n }\n}\n\n// Utility function to format heartbeat message\nexport function formatHeartbeatMessage(elapsedMs: number, stage: ProgressStage, detail?: string): string {\n const seconds = Math.floor(elapsedMs / 1000);\n const emoji = STAGE_EMOJI[stage];\n const label = STAGE_LABEL[stage];\n \n let message = `${emoji} Still ${label.toLowerCase()}...`;\n if (detail) {\n message += `\\n${detail}`;\n }\n message += `\\n⏱️ ${seconds}s elapsed`;\n \n return message;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"progress.js","names":[],"sources":["../../../../src/agent/lifecycle/progress.ts"],"sourcesContent":["/**\n * Progress Feedback Manager\n * \n * Manages real-time progress feedback for long-running agent tasks.\n * Provides:\n * - Tool/end notifications execution start\n * - Thinking indicators\n * - Progress stage updates\n * - Stream integration for real-time display\n */\n\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('ProgressFeedback');\n\n// Progress stages for different operations\nexport type ProgressStage = \n | 'thinking' // AI is thinking/reasoning\n | 'searching' // Web search\n | 'reading' // Reading files\n | 'writing' // Writing files \n | 'executing' // Running shell commands\n | 'analyzing' // Analyzing data\n | 'idle'; // No active operation\n\nexport interface ProgressUpdate {\n stage: ProgressStage;\n detail?: string;\n toolName?: string;\n toolArgs?: Record<string, unknown>;\n timestamp: number;\n}\n\nexport interface ProgressFeedbackConfig {\n // Feedback level: minimal (errors only), normal (key events), verbose (all updates)\n level: 'minimal' | 'normal' | 'verbose';\n // Whether to show thinking indicators\n showThinking: boolean;\n // Whether to stream tool progress updates\n streamToolProgress: boolean;\n // Whether to send periodic heartbeats for long tasks\n heartbeatEnabled: boolean;\n // Heartbeat interval in ms\n heartbeatIntervalMs: number;\n // Threshold for long-running task in ms\n longTaskThresholdMs: number;\n // Enhanced feedback options\n showErrorAccumulation: boolean; // Show tool error accumulation (default: true)\n showRequestLimitWarning: boolean; // Show request limit warnings (default: true)\n}\n\nconst DEFAULT_CONFIG: ProgressFeedbackConfig = {\n level: 'normal',\n showThinking: true,\n streamToolProgress: true,\n heartbeatEnabled: true,\n heartbeatIntervalMs: 20000, // 20 seconds\n longTaskThresholdMs: 30000, // 30 seconds\n showErrorAccumulation: true,\n showRequestLimitWarning: true,\n};\n\n// Tool category mapping for better feedback\nconst TOOL_STAGE_MAP: Record<string, ProgressStage> = {\n // Reading tools\n 'read': 'reading',\n 'read_file': 'reading',\n 'read_multiple': 'reading',\n 'glob': 'reading',\n 'find': 'reading',\n 'list_dir': 'reading',\n 'ls': 'reading',\n 'memory_get': 'reading',\n 'curated_memory': 'writing',\n\n // Searching tools\n 'grep': 'searching',\n 'web_search': 'searching',\n 'search': 'searching',\n 'memory_search': 'searching',\n 'session_search': 'searching',\n 'find_files': 'searching',\n \n // Web fetching\n 'web_fetch': 'reading',\n 'fetch': 'reading',\n 'curl': 'reading',\n 'wget': 'reading',\n \n // Writing tools\n 'write': 'writing',\n 'write_file': 'writing',\n 'write_multiple': 'writing',\n 'edit': 'writing',\n 'patch': 'writing',\n 'create': 'writing',\n 'mkdir': 'writing',\n 'delete': 'writing',\n 'remove': 'writing',\n \n // Executing tools\n 'bash': 'executing',\n 'shell': 'executing',\n 'exec': 'executing',\n 'run': 'executing',\n 'command': 'executing',\n 'node': 'executing',\n 'python': 'executing',\n 'npm': 'executing',\n 'pnpm': 'executing',\n 'git': 'executing',\n 'docker': 'executing',\n 'kubectl': 'executing',\n \n // Message/communication\n 'message': 'idle',\n 'send': 'idle',\n 'reply': 'idle',\n \n // Analysis tools\n 'analyze': 'analyzing',\n 'compare': 'analyzing',\n 'diff': 'analyzing',\n 'summarize': 'analyzing',\n};\n\n// Emoji mapping for stages\nconst STAGE_EMOJI: Record<ProgressStage, string> = {\n 'thinking': '🤔',\n 'searching': '🔍',\n 'reading': '📖',\n 'writing': '✍️',\n 'executing': '⚙️',\n 'analyzing': '📊',\n 'idle': '💬',\n};\n\n// Stage labels in English\nconst STAGE_LABEL: Record<ProgressStage, string> = {\n 'thinking': 'Thinking',\n 'searching': 'Searching',\n 'reading': 'Reading',\n 'writing': 'Writing',\n 'executing': 'Executing',\n 'analyzing': 'Analyzing',\n 'idle': 'Ready',\n};\n\nexport interface ProgressMessage {\n type: 'start' | 'update' | 'complete' | 'error' | 'warning' | 'thinking';\n stage: ProgressStage;\n message: string;\n detail?: string;\n toolName?: string;\n toolArgs?: Record<string, unknown>;\n}\n\nexport interface ProgressCallbacks {\n onProgress?: (msg: ProgressMessage) => void;\n onStreamStart?: (toolName: string, toolArgs: Record<string, unknown>) => void;\n onStreamUpdate?: (toolName: string, partialResult: string) => void;\n onStreamEnd?: (toolName: string, result: string, isError: boolean) => void;\n onThinking?: (thinking: string) => void;\n onHeartbeat?: (elapsedMs: number, currentStage: ProgressStage) => void;\n}\n\nexport class ProgressFeedbackManager {\n private config: ProgressFeedbackConfig;\n private callbacks: ProgressCallbacks = {};\n private currentStage: ProgressStage = 'idle';\n private currentTool: string | null = null;\n private taskStartTime: number = 0;\n private heartbeatTimer?: ReturnType<typeof setInterval>;\n private streamActive: boolean = false;\n\n constructor(config: Partial<ProgressFeedbackConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n setCallbacks(callbacks: ProgressCallbacks): void {\n this.callbacks = { ...this.callbacks, ...callbacks };\n }\n\n startTask(): void {\n this.taskStartTime = Date.now();\n \n if (this.config.heartbeatEnabled) {\n this.startHeartbeat();\n }\n }\n\n endTask(): void {\n this.stopHeartbeat();\n this.currentStage = 'idle';\n this.currentTool = null;\n this.taskStartTime = 0;\n this.streamActive = false;\n }\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n this.heartbeatTimer = setInterval(() => {\n const elapsed = Date.now() - this.taskStartTime;\n if (elapsed >= this.config.longTaskThresholdMs) {\n this.callbacks.onHeartbeat?.(elapsed, this.currentStage);\n }\n }, this.config.heartbeatIntervalMs);\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = undefined;\n }\n }\n\n private mapToolToStage(toolName: string): ProgressStage {\n const normalizedName = toolName.toLowerCase();\n const normalizedNoUnderscore = normalizedName.replace(/_/g, '');\n \n // Try both versions: with and without underscores\n const toCheck = [normalizedName, normalizedNoUnderscore];\n \n // Sort keys by length (longest first) to match more specific keys first\n const sortedKeys = Object.keys(TOOL_STAGE_MAP).sort((a, b) => b.length - a.length);\n \n for (const name of toCheck) {\n for (const key of sortedKeys) {\n if (name.includes(key)) {\n return TOOL_STAGE_MAP[key];\n }\n }\n }\n return 'executing';\n }\n\n private formatToolName(toolName: string): string {\n // Convert snake_case to Title Case\n return toolName\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, c => c.toUpperCase());\n }\n\n // Handle tool execution start\n onToolStart(toolName: string, toolArgs: Record<string, unknown> = {}): void {\n if (this.config.level === 'minimal') return;\n\n this.currentTool = toolName;\n this.currentStage = this.mapToolToStage(toolName);\n this.streamActive = true;\n\n const formattedName = this.formatToolName(toolName);\n const emoji = STAGE_EMOJI[this.currentStage];\n const label = STAGE_LABEL[this.currentStage];\n\n // Build detail message\n let detail = '';\n if (toolArgs.path) {\n detail = `📁 ${toolArgs.path}`;\n } else if (toolArgs.query) {\n detail = `🔎 ${toolArgs.query}`;\n } else if (toolArgs.command) {\n const cmd = String(toolArgs.command);\n detail = `⚡ ${cmd.length > 50 ? cmd.slice(0, 50) + '...' : cmd}`;\n }\n\n const message: ProgressMessage = {\n type: 'start',\n stage: this.currentStage,\n message: `${emoji} ${label}: ${formattedName}`,\n detail: detail || undefined,\n toolName,\n toolArgs,\n };\n\n if (this.config.level === 'verbose' || this.config.streamToolProgress) {\n this.callbacks.onProgress?.(message);\n }\n this.callbacks.onStreamStart?.(toolName, toolArgs);\n\n log.debug({ toolName, stage: this.currentStage }, 'Tool execution started');\n }\n\n // Handle tool execution update (streaming)\n onToolUpdate(toolName: string, partialResult: unknown): void {\n if (this.config.level === 'minimal') return;\n if (!this.streamActive || !this.config.streamToolProgress) return;\n\n // Format partial result for display\n let displayResult = '';\n if (typeof partialResult === 'string') {\n displayResult = partialResult.slice(0, 200); // Truncate for display\n } else if (partialResult && typeof partialResult === 'object') {\n const result = partialResult as Record<string, unknown>;\n if (result.output) {\n displayResult = String(result.output).slice(0, 200);\n } else if (result.content) {\n displayResult = String(result.content).slice(0, 200);\n } else if (result.text) {\n displayResult = String(result.text).slice(0, 200);\n }\n }\n\n this.callbacks.onStreamUpdate?.(toolName, displayResult);\n }\n\n // Handle tool execution end\n onToolEnd(toolName: string, result: unknown, isError: boolean = false): void {\n if (this.config.level === 'minimal' && !isError) return;\n\n const wasActive = this.streamActive;\n this.streamActive = false;\n\n // Extract result summary\n let resultSummary = '';\n if (result && typeof result === 'object') {\n const res = result as Record<string, unknown>;\n if (res.content && Array.isArray(res.content)) {\n const textContent = res.content.find((c: unknown) => \n typeof c === 'object' && c !== null && (c as Record<string, unknown>).type === 'text'\n );\n if (textContent) {\n resultSummary = String((textContent as Record<string, unknown>).text || '').slice(0, 100);\n }\n }\n }\n\n const message: ProgressMessage = {\n type: isError ? 'error' : 'complete',\n stage: this.currentStage,\n message: isError \n ? `❌ Tool failed: ${this.formatToolName(toolName)}`\n : `✅ Done: ${this.formatToolName(toolName)}`,\n toolName,\n };\n\n if (wasActive) {\n this.callbacks.onStreamEnd?.(toolName, resultSummary, isError);\n }\n \n if (isError || this.config.level !== 'minimal') {\n this.callbacks.onProgress?.(message);\n }\n\n // Reset current tool if it matches\n if (this.currentTool === toolName) {\n this.currentTool = null;\n this.currentStage = 'idle';\n }\n\n log.debug({ toolName, isError }, 'Tool execution ended');\n }\n\n // Handle thinking/reasoning\n onThinking(thinking: string): void {\n if (!this.config.showThinking || this.config.level === 'minimal') return;\n\n const displayThinking = thinking.length > 100 \n ? thinking.slice(0, 100) + '...'\n : thinking;\n\n this.callbacks.onThinking?.(displayThinking);\n }\n\n // Handle turn start\n onTurnStart(): void {\n if (this.config.level === 'minimal') return;\n \n this.currentStage = 'thinking';\n this.taskStartTime = Date.now();\n \n if (this.config.heartbeatEnabled && !this.heartbeatTimer) {\n this.startHeartbeat();\n }\n }\n\n // Handle message/response start\n onMessageStart(): void {\n // Could be used to show \"AI is typing...\" indicator\n }\n\n // Handle message/response update (streaming tokens)\n onMessageUpdate(_content: string): void {\n // Could be used to show live response\n }\n\n // Handle agent end\n onAgentEnd(): void {\n this.endTask();\n }\n\n // Get current state\n getCurrentState(): { stage: ProgressStage; tool: string | null; elapsedMs: number } {\n return {\n stage: this.currentStage,\n tool: this.currentTool,\n elapsedMs: this.taskStartTime ? Date.now() - this.taskStartTime : 0,\n };\n }\n\n // Update config\n setConfig(config: Partial<ProgressFeedbackConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n // Get stage emoji\n getStageEmoji(stage: ProgressStage): string {\n return STAGE_EMOJI[stage] || '💬';\n }\n\n // Get stage label\n getStageLabel(stage: ProgressStage): string {\n return STAGE_LABEL[stage] || 'Unknown';\n }\n\n /**\n * Notify about accumulated tool errors\n * Enhanced feedback for tool failures\n */\n onToolErrorAccumulated(\n toolName: string,\n failureCount: number,\n maxFailures: number,\n remainingAttempts?: number\n ): void {\n if (!this.config.showErrorAccumulation) return;\n if (this.config.level === 'minimal') return;\n\n const remaining = remainingAttempts ?? (maxFailures - failureCount);\n const percentage = (failureCount / maxFailures) * 100;\n \n let message: string;\n let type: 'warning' | 'error' = percentage >= 100 ? 'error' : 'warning';\n\n if (percentage >= 100) {\n message = `❌ ${this.formatToolName(toolName)} has reached maximum failures (${maxFailures}/${maxFailures})`;\n } else if (percentage >= 50) {\n message = `⚠️ ${this.formatToolName(toolName)} failed ${failureCount}/${maxFailures} times`;\n } else {\n message = `⚠️ ${this.formatToolName(toolName)} failed ${failureCount} time${failureCount > 1 ? 's' : ''}`;\n }\n\n const progressMsg: ProgressMessage = {\n type,\n stage: 'executing',\n message,\n detail: remaining > 0 \n ? `Remaining attempts: ${remaining}` \n : 'Stopping tool execution',\n toolName,\n };\n\n this.callbacks.onProgress?.(progressMsg);\n\n const atCap = maxFailures > 0 && failureCount >= maxFailures;\n const summary = atCap\n ? `Tool \"${toolName}\" reached max failures (${failureCount}/${maxFailures})`\n : `Tool \"${toolName}\" failures ${failureCount}/${maxFailures} (${remaining} attempt(s) left)`;\n if (atCap) {\n log.error({ toolName, failureCount, maxFailures, remaining }, summary);\n } else {\n log.warn({ toolName, failureCount, maxFailures, remaining }, summary);\n }\n }\n\n /**\n * Notify about request limit status\n * Enhanced feedback for approaching/ reaching request limits\n */\n onRequestLimitStatus(\n count: number,\n limit: number,\n remaining: number,\n isWarning: boolean,\n shouldStop: boolean\n ): void {\n if (!this.config.showRequestLimitWarning) return;\n if (this.config.level === 'minimal') return;\n if (!isWarning && !shouldStop) return;\n\n const percentage = (count / limit) * 100;\n \n let message: string;\n let type: 'warning' | 'error' = shouldStop ? 'error' : 'warning';\n\n if (shouldStop) {\n message = `❌ Request limit reached (${count}/${limit})`;\n } else if (percentage >= 90) {\n message = `⚠️ Request limit warning (${count}/${limit})`;\n } else {\n message = `⚠️ Approaching request limit (${count}/${limit})`;\n }\n\n const progressMsg: ProgressMessage = {\n type,\n stage: 'thinking',\n message,\n detail: remaining > 0 \n ? `Remaining requests: ${remaining}` \n : 'Stopping execution',\n };\n\n this.callbacks.onProgress?.(progressMsg);\n\n const summary = shouldStop\n ? `LLM request limit reached (${count}/${limit}); stopping turn`\n : `LLM request budget ${count}/${limit} (${remaining} left)`;\n if (shouldStop) {\n log.error({ count, limit, remaining, shouldStop }, summary);\n } else {\n log.warn({ count, limit, remaining, shouldStop }, summary);\n }\n }\n}\n\n// Singleton instance with default config\nexport const progressFeedbackManager = new ProgressFeedbackManager();\n\n// Utility function to format progress messages for Telegram\nexport function formatProgressMessage(msg: ProgressMessage, parseMode: 'Markdown' | 'HTML' = 'HTML'): string {\n const emoji = STAGE_EMOJI[msg.stage];\n \n if (parseMode === 'HTML') {\n let html = `<b>${emoji} ${msg.message}</b>`;\n if (msg.detail) {\n html += `\\n${msg.detail}`;\n }\n return html;\n } else {\n let md = `**${emoji} ${msg.message}**`;\n if (msg.detail) {\n md += `\\n${msg.detail}`;\n }\n return md;\n }\n}\n\n// Utility function to format heartbeat message\nexport function formatHeartbeatMessage(elapsedMs: number, stage: ProgressStage, detail?: string): string {\n const seconds = Math.floor(elapsedMs / 1000);\n const emoji = STAGE_EMOJI[stage];\n const label = STAGE_LABEL[stage];\n \n let message = `${emoji} Still ${label.toLowerCase()}...`;\n if (detail) {\n message += `\\n${detail}`;\n }\n message += `\\n⏱️ ${seconds}s elapsed`;\n \n return message;\n}\n"],"mappings":"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"}
|
|
@@ -67,9 +67,8 @@ export declare function withTimeout<T extends (...args: any[]) => Promise<any>>(
|
|
|
67
67
|
* Timeout manager for tracking execution statistics
|
|
68
68
|
*/
|
|
69
69
|
export declare class TimeoutManager {
|
|
70
|
-
private config;
|
|
71
70
|
private executions;
|
|
72
|
-
constructor(
|
|
71
|
+
constructor(_config?: Partial<TimeoutConfig>);
|
|
73
72
|
/**
|
|
74
73
|
* Execute with timeout and track statistics
|
|
75
74
|
*/
|
|
@@ -157,14 +157,8 @@ function withTimeout(fn, config) {
|
|
|
157
157
|
* Timeout manager for tracking execution statistics
|
|
158
158
|
*/
|
|
159
159
|
var TimeoutManager = class {
|
|
160
|
-
config;
|
|
161
160
|
executions = [];
|
|
162
|
-
constructor(
|
|
163
|
-
this.config = {
|
|
164
|
-
...DEFAULT_CONFIG,
|
|
165
|
-
...config
|
|
166
|
-
};
|
|
167
|
-
}
|
|
161
|
+
constructor(_config = {}) {}
|
|
168
162
|
/**
|
|
169
163
|
* Execute with timeout and track statistics
|
|
170
164
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeout-wrapper.js","names":[],"sources":["../../../../src/agent/lifecycle/timeout-wrapper.ts"],"sourcesContent":["/**\n * Timeout Wrapper - Protects tool execution from hanging\n *\n * Wraps tool execution with timeout protection to prevent:\n * - Infinite loops in shell commands\n * - Network requests hanging forever\n * - Resource exhaustion\n */\n\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('TimeoutWrapper');\n\nexport interface TimeoutConfig {\n defaultTimeoutMs: number; // Default timeout in ms (default: 300000 = 5 min)\n shellTimeoutMs: number; // Shell command timeout (default: 300000 = 5 min)\n readTimeoutMs: number; // File read timeout (default: 30000 = 30 sec)\n writeTimeoutMs: number; // File write timeout (default: 60000 = 1 min)\n networkTimeoutMs: number; // Network request timeout (default: 60000 = 1 min)\n gracefulShutdownMs: number; // Time to wait for graceful shutdown (default: 5000)\n}\n\nconst DEFAULT_CONFIG: TimeoutConfig = {\n defaultTimeoutMs: 5 * 60 * 1000, // 5 minutes\n shellTimeoutMs: 5 * 60 * 1000, // 5 minutes\n readTimeoutMs: 30 * 1000, // 30 seconds\n writeTimeoutMs: 60 * 1000, // 1 minute\n networkTimeoutMs: 60 * 1000, // 1 minute\n gracefulShutdownMs: 5000, // 5 seconds\n};\n\nexport interface TimeoutResult<T> {\n success: boolean;\n result?: T;\n error?: Error;\n timedOut: boolean;\n executionTimeMs: number;\n}\n\nexport interface ToolTimeoutConfig {\n toolName: string;\n timeoutMs?: number;\n description?: string;\n}\n\n/**\n * Get timeout for specific tool type\n */\nfunction getTimeoutForTool(toolName: string, config: TimeoutConfig): number {\n const toolLower = toolName.toLowerCase();\n\n if (toolLower.includes('shell') || toolLower.includes('exec')) {\n return config.shellTimeoutMs;\n }\n if (toolLower.includes('read') || toolLower.includes('view')) {\n return config.readTimeoutMs;\n }\n if (toolLower.includes('write') || toolLower.includes('edit')) {\n return config.writeTimeoutMs;\n }\n if (toolLower.includes('web') || toolLower.includes('http') || toolLower.includes('fetch')) {\n return config.networkTimeoutMs;\n }\n\n return config.defaultTimeoutMs;\n}\n\n/**\n * Execute function with timeout protection\n *\n * @example\n * ```typescript\n * const result = await executeWithTimeout(\n * () => shell.exec('long-running-command'),\n * { toolName: 'shell', timeoutMs: 60000 }\n * );\n * ```\n */\nexport async function executeWithTimeout<T>(\n operation: () => Promise<T>,\n config: ToolTimeoutConfig\n): Promise<T> {\n const fullConfig = { ...DEFAULT_CONFIG };\n const timeoutMs = config.timeoutMs || getTimeoutForTool(config.toolName, fullConfig);\n\n const startTime = Date.now();\n let timeoutId: NodeJS.Timeout | null = null;\n let isSettled = false;\n\n return new Promise((resolve, reject) => {\n // Set up timeout\n timeoutId = setTimeout(() => {\n if (isSettled) return;\n isSettled = true;\n \n const executionTime = Date.now() - startTime;\n const desc = config.description ? ` (${config.description})` : '';\n log.error(\n {\n toolName: config.toolName,\n timeoutMs,\n executionTimeMs: executionTime,\n description: config.description,\n },\n `Tool \"${config.toolName}\" timed out after ${timeoutMs}ms (elapsed ${executionTime}ms)${desc}`,\n );\n\n reject(\n new TimeoutError(\n config.toolName,\n timeoutMs,\n config.description,\n executionTime\n )\n );\n }, timeoutMs);\n\n // Execute operation\n operation()\n .then(result => {\n if (isSettled) return;\n isSettled = true;\n \n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n const executionTime = Date.now() - startTime;\n\n log.debug(\n {\n toolName: config.toolName,\n executionTimeMs: executionTime,\n },\n 'Tool execution completed within timeout'\n );\n\n resolve(result);\n })\n .catch(error => {\n if (isSettled) return;\n isSettled = true;\n \n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n reject(error);\n });\n });\n}\n\n/**\n * Execute with timeout and return detailed result\n */\nexport async function executeWithTimeoutResult<T>(\n operation: () => Promise<T>,\n config: ToolTimeoutConfig\n): Promise<TimeoutResult<T>> {\n const startTime = Date.now();\n\n try {\n const result = await executeWithTimeout(operation, config);\n return {\n success: true,\n result,\n timedOut: false,\n executionTimeMs: Date.now() - startTime,\n };\n } catch (error) {\n const isTimeout = error instanceof TimeoutError;\n return {\n success: false,\n error: error instanceof Error ? error : new Error(String(error)),\n timedOut: isTimeout,\n executionTimeMs: Date.now() - startTime,\n };\n }\n}\n\n/**\n * Custom timeout error\n */\nexport class TimeoutError extends Error {\n public readonly toolName: string;\n public readonly timeoutMs: number;\n public readonly executionTimeMs: number;\n public readonly description?: string;\n\n constructor(\n toolName: string,\n timeoutMs: number,\n description?: string,\n executionTimeMs: number = timeoutMs\n ) {\n const timeoutSec = Math.round(timeoutMs / 1000);\n const message = description\n ? `Tool '${toolName}' timed out after ${timeoutSec}s: ${description}`\n : `Tool '${toolName}' timed out after ${timeoutSec}s. Consider breaking the operation into smaller steps.`;\n\n super(message);\n this.name = 'TimeoutError';\n this.toolName = toolName;\n this.timeoutMs = timeoutMs;\n this.description = description;\n this.executionTimeMs = executionTimeMs;\n\n // Maintain proper stack trace\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TimeoutError);\n }\n }\n\n /**\n * Get user-friendly error message with suggestions\n */\n getUserMessage(): string {\n const suggestions: string[] = [];\n\n if (this.toolName.toLowerCase().includes('shell')) {\n suggestions.push('Break the command into smaller steps');\n suggestions.push('Add timeouts to individual commands');\n suggestions.push('Check for infinite loops or waiting for input');\n } else if (this.toolName.toLowerCase().includes('read')) {\n suggestions.push('Read the file in smaller chunks');\n suggestions.push('Use grep to find specific content first');\n } else if (this.toolName.toLowerCase().includes('write')) {\n suggestions.push('Write content in smaller batches');\n } else if (this.toolName.toLowerCase().includes('web')) {\n suggestions.push('Check network connectivity');\n suggestions.push('Try again later');\n }\n\n let message = `⚠️ ${this.message}`;\n if (suggestions.length > 0) {\n message += '\\n\\nSuggestions:\\n' + suggestions.map(s => `• ${s}`).join('\\n');\n }\n\n return message;\n }\n}\n\n/**\n * Wrap a function with timeout protection\n */\nexport function withTimeout<T extends (...args: any[]) => Promise<any>>(\n fn: T,\n config: Omit<ToolTimeoutConfig, 'description'> & { description?: string | ((...args: Parameters<T>) => string) }\n): (...args: Parameters<T>) => Promise<ReturnType<T>> {\n return async (...args: Parameters<T>): Promise<ReturnType<T>> => {\n const description = typeof config.description === 'function'\n ? config.description(...args)\n : config.description;\n\n return executeWithTimeout(() => fn(...args), {\n toolName: config.toolName,\n timeoutMs: config.timeoutMs,\n description,\n });\n };\n}\n\n/**\n * Timeout manager for tracking execution statistics\n */\nexport class TimeoutManager {\n private config: TimeoutConfig;\n private executions: Array<{\n toolName: string;\n executionTimeMs: number;\n timedOut: boolean;\n timestamp: number;\n }> = [];\n\n constructor(config: Partial<TimeoutConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Execute with timeout and track statistics\n */\n async execute<T>(\n operation: () => Promise<T>,\n toolConfig: ToolTimeoutConfig\n ): Promise<T> {\n const startTime = Date.now();\n let timedOut = false;\n\n try {\n const result = await executeWithTimeout(operation, toolConfig);\n this.executions.push({\n toolName: toolConfig.toolName,\n executionTimeMs: Date.now() - startTime,\n timedOut: false,\n timestamp: Date.now(),\n });\n return result;\n } catch (error) {\n if (error instanceof TimeoutError) {\n timedOut = true;\n }\n this.executions.push({\n toolName: toolConfig.toolName,\n executionTimeMs: Date.now() - startTime,\n timedOut,\n timestamp: Date.now(),\n });\n throw error;\n }\n }\n\n /**\n * Get execution statistics\n */\n getStats(): {\n totalExecutions: number;\n timeoutCount: number;\n timeoutRate: number;\n averageExecutionTimeMs: number;\n byTool: Record<string, {\n count: number;\n timeouts: number;\n avgExecutionTimeMs: number;\n }>;\n } {\n const byTool: Record<string, { count: number; timeouts: number; totalTime: number }> = {};\n\n for (const exec of this.executions) {\n if (!byTool[exec.toolName]) {\n byTool[exec.toolName] = { count: 0, timeouts: 0, totalTime: 0 };\n }\n byTool[exec.toolName].count++;\n if (exec.timedOut) {\n byTool[exec.toolName].timeouts++;\n }\n byTool[exec.toolName].totalTime += exec.executionTimeMs;\n }\n\n const totalExecutions = this.executions.length;\n const timeoutCount = this.executions.filter(e => e.timedOut).length;\n const totalTime = this.executions.reduce((sum, e) => sum + e.executionTimeMs, 0);\n\n return {\n totalExecutions,\n timeoutCount,\n timeoutRate: totalExecutions > 0 ? timeoutCount / totalExecutions : 0,\n averageExecutionTimeMs: totalExecutions > 0 ? totalTime / totalExecutions : 0,\n byTool: Object.fromEntries(\n Object.entries(byTool).map(([name, stats]) => [\n name,\n {\n count: stats.count,\n timeouts: stats.timeouts,\n avgExecutionTimeMs: stats.count > 0 ? stats.totalTime / stats.count : 0,\n },\n ])\n ),\n };\n }\n\n /**\n * Clean up old execution records\n */\n cleanup(maxAgeMs: number = 24 * 60 * 60 * 1000): void {\n const cutoff = Date.now() - maxAgeMs;\n this.executions = this.executions.filter(e => e.timestamp > cutoff);\n }\n}\n\n// Convenience exports\nexport { DEFAULT_CONFIG as DEFAULT_TIMEOUT_CONFIG };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"timeout-wrapper.js","names":[],"sources":["../../../../src/agent/lifecycle/timeout-wrapper.ts"],"sourcesContent":["/**\n * Timeout Wrapper - Protects tool execution from hanging\n *\n * Wraps tool execution with timeout protection to prevent:\n * - Infinite loops in shell commands\n * - Network requests hanging forever\n * - Resource exhaustion\n */\n\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('TimeoutWrapper');\n\nexport interface TimeoutConfig {\n defaultTimeoutMs: number; // Default timeout in ms (default: 300000 = 5 min)\n shellTimeoutMs: number; // Shell command timeout (default: 300000 = 5 min)\n readTimeoutMs: number; // File read timeout (default: 30000 = 30 sec)\n writeTimeoutMs: number; // File write timeout (default: 60000 = 1 min)\n networkTimeoutMs: number; // Network request timeout (default: 60000 = 1 min)\n gracefulShutdownMs: number; // Time to wait for graceful shutdown (default: 5000)\n}\n\nconst DEFAULT_CONFIG: TimeoutConfig = {\n defaultTimeoutMs: 5 * 60 * 1000, // 5 minutes\n shellTimeoutMs: 5 * 60 * 1000, // 5 minutes\n readTimeoutMs: 30 * 1000, // 30 seconds\n writeTimeoutMs: 60 * 1000, // 1 minute\n networkTimeoutMs: 60 * 1000, // 1 minute\n gracefulShutdownMs: 5000, // 5 seconds\n};\n\nexport interface TimeoutResult<T> {\n success: boolean;\n result?: T;\n error?: Error;\n timedOut: boolean;\n executionTimeMs: number;\n}\n\nexport interface ToolTimeoutConfig {\n toolName: string;\n timeoutMs?: number;\n description?: string;\n}\n\n/**\n * Get timeout for specific tool type\n */\nfunction getTimeoutForTool(toolName: string, config: TimeoutConfig): number {\n const toolLower = toolName.toLowerCase();\n\n if (toolLower.includes('shell') || toolLower.includes('exec')) {\n return config.shellTimeoutMs;\n }\n if (toolLower.includes('read') || toolLower.includes('view')) {\n return config.readTimeoutMs;\n }\n if (toolLower.includes('write') || toolLower.includes('edit')) {\n return config.writeTimeoutMs;\n }\n if (toolLower.includes('web') || toolLower.includes('http') || toolLower.includes('fetch')) {\n return config.networkTimeoutMs;\n }\n\n return config.defaultTimeoutMs;\n}\n\n/**\n * Execute function with timeout protection\n *\n * @example\n * ```typescript\n * const result = await executeWithTimeout(\n * () => shell.exec('long-running-command'),\n * { toolName: 'shell', timeoutMs: 60000 }\n * );\n * ```\n */\nexport async function executeWithTimeout<T>(\n operation: () => Promise<T>,\n config: ToolTimeoutConfig\n): Promise<T> {\n const fullConfig = { ...DEFAULT_CONFIG };\n const timeoutMs = config.timeoutMs || getTimeoutForTool(config.toolName, fullConfig);\n\n const startTime = Date.now();\n let timeoutId: NodeJS.Timeout | null = null;\n let isSettled = false;\n\n return new Promise((resolve, reject) => {\n // Set up timeout\n timeoutId = setTimeout(() => {\n if (isSettled) return;\n isSettled = true;\n \n const executionTime = Date.now() - startTime;\n const desc = config.description ? ` (${config.description})` : '';\n log.error(\n {\n toolName: config.toolName,\n timeoutMs,\n executionTimeMs: executionTime,\n description: config.description,\n },\n `Tool \"${config.toolName}\" timed out after ${timeoutMs}ms (elapsed ${executionTime}ms)${desc}`,\n );\n\n reject(\n new TimeoutError(\n config.toolName,\n timeoutMs,\n config.description,\n executionTime\n )\n );\n }, timeoutMs);\n\n // Execute operation\n operation()\n .then(result => {\n if (isSettled) return;\n isSettled = true;\n \n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n const executionTime = Date.now() - startTime;\n\n log.debug(\n {\n toolName: config.toolName,\n executionTimeMs: executionTime,\n },\n 'Tool execution completed within timeout'\n );\n\n resolve(result);\n })\n .catch(error => {\n if (isSettled) return;\n isSettled = true;\n \n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n reject(error);\n });\n });\n}\n\n/**\n * Execute with timeout and return detailed result\n */\nexport async function executeWithTimeoutResult<T>(\n operation: () => Promise<T>,\n config: ToolTimeoutConfig\n): Promise<TimeoutResult<T>> {\n const startTime = Date.now();\n\n try {\n const result = await executeWithTimeout(operation, config);\n return {\n success: true,\n result,\n timedOut: false,\n executionTimeMs: Date.now() - startTime,\n };\n } catch (error) {\n const isTimeout = error instanceof TimeoutError;\n return {\n success: false,\n error: error instanceof Error ? error : new Error(String(error)),\n timedOut: isTimeout,\n executionTimeMs: Date.now() - startTime,\n };\n }\n}\n\n/**\n * Custom timeout error\n */\nexport class TimeoutError extends Error {\n public readonly toolName: string;\n public readonly timeoutMs: number;\n public readonly executionTimeMs: number;\n public readonly description?: string;\n\n constructor(\n toolName: string,\n timeoutMs: number,\n description?: string,\n executionTimeMs: number = timeoutMs\n ) {\n const timeoutSec = Math.round(timeoutMs / 1000);\n const message = description\n ? `Tool '${toolName}' timed out after ${timeoutSec}s: ${description}`\n : `Tool '${toolName}' timed out after ${timeoutSec}s. Consider breaking the operation into smaller steps.`;\n\n super(message);\n this.name = 'TimeoutError';\n this.toolName = toolName;\n this.timeoutMs = timeoutMs;\n this.description = description;\n this.executionTimeMs = executionTimeMs;\n\n // Maintain proper stack trace\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TimeoutError);\n }\n }\n\n /**\n * Get user-friendly error message with suggestions\n */\n getUserMessage(): string {\n const suggestions: string[] = [];\n\n if (this.toolName.toLowerCase().includes('shell')) {\n suggestions.push('Break the command into smaller steps');\n suggestions.push('Add timeouts to individual commands');\n suggestions.push('Check for infinite loops or waiting for input');\n } else if (this.toolName.toLowerCase().includes('read')) {\n suggestions.push('Read the file in smaller chunks');\n suggestions.push('Use grep to find specific content first');\n } else if (this.toolName.toLowerCase().includes('write')) {\n suggestions.push('Write content in smaller batches');\n } else if (this.toolName.toLowerCase().includes('web')) {\n suggestions.push('Check network connectivity');\n suggestions.push('Try again later');\n }\n\n let message = `⚠️ ${this.message}`;\n if (suggestions.length > 0) {\n message += '\\n\\nSuggestions:\\n' + suggestions.map(s => `• ${s}`).join('\\n');\n }\n\n return message;\n }\n}\n\n/**\n * Wrap a function with timeout protection\n */\nexport function withTimeout<T extends (...args: any[]) => Promise<any>>(\n fn: T,\n config: Omit<ToolTimeoutConfig, 'description'> & { description?: string | ((...args: Parameters<T>) => string) }\n): (...args: Parameters<T>) => Promise<ReturnType<T>> {\n return async (...args: Parameters<T>): Promise<ReturnType<T>> => {\n const description = typeof config.description === 'function'\n ? config.description(...args)\n : config.description;\n\n return executeWithTimeout(() => fn(...args), {\n toolName: config.toolName,\n timeoutMs: config.timeoutMs,\n description,\n });\n };\n}\n\n/**\n * Timeout manager for tracking execution statistics\n */\nexport class TimeoutManager {\n private executions: Array<{\n toolName: string;\n executionTimeMs: number;\n timedOut: boolean;\n timestamp: number;\n }> = [];\n\n constructor(_config: Partial<TimeoutConfig> = {}) {}\n\n /**\n * Execute with timeout and track statistics\n */\n async execute<T>(\n operation: () => Promise<T>,\n toolConfig: ToolTimeoutConfig\n ): Promise<T> {\n const startTime = Date.now();\n let timedOut = false;\n\n try {\n const result = await executeWithTimeout(operation, toolConfig);\n this.executions.push({\n toolName: toolConfig.toolName,\n executionTimeMs: Date.now() - startTime,\n timedOut: false,\n timestamp: Date.now(),\n });\n return result;\n } catch (error) {\n if (error instanceof TimeoutError) {\n timedOut = true;\n }\n this.executions.push({\n toolName: toolConfig.toolName,\n executionTimeMs: Date.now() - startTime,\n timedOut,\n timestamp: Date.now(),\n });\n throw error;\n }\n }\n\n /**\n * Get execution statistics\n */\n getStats(): {\n totalExecutions: number;\n timeoutCount: number;\n timeoutRate: number;\n averageExecutionTimeMs: number;\n byTool: Record<string, {\n count: number;\n timeouts: number;\n avgExecutionTimeMs: number;\n }>;\n } {\n const byTool: Record<string, { count: number; timeouts: number; totalTime: number }> = {};\n\n for (const exec of this.executions) {\n if (!byTool[exec.toolName]) {\n byTool[exec.toolName] = { count: 0, timeouts: 0, totalTime: 0 };\n }\n byTool[exec.toolName].count++;\n if (exec.timedOut) {\n byTool[exec.toolName].timeouts++;\n }\n byTool[exec.toolName].totalTime += exec.executionTimeMs;\n }\n\n const totalExecutions = this.executions.length;\n const timeoutCount = this.executions.filter(e => e.timedOut).length;\n const totalTime = this.executions.reduce((sum, e) => sum + e.executionTimeMs, 0);\n\n return {\n totalExecutions,\n timeoutCount,\n timeoutRate: totalExecutions > 0 ? timeoutCount / totalExecutions : 0,\n averageExecutionTimeMs: totalExecutions > 0 ? totalTime / totalExecutions : 0,\n byTool: Object.fromEntries(\n Object.entries(byTool).map(([name, stats]) => [\n name,\n {\n count: stats.count,\n timeouts: stats.timeouts,\n avgExecutionTimeMs: stats.count > 0 ? stats.totalTime / stats.count : 0,\n },\n ])\n ),\n };\n }\n\n /**\n * Clean up old execution records\n */\n cleanup(maxAgeMs: number = 24 * 60 * 60 * 1000): void {\n const cutoff = Date.now() - maxAgeMs;\n this.executions = this.executions.filter(e => e.timestamp > cutoff);\n }\n}\n\n// Convenience exports\nexport { DEFAULT_CONFIG as DEFAULT_TIMEOUT_CONFIG };\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Bounded curated memory: agent home `memories/MEMORY.md` + `
|
|
2
|
+
* Bounded curated memory: agent home `memories/MEMORY.md` + global `user/MEMORY.md`, §-delimited entries.
|
|
3
3
|
* Snapshot for system prompt is captured at load time and not mutated until next load.
|
|
4
4
|
*/
|
|
5
5
|
import type { MemorySnapshot, MemoryStoreConfig } from './types.js';
|
|
@@ -10,10 +10,12 @@ export declare class BuiltinMemoryStore {
|
|
|
10
10
|
private userEntries;
|
|
11
11
|
private snapshot;
|
|
12
12
|
constructor(config: MemoryStoreConfig);
|
|
13
|
-
/** When false,
|
|
13
|
+
/** When false, global user memory is omitted from the snapshot and should not be edited via `curated_memory`. */
|
|
14
14
|
isUserProfileEnabled(): boolean;
|
|
15
15
|
/** Agent-scoped curated memory directory (`~/.xopc/agents/<id>/memories/`). */
|
|
16
16
|
get memoriesDir(): string;
|
|
17
|
+
/** Global user memory file (`~/.xopc/user/MEMORY.md`). */
|
|
18
|
+
get userMemoryPath(): string;
|
|
17
19
|
/** Workspace whose MEMORY.md and daily notes participate in local memory search. */
|
|
18
20
|
get workspaceDir(): string;
|
|
19
21
|
pathFor(target: 'memory' | 'user'): string;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
1
|
+
import { dirname, join } from "node:path";
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
3
3
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
4
4
|
import lockfile from "proper-lockfile";
|
|
5
5
|
//#region src/agent/memory/builtin-memory-store.ts
|
|
6
6
|
/**
|
|
7
|
-
* Bounded curated memory: agent home `memories/MEMORY.md` + `
|
|
7
|
+
* Bounded curated memory: agent home `memories/MEMORY.md` + global `user/MEMORY.md`, §-delimited entries.
|
|
8
8
|
* Snapshot for system prompt is captured at load time and not mutated until next load.
|
|
9
9
|
*/
|
|
10
10
|
const MEMORY_ENTRY_DELIMITER = "\n§\n";
|
|
@@ -20,7 +20,7 @@ var BuiltinMemoryStore = class {
|
|
|
20
20
|
constructor(config) {
|
|
21
21
|
this.config = config;
|
|
22
22
|
}
|
|
23
|
-
/** When false,
|
|
23
|
+
/** When false, global user memory is omitted from the snapshot and should not be edited via `curated_memory`. */
|
|
24
24
|
isUserProfileEnabled() {
|
|
25
25
|
return this.config.userProfileEnabled !== false;
|
|
26
26
|
}
|
|
@@ -28,12 +28,16 @@ var BuiltinMemoryStore = class {
|
|
|
28
28
|
get memoriesDir() {
|
|
29
29
|
return this.config.memoriesDir;
|
|
30
30
|
}
|
|
31
|
+
/** Global user memory file (`~/.xopc/user/MEMORY.md`). */
|
|
32
|
+
get userMemoryPath() {
|
|
33
|
+
return this.config.userMemoryPath;
|
|
34
|
+
}
|
|
31
35
|
/** Workspace whose MEMORY.md and daily notes participate in local memory search. */
|
|
32
36
|
get workspaceDir() {
|
|
33
37
|
return this.config.workspaceDir;
|
|
34
38
|
}
|
|
35
39
|
pathFor(target) {
|
|
36
|
-
return
|
|
40
|
+
return target === "memory" ? join(this.memoriesDir, "MEMORY.md") : this.userMemoryPath;
|
|
37
41
|
}
|
|
38
42
|
/**
|
|
39
43
|
* Load from disk and freeze {@link getSnapshot}. Sync for agent creation (prefix cache stability).
|
|
@@ -41,7 +45,7 @@ var BuiltinMemoryStore = class {
|
|
|
41
45
|
loadFromDiskSync() {
|
|
42
46
|
mkdirSync(this.memoriesDir, { recursive: true });
|
|
43
47
|
this.memoryEntries = this.parseFileContent(this.readPathSync(join(this.memoriesDir, "MEMORY.md")));
|
|
44
|
-
this.userEntries = this.config.userProfileEnabled === false ? [] : this.parseFileContent(this.readPathSync(
|
|
48
|
+
this.userEntries = this.config.userProfileEnabled === false ? [] : this.parseFileContent(this.readPathSync(this.userMemoryPath));
|
|
45
49
|
this.memoryEntries = dedupePreserveOrder(this.memoryEntries);
|
|
46
50
|
this.userEntries = dedupePreserveOrder(this.userEntries);
|
|
47
51
|
this.snapshot = {
|
|
@@ -185,7 +189,7 @@ var BuiltinMemoryStore = class {
|
|
|
185
189
|
}
|
|
186
190
|
async withFileLock(target, fn) {
|
|
187
191
|
const filePath = this.pathFor(target);
|
|
188
|
-
await mkdir(
|
|
192
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
189
193
|
if (!existsSync(filePath)) await writeFile(filePath, "", "utf-8");
|
|
190
194
|
await lockfile.lock(filePath, {
|
|
191
195
|
retries: 3,
|
|
@@ -214,6 +218,7 @@ var BuiltinMemoryStore = class {
|
|
|
214
218
|
const filePath = this.pathFor(target);
|
|
215
219
|
const content = entries.join(MEMORY_ENTRY_DELIMITER);
|
|
216
220
|
const tmpPath = `${filePath}.tmp.${Date.now()}`;
|
|
221
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
217
222
|
await writeFile(tmpPath, content, "utf-8");
|
|
218
223
|
await rename(tmpPath, filePath);
|
|
219
224
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtin-memory-store.js","names":[],"sources":["../../../../src/agent/memory/builtin-memory-store.ts"],"sourcesContent":["/**\n * Bounded curated memory: agent home `memories/MEMORY.md` + `USER.md`, §-delimited entries.\n * Snapshot for system prompt is captured at load time and not mutated until next load.\n */\n\nimport { existsSync, mkdirSync, readFileSync } from 'node:fs';\nimport { mkdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport lockfile from 'proper-lockfile';\n\nimport type { MemorySnapshot, MemoryStoreConfig } from './types.js';\n\nexport const MEMORY_ENTRY_DELIMITER = '\\n§\\n';\n\nconst DEFAULT_MEMORY_LIMIT = 2200;\nconst DEFAULT_USER_LIMIT = 1375;\n\nexport class BuiltinMemoryStore {\n private memoryEntries: string[] = [];\n private userEntries: string[] = [];\n private snapshot: MemorySnapshot = { memory: '', user: '' };\n\n constructor(private readonly config: MemoryStoreConfig) {}\n\n /** When false, USER.md is omitted from the snapshot and should not be edited via `curated_memory`. */\n isUserProfileEnabled(): boolean {\n return this.config.userProfileEnabled !== false;\n }\n\n /** Agent-scoped curated memory directory (`~/.xopc/agents/<id>/memories/`). */\n get memoriesDir(): string {\n return this.config.memoriesDir;\n }\n\n /** Workspace whose MEMORY.md and daily notes participate in local memory search. */\n get workspaceDir(): string {\n return this.config.workspaceDir;\n }\n\n pathFor(target: 'memory' | 'user'): string {\n return join(this.memoriesDir, target === 'memory' ? 'MEMORY.md' : 'USER.md');\n }\n\n /**\n * Load from disk and freeze {@link getSnapshot}. Sync for agent creation (prefix cache stability).\n */\n loadFromDiskSync(): void {\n mkdirSync(this.memoriesDir, { recursive: true });\n this.memoryEntries = this.parseFileContent(\n this.readPathSync(join(this.memoriesDir, 'MEMORY.md')),\n );\n this.userEntries =\n this.config.userProfileEnabled === false\n ? []\n : this.parseFileContent(this.readPathSync(join(this.memoriesDir, 'USER.md')));\n this.memoryEntries = dedupePreserveOrder(this.memoryEntries);\n this.userEntries = dedupePreserveOrder(this.userEntries);\n this.snapshot = {\n memory: this.renderBlock('memory', this.memoryEntries),\n user: this.renderBlock('user', this.userEntries),\n };\n }\n\n private readPathSync(path: string): string {\n try {\n if (!existsSync(path)) return '';\n return readFileSync(path, { encoding: 'utf-8' });\n } catch {\n return '';\n }\n }\n\n private parseFileContent(raw: string): string[] {\n if (!raw.trim()) return [];\n return raw\n .split(MEMORY_ENTRY_DELIMITER)\n .map((e) => e.trim())\n .filter(Boolean);\n }\n\n getSnapshot(): MemorySnapshot {\n return this.snapshot;\n }\n\n /**\n * Live entries for read/debug (may differ from snapshot after mutations).\n */\n getLiveEntries(target: 'memory' | 'user'): string[] {\n return target === 'memory' ? [...this.memoryEntries] : [...this.userEntries];\n }\n\n async add(\n target: 'memory' | 'user',\n content: string,\n ): Promise<{ success: boolean; error?: string; message?: string }> {\n const trimmed = content.trim();\n if (!trimmed) {\n return { success: false, error: 'Content cannot be empty.' };\n }\n const scanError = scanForThreats(trimmed);\n if (scanError) {\n return { success: false, error: scanError };\n }\n\n return this.withFileLock(target, async () => {\n await this.reloadTargetFromDisk(target);\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const limit = this.charLimit(target);\n\n if (entries.includes(trimmed)) {\n return { success: false, error: 'Entry already exists' };\n }\n\n const newTotal = calculateTotal([...entries, trimmed]);\n if (newTotal > limit) {\n return {\n success: false,\n error: `Memory at ${calculateTotal(entries)}/${limit} chars. Adding would exceed limit.`,\n };\n }\n\n entries.push(trimmed);\n await this.flushTargetToDisk(target);\n return { success: true, message: 'Entry added.' };\n });\n }\n\n async replace(\n target: 'memory' | 'user',\n oldText: string,\n newContent: string,\n ): Promise<{ success: boolean; error?: string; message?: string }> {\n const ot = oldText.trim();\n const nc = newContent.trim();\n if (!ot) {\n return { success: false, error: 'old_text cannot be empty.' };\n }\n if (!nc) {\n return { success: false, error: 'new_content cannot be empty. Use remove to delete entries.' };\n }\n const scanError = scanForThreats(nc);\n if (scanError) {\n return { success: false, error: scanError };\n }\n\n return this.withFileLock(target, async () => {\n await this.reloadTargetFromDisk(target);\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const matches: Array<{ index: number; entry: string }> = [];\n for (let i = 0; i < entries.length; i++) {\n if (entries[i].includes(ot)) {\n matches.push({ index: i, entry: entries[i] });\n }\n }\n\n if (matches.length === 0) {\n return { success: false, error: `No entry matched '${ot}'.` };\n }\n\n if (matches.length > 1) {\n const uniqueTexts = new Set(matches.map((m) => m.entry));\n if (uniqueTexts.size > 1) {\n const previews = matches.map((m) =>\n m.entry.length > 80 ? `${m.entry.slice(0, 80)}...` : m.entry,\n );\n return {\n success: false,\n error: `Multiple entries matched '${ot}'. Be more specific. Previews: ${previews.join(' | ')}`,\n };\n }\n }\n\n const idx = matches[0].index;\n const limit = this.charLimit(target);\n const testEntries = [...entries];\n testEntries[idx] = nc;\n\n if (calculateTotal(testEntries) > limit) {\n return {\n success: false,\n error: `Replacement would put memory at ${calculateTotal(testEntries)}/${limit} chars.`,\n };\n }\n\n entries[idx] = nc;\n await this.flushTargetToDisk(target);\n return { success: true, message: 'Entry replaced.' };\n });\n }\n\n async remove(\n target: 'memory' | 'user',\n oldText: string,\n ): Promise<{ success: boolean; error?: string; message?: string }> {\n const ot = oldText.trim();\n if (!ot) {\n return { success: false, error: 'old_text cannot be empty.' };\n }\n\n return this.withFileLock(target, async () => {\n await this.reloadTargetFromDisk(target);\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const matches: number[] = [];\n for (let i = 0; i < entries.length; i++) {\n if (entries[i].includes(ot)) {\n matches.push(i);\n }\n }\n\n if (matches.length === 0) {\n return { success: false, error: `No entry matched '${ot}'.` };\n }\n\n if (matches.length > 1) {\n const uniqueTexts = new Set(matches.map((i) => entries[i]));\n if (uniqueTexts.size > 1) {\n const previews = matches.map((i) => {\n const e = entries[i];\n return e.length > 80 ? `${e.slice(0, 80)}...` : e;\n });\n return {\n success: false,\n error: `Multiple entries matched '${ot}'. Be more specific. Previews: ${previews.join(' | ')}`,\n };\n }\n }\n\n entries.splice(matches[0], 1);\n await this.flushTargetToDisk(target);\n return { success: true, message: 'Entry removed.' };\n });\n }\n\n private async withFileLock<T>(\n target: 'memory' | 'user',\n fn: () => Promise<T>,\n ): Promise<T> {\n const filePath = this.pathFor(target);\n await mkdir(this.memoriesDir, { recursive: true });\n if (!existsSync(filePath)) {\n await writeFile(filePath, '', 'utf-8');\n }\n await lockfile.lock(filePath, { retries: 3, stale: 10_000 });\n try {\n return await fn();\n } finally {\n await lockfile.unlock(filePath);\n }\n }\n\n private async reloadTargetFromDisk(target: 'memory' | 'user'): Promise<void> {\n const path = this.pathFor(target);\n let raw = '';\n try {\n raw = await readFile(path, 'utf-8');\n } catch {\n raw = '';\n }\n const parsed = dedupePreserveOrder(this.parseFileContent(raw));\n if (target === 'memory') {\n this.memoryEntries = parsed;\n } else {\n this.userEntries = parsed;\n }\n }\n\n private async flushTargetToDisk(target: 'memory' | 'user'): Promise<void> {\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const filePath = this.pathFor(target);\n const content = entries.join(MEMORY_ENTRY_DELIMITER);\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await writeFile(tmpPath, content, 'utf-8');\n await rename(tmpPath, filePath);\n }\n\n private charLimit(target: 'memory' | 'user'): number {\n return target === 'memory'\n ? (this.config.memoryCharLimit ?? DEFAULT_MEMORY_LIMIT)\n : (this.config.userCharLimit ?? DEFAULT_USER_LIMIT);\n }\n\n private renderBlock(target: 'memory' | 'user', entries: string[]): string {\n if (entries.length === 0) {\n return '';\n }\n\n const limit = this.charLimit(target);\n const content = entries.join(MEMORY_ENTRY_DELIMITER);\n const current = content.length;\n const pct = Math.min(100, Math.round((current / limit) * 100));\n\n const header =\n target === 'user'\n ? `USER PROFILE (who the user is) [${pct}% — ${current.toLocaleString()}/${limit.toLocaleString()} chars]`\n : `MEMORY (your personal notes) [${pct}% — ${current.toLocaleString()}/${limit.toLocaleString()} chars]`;\n\n const separator = '═'.repeat(46);\n return `${separator}\\n${header}\\n${separator}\\n${content}`;\n }\n}\n\nfunction dedupePreserveOrder(entries: string[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const e of entries) {\n if (seen.has(e)) {\n continue;\n }\n seen.add(e);\n out.push(e);\n }\n return out;\n}\n\nfunction calculateTotal(entries: string[]): number {\n if (entries.length === 0) {\n return 0;\n }\n return entries.join(MEMORY_ENTRY_DELIMITER).length;\n}\n\nexport function scanForThreats(content: string): string | null {\n const patterns: Array<{ regex: RegExp; id: string }> = [\n { regex: /ignore\\s+(previous|all|above|prior)\\s+instructions/i, id: 'prompt_injection' },\n { regex: /you\\s+are\\s+now\\s+/i, id: 'role_hijack' },\n { regex: /do\\s+not\\s+tell\\s+the\\s+user/i, id: 'deception_hide' },\n { regex: /system\\s+prompt\\s+override/i, id: 'sys_prompt_override' },\n {\n regex: /disregard\\s+(your|all|any)\\s+(instructions|rules|guidelines)/i,\n id: 'disregard_rules',\n },\n {\n regex: /act\\s+as\\s+(if|though)\\s+you\\s+(have\\s+no|don't\\s+have)\\s+(restrictions|limits|rules)/i,\n id: 'bypass_restrictions',\n },\n { regex: /curl\\s+[^\\n]*\\$\\{?\\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)/i, id: 'exfil_curl' },\n { regex: /wget\\s+[^\\n]*\\$\\{?\\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)/i, id: 'exfil_wget' },\n {\n regex: /cat\\s+[^\\n]*(\\.env|credentials|\\.netrc|\\.pgpass|\\.npmrc|\\.pypirc)/i,\n id: 'read_secrets',\n },\n { regex: /authorized_keys/i, id: 'ssh_backdoor' },\n { regex: /\\$HOME\\/\\.ssh|\\~\\/\\.ssh/i, id: 'ssh_access' },\n ];\n\n for (const { regex, id } of patterns) {\n if (regex.test(content)) {\n return `Blocked: content matches threat pattern '${id}'. Memory entries are injected into the system prompt and must not contain injection payloads.`;\n }\n }\n\n const invisibleChars = [\n '\\u200b',\n '\\u200c',\n '\\u200d',\n '\\u2060',\n '\\ufeff',\n '\\u202a',\n '\\u202b',\n '\\u202c',\n '\\u202d',\n '\\u202e',\n ];\n for (const char of invisibleChars) {\n if (content.includes(char)) {\n const code = char.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0');\n return `Blocked: content contains invisible unicode character U+${code} (possible injection).`;\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"builtin-memory-store.js","names":[],"sources":["../../../../src/agent/memory/builtin-memory-store.ts"],"sourcesContent":["/**\n * Bounded curated memory: agent home `memories/MEMORY.md` + global `user/MEMORY.md`, §-delimited entries.\n * Snapshot for system prompt is captured at load time and not mutated until next load.\n */\n\nimport { existsSync, mkdirSync, readFileSync } from 'node:fs';\nimport { mkdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport lockfile from 'proper-lockfile';\n\nimport type { MemorySnapshot, MemoryStoreConfig } from './types.js';\n\nexport const MEMORY_ENTRY_DELIMITER = '\\n§\\n';\n\nconst DEFAULT_MEMORY_LIMIT = 2200;\nconst DEFAULT_USER_LIMIT = 1375;\n\nexport class BuiltinMemoryStore {\n private memoryEntries: string[] = [];\n private userEntries: string[] = [];\n private snapshot: MemorySnapshot = { memory: '', user: '' };\n\n constructor(private readonly config: MemoryStoreConfig) {}\n\n /** When false, global user memory is omitted from the snapshot and should not be edited via `curated_memory`. */\n isUserProfileEnabled(): boolean {\n return this.config.userProfileEnabled !== false;\n }\n\n /** Agent-scoped curated memory directory (`~/.xopc/agents/<id>/memories/`). */\n get memoriesDir(): string {\n return this.config.memoriesDir;\n }\n\n /** Global user memory file (`~/.xopc/user/MEMORY.md`). */\n get userMemoryPath(): string {\n return this.config.userMemoryPath;\n }\n\n /** Workspace whose MEMORY.md and daily notes participate in local memory search. */\n get workspaceDir(): string {\n return this.config.workspaceDir;\n }\n\n pathFor(target: 'memory' | 'user'): string {\n return target === 'memory' ? join(this.memoriesDir, 'MEMORY.md') : this.userMemoryPath;\n }\n\n /**\n * Load from disk and freeze {@link getSnapshot}. Sync for agent creation (prefix cache stability).\n */\n loadFromDiskSync(): void {\n mkdirSync(this.memoriesDir, { recursive: true });\n this.memoryEntries = this.parseFileContent(\n this.readPathSync(join(this.memoriesDir, 'MEMORY.md')),\n );\n this.userEntries =\n this.config.userProfileEnabled === false\n ? []\n : this.parseFileContent(this.readPathSync(this.userMemoryPath));\n this.memoryEntries = dedupePreserveOrder(this.memoryEntries);\n this.userEntries = dedupePreserveOrder(this.userEntries);\n this.snapshot = {\n memory: this.renderBlock('memory', this.memoryEntries),\n user: this.renderBlock('user', this.userEntries),\n };\n }\n\n private readPathSync(path: string): string {\n try {\n if (!existsSync(path)) return '';\n return readFileSync(path, { encoding: 'utf-8' });\n } catch {\n return '';\n }\n }\n\n private parseFileContent(raw: string): string[] {\n if (!raw.trim()) return [];\n return raw\n .split(MEMORY_ENTRY_DELIMITER)\n .map((e) => e.trim())\n .filter(Boolean);\n }\n\n getSnapshot(): MemorySnapshot {\n return this.snapshot;\n }\n\n /**\n * Live entries for read/debug (may differ from snapshot after mutations).\n */\n getLiveEntries(target: 'memory' | 'user'): string[] {\n return target === 'memory' ? [...this.memoryEntries] : [...this.userEntries];\n }\n\n async add(\n target: 'memory' | 'user',\n content: string,\n ): Promise<{ success: boolean; error?: string; message?: string }> {\n const trimmed = content.trim();\n if (!trimmed) {\n return { success: false, error: 'Content cannot be empty.' };\n }\n const scanError = scanForThreats(trimmed);\n if (scanError) {\n return { success: false, error: scanError };\n }\n\n return this.withFileLock(target, async () => {\n await this.reloadTargetFromDisk(target);\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const limit = this.charLimit(target);\n\n if (entries.includes(trimmed)) {\n return { success: false, error: 'Entry already exists' };\n }\n\n const newTotal = calculateTotal([...entries, trimmed]);\n if (newTotal > limit) {\n return {\n success: false,\n error: `Memory at ${calculateTotal(entries)}/${limit} chars. Adding would exceed limit.`,\n };\n }\n\n entries.push(trimmed);\n await this.flushTargetToDisk(target);\n return { success: true, message: 'Entry added.' };\n });\n }\n\n async replace(\n target: 'memory' | 'user',\n oldText: string,\n newContent: string,\n ): Promise<{ success: boolean; error?: string; message?: string }> {\n const ot = oldText.trim();\n const nc = newContent.trim();\n if (!ot) {\n return { success: false, error: 'old_text cannot be empty.' };\n }\n if (!nc) {\n return { success: false, error: 'new_content cannot be empty. Use remove to delete entries.' };\n }\n const scanError = scanForThreats(nc);\n if (scanError) {\n return { success: false, error: scanError };\n }\n\n return this.withFileLock(target, async () => {\n await this.reloadTargetFromDisk(target);\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const matches: Array<{ index: number; entry: string }> = [];\n for (let i = 0; i < entries.length; i++) {\n if (entries[i].includes(ot)) {\n matches.push({ index: i, entry: entries[i] });\n }\n }\n\n if (matches.length === 0) {\n return { success: false, error: `No entry matched '${ot}'.` };\n }\n\n if (matches.length > 1) {\n const uniqueTexts = new Set(matches.map((m) => m.entry));\n if (uniqueTexts.size > 1) {\n const previews = matches.map((m) =>\n m.entry.length > 80 ? `${m.entry.slice(0, 80)}...` : m.entry,\n );\n return {\n success: false,\n error: `Multiple entries matched '${ot}'. Be more specific. Previews: ${previews.join(' | ')}`,\n };\n }\n }\n\n const idx = matches[0].index;\n const limit = this.charLimit(target);\n const testEntries = [...entries];\n testEntries[idx] = nc;\n\n if (calculateTotal(testEntries) > limit) {\n return {\n success: false,\n error: `Replacement would put memory at ${calculateTotal(testEntries)}/${limit} chars.`,\n };\n }\n\n entries[idx] = nc;\n await this.flushTargetToDisk(target);\n return { success: true, message: 'Entry replaced.' };\n });\n }\n\n async remove(\n target: 'memory' | 'user',\n oldText: string,\n ): Promise<{ success: boolean; error?: string; message?: string }> {\n const ot = oldText.trim();\n if (!ot) {\n return { success: false, error: 'old_text cannot be empty.' };\n }\n\n return this.withFileLock(target, async () => {\n await this.reloadTargetFromDisk(target);\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const matches: number[] = [];\n for (let i = 0; i < entries.length; i++) {\n if (entries[i].includes(ot)) {\n matches.push(i);\n }\n }\n\n if (matches.length === 0) {\n return { success: false, error: `No entry matched '${ot}'.` };\n }\n\n if (matches.length > 1) {\n const uniqueTexts = new Set(matches.map((i) => entries[i]));\n if (uniqueTexts.size > 1) {\n const previews = matches.map((i) => {\n const e = entries[i];\n return e.length > 80 ? `${e.slice(0, 80)}...` : e;\n });\n return {\n success: false,\n error: `Multiple entries matched '${ot}'. Be more specific. Previews: ${previews.join(' | ')}`,\n };\n }\n }\n\n entries.splice(matches[0], 1);\n await this.flushTargetToDisk(target);\n return { success: true, message: 'Entry removed.' };\n });\n }\n\n private async withFileLock<T>(\n target: 'memory' | 'user',\n fn: () => Promise<T>,\n ): Promise<T> {\n const filePath = this.pathFor(target);\n await mkdir(dirname(filePath), { recursive: true });\n if (!existsSync(filePath)) {\n await writeFile(filePath, '', 'utf-8');\n }\n await lockfile.lock(filePath, { retries: 3, stale: 10_000 });\n try {\n return await fn();\n } finally {\n await lockfile.unlock(filePath);\n }\n }\n\n private async reloadTargetFromDisk(target: 'memory' | 'user'): Promise<void> {\n const path = this.pathFor(target);\n let raw = '';\n try {\n raw = await readFile(path, 'utf-8');\n } catch {\n raw = '';\n }\n const parsed = dedupePreserveOrder(this.parseFileContent(raw));\n if (target === 'memory') {\n this.memoryEntries = parsed;\n } else {\n this.userEntries = parsed;\n }\n }\n\n private async flushTargetToDisk(target: 'memory' | 'user'): Promise<void> {\n const entries = target === 'memory' ? this.memoryEntries : this.userEntries;\n const filePath = this.pathFor(target);\n const content = entries.join(MEMORY_ENTRY_DELIMITER);\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(tmpPath, content, 'utf-8');\n await rename(tmpPath, filePath);\n }\n\n private charLimit(target: 'memory' | 'user'): number {\n return target === 'memory'\n ? (this.config.memoryCharLimit ?? DEFAULT_MEMORY_LIMIT)\n : (this.config.userCharLimit ?? DEFAULT_USER_LIMIT);\n }\n\n private renderBlock(target: 'memory' | 'user', entries: string[]): string {\n if (entries.length === 0) {\n return '';\n }\n\n const limit = this.charLimit(target);\n const content = entries.join(MEMORY_ENTRY_DELIMITER);\n const current = content.length;\n const pct = Math.min(100, Math.round((current / limit) * 100));\n\n const header =\n target === 'user'\n ? `USER PROFILE (who the user is) [${pct}% — ${current.toLocaleString()}/${limit.toLocaleString()} chars]`\n : `MEMORY (your personal notes) [${pct}% — ${current.toLocaleString()}/${limit.toLocaleString()} chars]`;\n\n const separator = '═'.repeat(46);\n return `${separator}\\n${header}\\n${separator}\\n${content}`;\n }\n}\n\nfunction dedupePreserveOrder(entries: string[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const e of entries) {\n if (seen.has(e)) {\n continue;\n }\n seen.add(e);\n out.push(e);\n }\n return out;\n}\n\nfunction calculateTotal(entries: string[]): number {\n if (entries.length === 0) {\n return 0;\n }\n return entries.join(MEMORY_ENTRY_DELIMITER).length;\n}\n\nexport function scanForThreats(content: string): string | null {\n const patterns: Array<{ regex: RegExp; id: string }> = [\n { regex: /ignore\\s+(previous|all|above|prior)\\s+instructions/i, id: 'prompt_injection' },\n { regex: /you\\s+are\\s+now\\s+/i, id: 'role_hijack' },\n { regex: /do\\s+not\\s+tell\\s+the\\s+user/i, id: 'deception_hide' },\n { regex: /system\\s+prompt\\s+override/i, id: 'sys_prompt_override' },\n {\n regex: /disregard\\s+(your|all|any)\\s+(instructions|rules|guidelines)/i,\n id: 'disregard_rules',\n },\n {\n regex: /act\\s+as\\s+(if|though)\\s+you\\s+(have\\s+no|don't\\s+have)\\s+(restrictions|limits|rules)/i,\n id: 'bypass_restrictions',\n },\n { regex: /curl\\s+[^\\n]*\\$\\{?\\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)/i, id: 'exfil_curl' },\n { regex: /wget\\s+[^\\n]*\\$\\{?\\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)/i, id: 'exfil_wget' },\n {\n regex: /cat\\s+[^\\n]*(\\.env|credentials|\\.netrc|\\.pgpass|\\.npmrc|\\.pypirc)/i,\n id: 'read_secrets',\n },\n { regex: /authorized_keys/i, id: 'ssh_backdoor' },\n { regex: /\\$HOME\\/\\.ssh|\\~\\/\\.ssh/i, id: 'ssh_access' },\n ];\n\n for (const { regex, id } of patterns) {\n if (regex.test(content)) {\n return `Blocked: content matches threat pattern '${id}'. Memory entries are injected into the system prompt and must not contain injection payloads.`;\n }\n }\n\n const invisibleChars = [\n '\\u200b',\n '\\u200c',\n '\\u200d',\n '\\u2060',\n '\\ufeff',\n '\\u202a',\n '\\u202b',\n '\\u202c',\n '\\u202d',\n '\\u202e',\n ];\n for (const char of invisibleChars) {\n if (content.includes(char)) {\n const code = char.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0');\n return `Blocked: content contains invisible unicode character U+${code} (possible injection).`;\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
|
|
@@ -61,6 +61,7 @@ var BuiltinMemoryProvider = class {
|
|
|
61
61
|
maxResults: request.maxResults,
|
|
62
62
|
minScore: request.minScore,
|
|
63
63
|
memoriesDir: this.store.memoriesDir,
|
|
64
|
+
userMemoryPath: this.store.userMemoryPath,
|
|
64
65
|
agentId
|
|
65
66
|
})).map((entry) => {
|
|
66
67
|
const start = entry.lineNumbers[0] ?? 1;
|
|
@@ -100,7 +101,7 @@ var BuiltinMemoryProvider = class {
|
|
|
100
101
|
const existing = getMemoryRecord(request.id);
|
|
101
102
|
if (existing) return { record: existing };
|
|
102
103
|
}
|
|
103
|
-
const result = memoryGet(this.workspaceDir, request.path ?? request.id ?? "", request.from, request.lines, this.store.memoriesDir);
|
|
104
|
+
const result = memoryGet(this.workspaceDir, request.path ?? request.id ?? "", request.from, request.lines, this.store.memoriesDir, this.store.userMemoryPath);
|
|
104
105
|
if (!result) return null;
|
|
105
106
|
const path = request.path ?? request.id ?? "";
|
|
106
107
|
return {
|
|
@@ -148,12 +149,36 @@ var BuiltinMemoryProvider = class {
|
|
|
148
149
|
content,
|
|
149
150
|
source: {
|
|
150
151
|
provider: this.id,
|
|
151
|
-
path: target
|
|
152
|
+
path: sourcePathForTarget(target)
|
|
152
153
|
}
|
|
153
154
|
}));
|
|
154
155
|
}
|
|
155
156
|
async write(request) {
|
|
156
157
|
const target = request.target ?? (request.kind === "user_profile" ? "user" : "memory");
|
|
158
|
+
if (request.status === "candidate") return {
|
|
159
|
+
success: true,
|
|
160
|
+
message: "Memory candidate added to inbox",
|
|
161
|
+
record: upsertMemoryRecord({
|
|
162
|
+
providerId: this.id,
|
|
163
|
+
kind: target === "user" ? "user_profile" : request.kind,
|
|
164
|
+
agentId: request.scope?.agentId ?? this.agentId,
|
|
165
|
+
workspaceId: request.scope?.workspaceId ?? this.workspaceDir,
|
|
166
|
+
sessionKey: request.scope?.sessionKey,
|
|
167
|
+
content: request.content,
|
|
168
|
+
source: {
|
|
169
|
+
...request.source ?? {},
|
|
170
|
+
provider: this.id,
|
|
171
|
+
path: sourcePathForTarget(target)
|
|
172
|
+
},
|
|
173
|
+
confidence: request.confidence,
|
|
174
|
+
tags: request.tags,
|
|
175
|
+
status: "candidate",
|
|
176
|
+
sensitivity: request.sensitivity,
|
|
177
|
+
evidence: request.evidence,
|
|
178
|
+
reviewAfter: request.reviewAfter,
|
|
179
|
+
expiresAt: request.expiresAt
|
|
180
|
+
})
|
|
181
|
+
};
|
|
157
182
|
const result = await this.store.add(target, request.content);
|
|
158
183
|
if (!result.success) return {
|
|
159
184
|
success: false,
|
|
@@ -173,7 +198,7 @@ var BuiltinMemoryProvider = class {
|
|
|
173
198
|
source: {
|
|
174
199
|
...request.source ?? {},
|
|
175
200
|
provider: this.id,
|
|
176
|
-
path: target
|
|
201
|
+
path: sourcePathForTarget(target)
|
|
177
202
|
},
|
|
178
203
|
tags: request.tags
|
|
179
204
|
})
|
|
@@ -198,7 +223,7 @@ var BuiltinMemoryProvider = class {
|
|
|
198
223
|
content: request.content,
|
|
199
224
|
source: existing?.source ?? {
|
|
200
225
|
provider: this.id,
|
|
201
|
-
path: target
|
|
226
|
+
path: sourcePathForTarget(target)
|
|
202
227
|
},
|
|
203
228
|
tags: existing?.tags
|
|
204
229
|
});
|
|
@@ -262,14 +287,18 @@ var BuiltinMemoryProvider = class {
|
|
|
262
287
|
async shutdown() {}
|
|
263
288
|
};
|
|
264
289
|
function inferKindFromPath(path) {
|
|
265
|
-
|
|
266
|
-
if (
|
|
267
|
-
if (/^memory\/\d{4}-\d{2}-\d{2}\.md$/.test(
|
|
290
|
+
const normalized = path.replace(/\\/g, "/");
|
|
291
|
+
if (normalized === "user/MEMORY.md" || normalized.endsWith("/user/MEMORY.md")) return "user_profile";
|
|
292
|
+
if (/^memory\/\d{4}-\d{2}-\d{2}\.md$/.test(normalized)) return "daily_note";
|
|
293
|
+
if (normalized.endsWith("MEMORY.md")) return "agent_note";
|
|
268
294
|
return "workspace_fact";
|
|
269
295
|
}
|
|
270
296
|
function targetForKind(kind) {
|
|
271
297
|
return kind === "user_profile" ? "user" : "memory";
|
|
272
298
|
}
|
|
299
|
+
function sourcePathForTarget(target) {
|
|
300
|
+
return target === "user" ? "user/MEMORY.md" : "MEMORY.md";
|
|
301
|
+
}
|
|
273
302
|
function parseLineAddress(id) {
|
|
274
303
|
const match = /^(.*)#L(\d+)(?:-L(\d+))?$/.exec(id);
|
|
275
304
|
if (!match) return { path: id };
|