@xopcai/xopc 0.0.118 → 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 +2 -1
- package/README.zh-CN.md +2 -1
- 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-dzUrQoeZ.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--wMEL00f.js → apps-page-DU0dz9Zo.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-Cki4SfU2.js → archive-plugin-BkVIgZzA.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-D-KnS2Do.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-D2sgvJja.js → binary-plugins-B4TO_bsn.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-DIq-4Gvb.js → block-editor-DJRWwWNI.js} +28 -28
- 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-DYu1KaFQ.js → extension-debug-page-DYh402XV.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-B3W6QwlZ.js → extension-page-Cr_TizrN.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-D7VQ-cxE.js → extension-settings-page-BU1KoKRj.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-B7xDPu-R.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-BTBLSeUu.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-CYj9FWCp.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-CQQgzPr3.js → goals-config-api-DqxVi0p5.js} +1 -1
- package/dist/gateway/static/root/assets/{goals-page-BHitvJAv.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-C2Uqvae9.js → logs-page-DLXPcksQ.js} +2 -2
- package/dist/gateway/static/root/assets/{markdown-split-BwOFrJ2E.js → markdown-split-CpHxbtWJ.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-Cnz6Q5G6.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-B-Svpmza.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-Bss4TiwP.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-BRNjdgKq.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-Dub9sy8r.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-BeFLavGi.js → settings-collapsible-section-BouO6JTi.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-dmyaPOTG.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-C8glrdqe.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-DqlWTsDb.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-C73IVZeC.js → voice-api-key-field-CeEevm7z.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-player-store-C_ArLJPM.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 +4 -8
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/embedded/run-for-session.js +102 -14
- 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.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/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/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 +0 -12
- 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/service.d.ts +1 -9
- package/dist/src/agent/service.js +3 -40
- 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/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-manifest/resolver.d.ts +1 -0
- package/dist/src/agent-manifest/resolver.js +1 -0
- package/dist/src/agent-manifest/resolver.js.map +1 -1
- package/dist/src/agent-manifest/schema.d.ts +6 -0
- package/dist/src/agent-manifest/schema.js +12 -3
- package/dist/src/agent-manifest/schema.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/plugin-types.d.ts +3 -0
- 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.js +0 -4
- 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/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 +26 -8
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/tui.js +24 -2
- package/dist/src/cli/commands/tui.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 +40 -2
- package/dist/src/commands/agents.config.js.map +1 -1
- package/dist/src/config/agent-profile.js +4 -3
- package/dist/src/config/agent-profile.js.map +1 -1
- 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 +0 -9
- package/dist/src/config/paths.js +1 -14
- 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 +13 -12
- package/dist/src/config/schema.js +22 -26
- 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 +1 -0
- package/dist/src/gateway/agents-admin.js +13 -15
- 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/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/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.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/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 +10 -6
- package/dist/src/gateway/hono/routes/lazy-bundles.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/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/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 +24 -1
- package/dist/src/notes/service.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/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 +0 -2
- package/dist/src/storage/sqlite/index.js +1 -3
- 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 +6 -0
- package/dist/src/tui/backends/embedded-backend.js +18 -7
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +3 -0
- package/dist/src/tui/backends/gateway-sse-backend.js +14 -0
- package/dist/src/tui/backends/gateway-sse-backend.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-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 +7 -2
- 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-BCxZbkOw.js +0 -2
- package/dist/gateway/static/root/assets/app-management-settings-panel-bxYJVJpx.js +0 -1
- package/dist/gateway/static/root/assets/appearance-settings-BCIykXlI.js +0 -1
- package/dist/gateway/static/root/assets/browser-settings-page-DRhKdn81.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-api-CorhiHlg.js +0 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-qFx9Jtce.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-BAwAkqIA.js +0 -1
- package/dist/gateway/static/root/assets/cn-JdwAhgNB.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-BQLcfitM.js +0 -2
- package/dist/gateway/static/root/assets/createLucideIcon-DPHK1VkS.js +0 -1
- package/dist/gateway/static/root/assets/cron-api-qGwmL-24.js +0 -1
- package/dist/gateway/static/root/assets/cron-dreaming-jobs-nCMWCf0p.js +0 -1
- package/dist/gateway/static/root/assets/cron-page-BBWMAj8-.js +0 -3
- package/dist/gateway/static/root/assets/dreaming-settings-Dw89Obml.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-BzxafKxR.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-Cmwy58rS.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-l7mu5GWX.js +0 -1
- package/dist/gateway/static/root/assets/goals-settings-CYVY4NGp.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-config-api-DriO_CN9.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-CUQTKxg7.js +0 -1
- package/dist/gateway/static/root/assets/index-C17Tsd_b.css +0 -1
- package/dist/gateway/static/root/assets/index-Cc41r16l.js +0 -191
- package/dist/gateway/static/root/assets/interval-presets-DFPCKW_W.js +0 -1
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-CcVcqstx.js +0 -1
- package/dist/gateway/static/root/assets/messages-B1iSKjiw.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-BfLfcta8.js +0 -2
- package/dist/gateway/static/root/assets/note-detail-page-BjwOQBfT.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-DktdBiwB.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-rfVx96fq.js +0 -1
- package/dist/gateway/static/root/assets/schedule-field-CUCKODde.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-DbtMFuuH.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-5re5gAnP.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-D-WMyLlj.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-Cl-DEJAU.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-CyPVvbLt.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-CleTH1oz.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-Ck8hdXqR.js +0 -2
- package/dist/gateway/static/root/assets/system-settings-panel-DI-LN_8G.js +0 -1
- package/dist/gateway/static/root/assets/typed-models-lib-kX9duch4.js +0 -1
- package/dist/gateway/static/root/assets/user-profile-settings-panel-B-UFPMXy.js +0 -1
- package/dist/gateway/static/root/assets/utils-Dm0hKalR.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-BtFGp2y_.js +0 -18
- 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
|
@@ -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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"}
|
|
@@ -17,7 +17,6 @@ var SelfVerifyMiddleware = class {
|
|
|
17
17
|
config;
|
|
18
18
|
turnCount = 0;
|
|
19
19
|
verificationRequested = false;
|
|
20
|
-
lastVerificationPrompt = "";
|
|
21
20
|
constructor(config = {}) {
|
|
22
21
|
this.config = {
|
|
23
22
|
...DEFAULT_CONFIG,
|
|
@@ -76,7 +75,6 @@ var SelfVerifyMiddleware = class {
|
|
|
76
75
|
this.fileEdits.clear();
|
|
77
76
|
this.turnCount = 0;
|
|
78
77
|
this.verificationRequested = false;
|
|
79
|
-
this.lastVerificationPrompt = "";
|
|
80
78
|
log.debug("Self-verify middleware reset");
|
|
81
79
|
}
|
|
82
80
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"self-verify.js","names":[],"sources":["../../../../src/agent/middleware/self-verify.ts"],"sourcesContent":["/**\n * Self-Verification Middleware\n *\n * Implements the \"Build & Self-Verify\" pattern from harness engineering:\n * - Intercepts agent before completion to remind verification\n * - Tracks file edits to detect potential issues\n * - Injects verification reminders into context\n *\n * Inspired by: https://blog.langchain.com/improving-deep-agents-with-harness-engineering/\n */\n\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('SelfVerifyMiddleware');\n\nexport interface FileEditRecord {\n path: string;\n editCount: number;\n lastEditTime: number;\n operations: string[]; // 'write', 'edit', 'shell' (for build commands)\n}\n\nexport interface SelfVerifyConfig {\n /** Max edits to same file before warning (default: 5) */\n maxEditsPerFile: number;\n /** Enable pre-completion verification check (default: true) */\n enablePreCompletionCheck: boolean;\n /** Min turns before triggering verification reminder (default: 3) */\n minTurnsForVerification: number;\n /** Reset counters on successful verification (default: true) */\n resetOnVerification: boolean;\n}\n\nconst DEFAULT_CONFIG: SelfVerifyConfig = {\n maxEditsPerFile: 5,\n enablePreCompletionCheck: true,\n minTurnsForVerification: 3,\n resetOnVerification: true,\n};\n\n/**\n * Tracks file modifications and provides verification guidance\n */\nexport class SelfVerifyMiddleware {\n private fileEdits: Map<string, FileEditRecord> = new Map();\n private config: SelfVerifyConfig;\n private turnCount = 0;\n private verificationRequested = false;\n
|
|
1
|
+
{"version":3,"file":"self-verify.js","names":[],"sources":["../../../../src/agent/middleware/self-verify.ts"],"sourcesContent":["/**\n * Self-Verification Middleware\n *\n * Implements the \"Build & Self-Verify\" pattern from harness engineering:\n * - Intercepts agent before completion to remind verification\n * - Tracks file edits to detect potential issues\n * - Injects verification reminders into context\n *\n * Inspired by: https://blog.langchain.com/improving-deep-agents-with-harness-engineering/\n */\n\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('SelfVerifyMiddleware');\n\nexport interface FileEditRecord {\n path: string;\n editCount: number;\n lastEditTime: number;\n operations: string[]; // 'write', 'edit', 'shell' (for build commands)\n}\n\nexport interface SelfVerifyConfig {\n /** Max edits to same file before warning (default: 5) */\n maxEditsPerFile: number;\n /** Enable pre-completion verification check (default: true) */\n enablePreCompletionCheck: boolean;\n /** Min turns before triggering verification reminder (default: 3) */\n minTurnsForVerification: number;\n /** Reset counters on successful verification (default: true) */\n resetOnVerification: boolean;\n}\n\nconst DEFAULT_CONFIG: SelfVerifyConfig = {\n maxEditsPerFile: 5,\n enablePreCompletionCheck: true,\n minTurnsForVerification: 3,\n resetOnVerification: true,\n};\n\n/**\n * Tracks file modifications and provides verification guidance\n */\nexport class SelfVerifyMiddleware {\n private fileEdits: Map<string, FileEditRecord> = new Map();\n private config: SelfVerifyConfig;\n private turnCount = 0;\n private verificationRequested = false;\n\n constructor(config: Partial<SelfVerifyConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Record a file edit operation\n */\n recordEdit(filePath: string, operation: 'write' | 'edit' | 'shell'): void {\n const now = Date.now();\n const existing = this.fileEdits.get(filePath);\n\n if (existing) {\n existing.editCount++;\n existing.lastEditTime = now;\n if (!existing.operations.includes(operation)) {\n existing.operations.push(operation);\n }\n } else {\n this.fileEdits.set(filePath, {\n path: filePath,\n editCount: 1,\n lastEditTime: now,\n operations: [operation],\n });\n }\n\n log.debug({ filePath, editCount: this.getEditCount(filePath), operation }, 'File edit recorded');\n }\n\n /**\n * Get edit count for a specific file\n */\n getEditCount(filePath: string): number {\n return this.fileEdits.get(filePath)?.editCount || 0;\n }\n\n /**\n * Check if any file has excessive edits (potential doom loop)\n */\n hasExcessiveEdits(): { file: string; count: number } | null {\n for (const [path, record] of this.fileEdits.entries()) {\n if (record.editCount >= this.config.maxEditsPerFile) {\n return { file: path, count: record.editCount };\n }\n }\n return null;\n }\n\n /**\n * Increment turn counter\n */\n onTurnStart(): void {\n this.turnCount++;\n this.verificationRequested = false;\n }\n\n /**\n * Reset all tracking (e.g., after successful task completion)\n */\n reset(): void {\n this.fileEdits.clear();\n this.turnCount = 0;\n this.verificationRequested = false;\n log.debug('Self-verify middleware reset');\n }\n\n /**\n * Get context injection for system prompt\n * Adds verification guidance based on current state\n */\n getContextInjection(): string {\n const sections: string[] = [];\n\n // Add workflow guidance\n sections.push(this.buildWorkflowGuidance());\n\n // Add excessive edit warning if needed\n const excessive = this.hasExcessiveEdits();\n if (excessive) {\n sections.push(this.buildExcessiveEditWarning(excessive.file, excessive.count));\n }\n\n // Add pre-completion check reminder for long sessions\n if (this.shouldPromptForVerification()) {\n sections.push(this.buildPreCompletionReminder());\n }\n\n return sections.filter(Boolean).join('\\n\\n');\n }\n\n /**\n * Check if we should inject verification reminder\n */\n private shouldPromptForVerification(): boolean {\n if (!this.config.enablePreCompletionCheck) return false;\n if (this.turnCount < this.config.minTurnsForVerification) return false;\n if (this.verificationRequested) return false;\n return true;\n }\n\n /**\n * Mark verification as requested (to avoid spam)\n */\n markVerificationRequested(): void {\n this.verificationRequested = true;\n }\n\n /**\n * Build workflow guidance section\n */\n private buildWorkflowGuidance(): string {\n return `## Problem Solving Workflow\n\nFollow this iterative process for all tasks:\n\n1. **Plan**: Understand the task, read relevant files, and create a plan\n2. **Build**: Implement your solution with verification in mind\n3. **Verify**: Test your work, run checks, compare against requirements\n4. **Fix**: If issues found, analyze and fix them\n\n**Important**: Before declaring a task complete:\n- Re-read the original requirements\n- Verify your solution meets ALL requirements\n- Run any available tests or validation\n- Check for edge cases\n\nDo not skip verification. Incomplete verification leads to incorrect solutions.`;\n }\n\n /**\n * Build warning for excessive file edits (doom loop detection)\n */\n private buildExcessiveEditWarning(filePath: string, count: number): string {\n return `⚠️ **Pattern Alert**: You have edited \"${filePath}\" ${count} times.\n\nThis may indicate:\n- You're fixing symptoms rather than root causes\n- The approach needs reconsideration\n- Requirements may be unclear\n\n**Recommendation**: \n- Step back and re-read the original task\n- Consider if there's a better approach\n- Verify you understand the requirements correctly`;\n }\n\n /**\n * Build pre-completion verification reminder\n */\n private buildPreCompletionReminder(): string {\n this.markVerificationRequested();\n return `⏳ **Verification Check**: You've made ${this.turnCount} turns on this task.\n\nBefore completing:\n1. ✅ Verify your solution matches the original requirements\n2. ✅ Run tests or validation if available\n3. ✅ Check edge cases and error handling\n4. ✅ Review your changes one final time\n\nIf you're confident, proceed. If unsure, continue refining.`;\n }\n\n /**\n * Get summary of file edits for debugging\n */\n getEditSummary(): { totalFiles: number; totalEdits: number; topFiles: Array<{ path: string; count: number }> } {\n const entries = Array.from(this.fileEdits.entries());\n const totalEdits = entries.reduce((sum, [, record]) => sum + record.editCount, 0);\n const topFiles = entries\n .sort((a, b) => b[1].editCount - a[1].editCount)\n .slice(0, 5)\n .map(([path, record]) => ({ path, count: record.editCount }));\n\n return {\n totalFiles: entries.length,\n totalEdits,\n topFiles,\n };\n }\n\n /**\n * Update configuration\n */\n setConfig(config: Partial<SelfVerifyConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n /**\n * Get current configuration\n */\n getConfig(): SelfVerifyConfig {\n return { ...this.config };\n }\n}\n"],"mappings":";;;aAWqD;AAErD,MAAM,MAAM,aAAa,uBAAuB;AAoBhD,MAAM,iBAAmC;CACvC,iBAAiB;CACjB,0BAA0B;CAC1B,yBAAyB;CACzB,qBAAqB;CACtB;;;;AAKD,IAAa,uBAAb,MAAkC;CAChC,4BAAiD,IAAI,KAAK;CAC1D;CACA,YAAoB;CACpB,wBAAgC;CAEhC,YAAY,SAAoC,EAAE,EAAE;AAClD,OAAK,SAAS;GAAE,GAAG;GAAgB,GAAG;GAAQ;;;;;CAMhD,WAAW,UAAkB,WAA6C;EACxE,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,WAAW,KAAK,UAAU,IAAI,SAAS;AAE7C,MAAI,UAAU;AACZ,YAAS;AACT,YAAS,eAAe;AACxB,OAAI,CAAC,SAAS,WAAW,SAAS,UAAU,CAC1C,UAAS,WAAW,KAAK,UAAU;QAGrC,MAAK,UAAU,IAAI,UAAU;GAC3B,MAAM;GACN,WAAW;GACX,cAAc;GACd,YAAY,CAAC,UAAU;GACxB,CAAC;AAGJ,MAAI,MAAM;GAAE;GAAU,WAAW,KAAK,aAAa,SAAS;GAAE;GAAW,EAAE,qBAAqB;;;;;CAMlG,aAAa,UAA0B;AACrC,SAAO,KAAK,UAAU,IAAI,SAAS,EAAE,aAAa;;;;;CAMpD,oBAA4D;AAC1D,OAAK,MAAM,CAAC,MAAM,WAAW,KAAK,UAAU,SAAS,CACnD,KAAI,OAAO,aAAa,KAAK,OAAO,gBAClC,QAAO;GAAE,MAAM;GAAM,OAAO,OAAO;GAAW;AAGlD,SAAO;;;;;CAMT,cAAoB;AAClB,OAAK;AACL,OAAK,wBAAwB;;;;;CAM/B,QAAc;AACZ,OAAK,UAAU,OAAO;AACtB,OAAK,YAAY;AACjB,OAAK,wBAAwB;AAC7B,MAAI,MAAM,+BAA+B;;;;;;CAO3C,sBAA8B;EAC5B,MAAM,WAAqB,EAAE;AAG7B,WAAS,KAAK,KAAK,uBAAuB,CAAC;EAG3C,MAAM,YAAY,KAAK,mBAAmB;AAC1C,MAAI,UACF,UAAS,KAAK,KAAK,0BAA0B,UAAU,MAAM,UAAU,MAAM,CAAC;AAIhF,MAAI,KAAK,6BAA6B,CACpC,UAAS,KAAK,KAAK,4BAA4B,CAAC;AAGlD,SAAO,SAAS,OAAO,QAAQ,CAAC,KAAK,OAAO;;;;;CAM9C,8BAA+C;AAC7C,MAAI,CAAC,KAAK,OAAO,yBAA0B,QAAO;AAClD,MAAI,KAAK,YAAY,KAAK,OAAO,wBAAyB,QAAO;AACjE,MAAI,KAAK,sBAAuB,QAAO;AACvC,SAAO;;;;;CAMT,4BAAkC;AAChC,OAAK,wBAAwB;;;;;CAM/B,wBAAwC;AACtC,SAAO;;;;;;;;;;;;;;;;;;;;CAqBT,0BAAkC,UAAkB,OAAuB;AACzE,SAAO,0CAA0C,SAAS,IAAI,MAAM;;;;;;;;;;;;;;;CAgBtE,6BAA6C;AAC3C,OAAK,2BAA2B;AAChC,SAAO,yCAAyC,KAAK,UAAU;;;;;;;;;;;;;CAcjE,iBAA+G;EAC7G,MAAM,UAAU,MAAM,KAAK,KAAK,UAAU,SAAS,CAAC;EACpD,MAAM,aAAa,QAAQ,QAAQ,KAAK,GAAG,YAAY,MAAM,OAAO,WAAW,EAAE;EACjF,MAAM,WAAW,QACd,MAAM,GAAG,MAAM,EAAE,GAAG,YAAY,EAAE,GAAG,UAAU,CAC/C,MAAM,GAAG,EAAE,CACX,KAAK,CAAC,MAAM,aAAa;GAAE;GAAM,OAAO,OAAO;GAAW,EAAE;AAE/D,SAAO;GACL,YAAY,QAAQ;GACpB;GACA;GACD;;;;;CAMH,UAAU,QAAyC;AACjD,OAAK,SAAS;GAAE,GAAG,KAAK;GAAQ,GAAG;GAAQ;;;;;CAM7C,YAA8B;AAC5B,SAAO,EAAE,GAAG,KAAK,QAAQ"}
|
|
@@ -4,22 +4,14 @@
|
|
|
4
4
|
* Handles model selection, switching, and automatic fallback
|
|
5
5
|
* when a provider fails.
|
|
6
6
|
*/
|
|
7
|
-
import { Agent
|
|
7
|
+
import { Agent } from '@earendil-works/pi-agent-core';
|
|
8
8
|
import type { Model, Api } from '@earendil-works/pi-ai';
|
|
9
9
|
import { type Config } from '../../config/schema.js';
|
|
10
|
-
import { type ModelCandidate } from '../fallback/
|
|
10
|
+
import { type ModelCandidate } from '../fallback/candidates.js';
|
|
11
11
|
export interface ModelManagerConfig {
|
|
12
12
|
defaultModel?: string;
|
|
13
13
|
config?: Config;
|
|
14
14
|
}
|
|
15
|
-
export interface RunResult {
|
|
16
|
-
content: string | null;
|
|
17
|
-
usage?: {
|
|
18
|
-
prompt_tokens: number;
|
|
19
|
-
completion_tokens: number;
|
|
20
|
-
total_tokens: number;
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
15
|
export declare class ModelManager {
|
|
24
16
|
private defaultModel;
|
|
25
17
|
private config?;
|
|
@@ -28,12 +20,8 @@ export declare class ModelManager {
|
|
|
28
20
|
private sessionModels;
|
|
29
21
|
/** Baseline model from `agents.list` / defaults merge when the session agent is created. */
|
|
30
22
|
private sessionProfileDefaults;
|
|
31
|
-
private
|
|
23
|
+
private sessionProfileFallbacks;
|
|
32
24
|
constructor(config?: ModelManagerConfig);
|
|
33
|
-
/**
|
|
34
|
-
* Set channel manager reference for accessing session-specific settings
|
|
35
|
-
*/
|
|
36
|
-
setChannelManager(channelManager: any): void;
|
|
37
25
|
/**
|
|
38
26
|
* Apply updated config so default model and failover metadata match disk/runtime config.
|
|
39
27
|
*/
|
|
@@ -42,7 +30,7 @@ export declare class ModelManager {
|
|
|
42
30
|
* Set the config-derived default model for a session (from effective agent profile).
|
|
43
31
|
* Cleared by {@link updateFromConfig} or {@link clearSessionProfileDefault}.
|
|
44
32
|
*/
|
|
45
|
-
setSessionProfileDefault(sessionKey: string, modelRef: string): void;
|
|
33
|
+
setSessionProfileDefault(sessionKey: string, modelRef: string, fallbacks?: string[]): void;
|
|
46
34
|
clearSessionProfileDefault(sessionKey: string): void;
|
|
47
35
|
/**
|
|
48
36
|
* Get current model name
|
|
@@ -78,10 +66,6 @@ export declare class ModelManager {
|
|
|
78
66
|
* Apply a resolved pi-ai model and sync {@link currentModelName} / {@link currentProvider}.
|
|
79
67
|
*/
|
|
80
68
|
applyResolvedModel(agent: Agent, model: Model<Api>, modelRef: string): void;
|
|
81
|
-
/**
|
|
82
|
-
* Run the agent with automatic model fallback on failure.
|
|
83
|
-
*/
|
|
84
|
-
runWithFallback(agent: Agent, sessionKey: string, userMessage: AgentMessage, provider: string, model: string): Promise<RunResult>;
|
|
85
69
|
/**
|
|
86
70
|
* Find model by reference (provider/modelId)
|
|
87
71
|
*/
|
|
@@ -2,28 +2,13 @@ import { getAgentDefaultModelRef, init_schema } from "../../config/schema.js";
|
|
|
2
2
|
import { createLogger } from "../../utils/logger/index.js";
|
|
3
3
|
import { init_logger } from "../../utils/logger.js";
|
|
4
4
|
import { getAllModels, getDefaultModelSync, init_providers, resolveModel } from "../../providers/index.js";
|
|
5
|
-
import { resolveAgentTurnTimeoutMs, runAgentTurnWithTimeout } from "../orchestration/run-agent-turn-with-timeout.js";
|
|
6
|
-
import { isAssistantTurnAborted, isAssistantTurnFailed, maybeRetryTurnAfterTransientLlmFailure } from "../orchestration/llm-turn-retry.js";
|
|
7
|
-
import { describeFailoverError, isFailoverError } from "../fallback/error.js";
|
|
8
5
|
import { parseModelRef } from "./selection.js";
|
|
9
6
|
import { resolveFallbackCandidates } from "../fallback/candidates.js";
|
|
10
|
-
import "../fallback/index.js";
|
|
11
7
|
//#region src/agent/models/manager.ts
|
|
12
8
|
init_schema();
|
|
13
9
|
init_logger();
|
|
14
10
|
init_providers();
|
|
15
11
|
const log = createLogger("ModelManager");
|
|
16
|
-
/**
|
|
17
|
-
* Get the last assistant content from agent messages
|
|
18
|
-
*/
|
|
19
|
-
function getLastAssistantContent(agent) {
|
|
20
|
-
const messages = agent.state.messages;
|
|
21
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
22
|
-
const msg = messages[i];
|
|
23
|
-
if (msg.role === "assistant") return msg.content.filter((c) => c.type === "text").map((c) => c.text || "").join("");
|
|
24
|
-
}
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
12
|
var ModelManager = class {
|
|
28
13
|
defaultModel;
|
|
29
14
|
config;
|
|
@@ -32,7 +17,7 @@ var ModelManager = class {
|
|
|
32
17
|
sessionModels = /* @__PURE__ */ new Map();
|
|
33
18
|
/** Baseline model from `agents.list` / defaults merge when the session agent is created. */
|
|
34
19
|
sessionProfileDefaults = /* @__PURE__ */ new Map();
|
|
35
|
-
|
|
20
|
+
sessionProfileFallbacks = /* @__PURE__ */ new Map();
|
|
36
21
|
constructor(config = {}) {
|
|
37
22
|
this.config = config.config;
|
|
38
23
|
this.defaultModel = config.defaultModel || getDefaultModelSync(config.config);
|
|
@@ -40,12 +25,6 @@ var ModelManager = class {
|
|
|
40
25
|
this.currentProvider = this.defaultModel.split("/")[0] || "anthropic";
|
|
41
26
|
}
|
|
42
27
|
/**
|
|
43
|
-
* Set channel manager reference for accessing session-specific settings
|
|
44
|
-
*/
|
|
45
|
-
setChannelManager(channelManager) {
|
|
46
|
-
this.channelManager = channelManager;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
28
|
* Apply updated config so default model and failover metadata match disk/runtime config.
|
|
50
29
|
*/
|
|
51
30
|
updateFromConfig(config) {
|
|
@@ -53,16 +32,21 @@ var ModelManager = class {
|
|
|
53
32
|
const ref = getAgentDefaultModelRef(config);
|
|
54
33
|
this.defaultModel = ref ? ref : getDefaultModelSync(config);
|
|
55
34
|
this.sessionProfileDefaults.clear();
|
|
35
|
+
this.sessionProfileFallbacks.clear();
|
|
56
36
|
}
|
|
57
37
|
/**
|
|
58
38
|
* Set the config-derived default model for a session (from effective agent profile).
|
|
59
39
|
* Cleared by {@link updateFromConfig} or {@link clearSessionProfileDefault}.
|
|
60
40
|
*/
|
|
61
|
-
setSessionProfileDefault(sessionKey, modelRef) {
|
|
41
|
+
setSessionProfileDefault(sessionKey, modelRef, fallbacks = []) {
|
|
62
42
|
this.sessionProfileDefaults.set(sessionKey, modelRef);
|
|
43
|
+
const cleanFallbacks = fallbacks.map((ref) => ref.trim()).filter(Boolean);
|
|
44
|
+
if (cleanFallbacks.length > 0) this.sessionProfileFallbacks.set(sessionKey, cleanFallbacks);
|
|
45
|
+
else this.sessionProfileFallbacks.delete(sessionKey);
|
|
63
46
|
}
|
|
64
47
|
clearSessionProfileDefault(sessionKey) {
|
|
65
48
|
this.sessionProfileDefaults.delete(sessionKey);
|
|
49
|
+
this.sessionProfileFallbacks.delete(sessionKey);
|
|
66
50
|
}
|
|
67
51
|
/**
|
|
68
52
|
* Get current model name
|
|
@@ -156,7 +140,8 @@ var ModelManager = class {
|
|
|
156
140
|
return resolveFallbackCandidates({
|
|
157
141
|
cfg: this.config,
|
|
158
142
|
provider: parsed.provider,
|
|
159
|
-
model: parsed.model
|
|
143
|
+
model: parsed.model,
|
|
144
|
+
fallbacksOverride: this.sessionModels.has(sessionKey) ? void 0 : this.sessionProfileFallbacks.get(sessionKey)
|
|
160
145
|
});
|
|
161
146
|
}
|
|
162
147
|
/**
|
|
@@ -168,108 +153,6 @@ var ModelManager = class {
|
|
|
168
153
|
this.currentProvider = model.provider || "unknown";
|
|
169
154
|
}
|
|
170
155
|
/**
|
|
171
|
-
* Run the agent with automatic model fallback on failure.
|
|
172
|
-
*/
|
|
173
|
-
async runWithFallback(agent, sessionKey, userMessage, provider, model) {
|
|
174
|
-
const candidates = resolveFallbackCandidates({
|
|
175
|
-
cfg: this.config ?? void 0,
|
|
176
|
-
provider,
|
|
177
|
-
model
|
|
178
|
-
});
|
|
179
|
-
let lastError;
|
|
180
|
-
for (let i = 0; i < candidates.length; i++) {
|
|
181
|
-
const candidate = candidates[i];
|
|
182
|
-
const modelRef = `${candidate.provider}/${candidate.model}`;
|
|
183
|
-
let candidateModel;
|
|
184
|
-
try {
|
|
185
|
-
candidateModel = resolveModel(modelRef);
|
|
186
|
-
} catch {
|
|
187
|
-
log.warn({
|
|
188
|
-
provider: candidate.provider,
|
|
189
|
-
model: candidate.model
|
|
190
|
-
}, "Fallback model not found");
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
log.info({
|
|
194
|
-
attempt: i + 1,
|
|
195
|
-
total: candidates.length,
|
|
196
|
-
provider: candidate.provider,
|
|
197
|
-
model: candidate.model
|
|
198
|
-
}, "Attempting model");
|
|
199
|
-
const beforeLen = agent.state.messages.length;
|
|
200
|
-
try {
|
|
201
|
-
this.applyResolvedModel(agent, candidateModel, modelRef);
|
|
202
|
-
const runTurn = async () => {
|
|
203
|
-
await agent.prompt(userMessage);
|
|
204
|
-
await agent.waitForIdle();
|
|
205
|
-
await maybeRetryTurnAfterTransientLlmFailure(agent, {
|
|
206
|
-
sessionKey,
|
|
207
|
-
log
|
|
208
|
-
});
|
|
209
|
-
};
|
|
210
|
-
await runAgentTurnWithTimeout(agent, runTurn, resolveAgentTurnTimeoutMs(this.config));
|
|
211
|
-
if (isAssistantTurnAborted(agent)) {
|
|
212
|
-
const usage = agent.state.lastUsage;
|
|
213
|
-
return {
|
|
214
|
-
content: getLastAssistantContent(agent),
|
|
215
|
-
usage
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
if (!isAssistantTurnFailed(agent)) {
|
|
219
|
-
const usage = agent.state.lastUsage;
|
|
220
|
-
log.info({
|
|
221
|
-
provider: candidate.provider,
|
|
222
|
-
model: candidate.model,
|
|
223
|
-
success: true
|
|
224
|
-
}, "Model call completed");
|
|
225
|
-
return {
|
|
226
|
-
content: getLastAssistantContent(agent),
|
|
227
|
-
usage
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
lastError = /* @__PURE__ */ new Error(`Assistant turn failed: ${modelRef}`);
|
|
231
|
-
log.warn({
|
|
232
|
-
attempt: i + 1,
|
|
233
|
-
sessionKey,
|
|
234
|
-
modelRef
|
|
235
|
-
}, "Model turn failed after retries, trying fallback");
|
|
236
|
-
} catch (err) {
|
|
237
|
-
lastError = err;
|
|
238
|
-
if (err instanceof DOMException && err.name === "AbortError") {
|
|
239
|
-
log.info({ sessionKey }, "User aborted model call");
|
|
240
|
-
throw err;
|
|
241
|
-
}
|
|
242
|
-
const errorDetails = {
|
|
243
|
-
attempt: i + 1,
|
|
244
|
-
provider: candidate.provider,
|
|
245
|
-
model: candidate.model,
|
|
246
|
-
errorMessage: err instanceof Error ? err.message : String(err)
|
|
247
|
-
};
|
|
248
|
-
if (isFailoverError(err)) {
|
|
249
|
-
const described = describeFailoverError(err);
|
|
250
|
-
log.warn({
|
|
251
|
-
...errorDetails,
|
|
252
|
-
...described
|
|
253
|
-
}, "Model call failed, trying fallback");
|
|
254
|
-
} else log.warn(errorDetails, "Model call failed, trying fallback");
|
|
255
|
-
}
|
|
256
|
-
agent.state.messages = agent.state.messages.slice(0, beforeLen);
|
|
257
|
-
}
|
|
258
|
-
if (lastError) {
|
|
259
|
-
const em = lastError instanceof Error ? lastError.message : String(lastError);
|
|
260
|
-
const attemptedModelRefs = candidates.map((c) => `${c.provider}/${c.model}`);
|
|
261
|
-
log.error({
|
|
262
|
-
err: lastError instanceof Error ? lastError : void 0,
|
|
263
|
-
errorMessage: em,
|
|
264
|
-
attemptedCandidates: candidates.length,
|
|
265
|
-
attemptedModelRefs,
|
|
266
|
-
sessionKey
|
|
267
|
-
}, `All model candidates failed (${candidates.length} refs): ${em}`);
|
|
268
|
-
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
269
|
-
}
|
|
270
|
-
return { content: null };
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
156
|
* Find model by reference (provider/modelId)
|
|
274
157
|
*/
|
|
275
158
|
findByRef(ref) {
|