@roj-ai/sdk 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bootstrap.js +191 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/builtin-events.js +8 -0
- package/dist/builtin-events.js.map +1 -0
- package/dist/bun-platform/fs.js +39 -0
- package/dist/bun-platform/fs.js.map +1 -0
- package/dist/bun-platform/index.js +18 -0
- package/dist/bun-platform/index.js.map +1 -0
- package/dist/bun-platform/process.js +21 -0
- package/dist/bun-platform/process.js.map +1 -0
- package/dist/config.js +54 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.js +155 -0
- package/dist/config.test.js.map +1 -0
- package/dist/core/agent-loop.integration.test.js +414 -0
- package/dist/core/agent-loop.integration.test.js.map +1 -0
- package/dist/core/agents/agent-config.test.js +194 -0
- package/dist/core/agents/agent-config.test.js.map +1 -0
- package/dist/core/agents/agent-roles.js +25 -0
- package/dist/core/agents/agent-roles.js.map +1 -0
- package/dist/core/agents/agent-shutdown.test.js +180 -0
- package/dist/core/agents/agent-shutdown.test.js.map +1 -0
- package/dist/core/agents/agent.js +1205 -0
- package/dist/core/agents/agent.js.map +1 -0
- package/dist/core/agents/agent.test.js +313 -0
- package/dist/core/agents/agent.test.js.map +1 -0
- package/dist/core/agents/communicator.js +13 -0
- package/dist/core/agents/communicator.js.map +1 -0
- package/dist/core/agents/config.js +5 -0
- package/dist/core/agents/config.js.map +1 -0
- package/dist/core/agents/context.js +2 -0
- package/dist/core/agents/context.js.map +1 -0
- package/dist/core/agents/debounce.js +74 -0
- package/dist/core/agents/debounce.js.map +1 -0
- package/dist/core/agents/handler-events.test.js +115 -0
- package/dist/core/agents/handler-events.test.js.map +1 -0
- package/dist/core/agents/index.js +2 -0
- package/dist/core/agents/index.js.map +1 -0
- package/dist/core/agents/response-sanitizer.js +46 -0
- package/dist/core/agents/response-sanitizer.js.map +1 -0
- package/dist/core/agents/response-sanitizer.test.js +101 -0
- package/dist/core/agents/response-sanitizer.test.js.map +1 -0
- package/dist/core/agents/retry.js +105 -0
- package/dist/core/agents/retry.js.map +1 -0
- package/dist/core/agents/schema.js +39 -0
- package/dist/core/agents/schema.js.map +1 -0
- package/dist/core/agents/state.js +90 -0
- package/dist/core/agents/state.js.map +1 -0
- package/dist/core/context/state.js +23 -0
- package/dist/core/context/state.js.map +1 -0
- package/dist/core/errors.js +38 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/event-sourcing.integration.test.js +154 -0
- package/dist/core/event-sourcing.integration.test.js.map +1 -0
- package/dist/core/events/base-event-store.js +201 -0
- package/dist/core/events/base-event-store.js.map +1 -0
- package/dist/core/events/event-store.js +26 -0
- package/dist/core/events/event-store.js.map +1 -0
- package/dist/core/events/file.js +320 -0
- package/dist/core/events/file.js.map +1 -0
- package/dist/core/events/file.test.js +284 -0
- package/dist/core/events/file.test.js.map +1 -0
- package/dist/core/events/index.js +3 -0
- package/dist/core/events/index.js.map +1 -0
- package/dist/core/events/memory.js +101 -0
- package/dist/core/events/memory.js.map +1 -0
- package/dist/core/events/memory.test.js +502 -0
- package/dist/core/events/memory.test.js.map +1 -0
- package/dist/core/events/metadata-utils.js +107 -0
- package/dist/core/events/metadata-utils.js.map +1 -0
- package/dist/core/events/test-helpers.js +15 -0
- package/dist/core/events/test-helpers.js.map +1 -0
- package/dist/core/events/types.js +21 -0
- package/dist/core/events/types.js.map +1 -0
- package/dist/core/file-store/file-store.js +250 -0
- package/dist/core/file-store/file-store.js.map +1 -0
- package/dist/core/file-store/types.js +7 -0
- package/dist/core/file-store/types.js.map +1 -0
- package/dist/core/image/image-processor.js +106 -0
- package/dist/core/image/image-processor.js.map +1 -0
- package/dist/core/image/image-processor.test.js +171 -0
- package/dist/core/image/image-processor.test.js.map +1 -0
- package/dist/core/image/index.js +4 -0
- package/dist/core/image/index.js.map +1 -0
- package/dist/core/image/noop-resizer.js +6 -0
- package/dist/core/image/noop-resizer.js.map +1 -0
- package/dist/core/image/types.js +2 -0
- package/dist/core/image/types.js.map +1 -0
- package/dist/core/image/vips-resizer.js +100 -0
- package/dist/core/image/vips-resizer.js.map +1 -0
- package/dist/core/image/vips-resizer.test.js +324 -0
- package/dist/core/image/vips-resizer.test.js.map +1 -0
- package/dist/core/llm/anthropic.js +396 -0
- package/dist/core/llm/anthropic.js.map +1 -0
- package/dist/core/llm/anthropic.test.js +434 -0
- package/dist/core/llm/anthropic.test.js.map +1 -0
- package/dist/core/llm/cache-breakpoints.js +37 -0
- package/dist/core/llm/cache-breakpoints.js.map +1 -0
- package/dist/core/llm/cache-live.test.js +137 -0
- package/dist/core/llm/cache-live.test.js.map +1 -0
- package/dist/core/llm/index.js +9 -0
- package/dist/core/llm/index.js.map +1 -0
- package/dist/core/llm/llm-log-types.js +12 -0
- package/dist/core/llm/llm-log-types.js.map +1 -0
- package/dist/core/llm/logger.js +241 -0
- package/dist/core/llm/logger.js.map +1 -0
- package/dist/core/llm/logger.test.js +228 -0
- package/dist/core/llm/logger.test.js.map +1 -0
- package/dist/core/llm/logging-provider.js +49 -0
- package/dist/core/llm/logging-provider.js.map +1 -0
- package/dist/core/llm/middleware.js +114 -0
- package/dist/core/llm/middleware.js.map +1 -0
- package/dist/core/llm/mock.js +186 -0
- package/dist/core/llm/mock.js.map +1 -0
- package/dist/core/llm/mock.test.js +318 -0
- package/dist/core/llm/mock.test.js.map +1 -0
- package/dist/core/llm/openrouter-mapping.test.js +125 -0
- package/dist/core/llm/openrouter-mapping.test.js.map +1 -0
- package/dist/core/llm/openrouter.js +298 -0
- package/dist/core/llm/openrouter.js.map +1 -0
- package/dist/core/llm/openrouter.test.js +377 -0
- package/dist/core/llm/openrouter.test.js.map +1 -0
- package/dist/core/llm/provider-integration.test.js +350 -0
- package/dist/core/llm/provider-integration.test.js.map +1 -0
- package/dist/core/llm/provider.js +18 -0
- package/dist/core/llm/provider.js.map +1 -0
- package/dist/core/llm/routing-provider.js +52 -0
- package/dist/core/llm/routing-provider.js.map +1 -0
- package/dist/core/llm/routing-provider.test.js +94 -0
- package/dist/core/llm/routing-provider.test.js.map +1 -0
- package/dist/core/llm/schema.js +31 -0
- package/dist/core/llm/schema.js.map +1 -0
- package/dist/core/llm/snapshot-fetch.js +122 -0
- package/dist/core/llm/snapshot-fetch.js.map +1 -0
- package/dist/core/llm/snapshot-middleware.js +142 -0
- package/dist/core/llm/snapshot-middleware.js.map +1 -0
- package/dist/core/llm/snapshot-middleware.test.js +144 -0
- package/dist/core/llm/snapshot-middleware.test.js.map +1 -0
- package/dist/core/llm/state.js +48 -0
- package/dist/core/llm/state.js.map +1 -0
- package/dist/core/llm/tokens.js +40 -0
- package/dist/core/llm/tokens.js.map +1 -0
- package/dist/core/multi-agent.integration.test.js +298 -0
- package/dist/core/multi-agent.integration.test.js.map +1 -0
- package/dist/core/plugin-hooks.integration.test.js +344 -0
- package/dist/core/plugin-hooks.integration.test.js.map +1 -0
- package/dist/core/plugins/hook-types.js +5 -0
- package/dist/core/plugins/hook-types.js.map +1 -0
- package/dist/core/plugins/index.js +5 -0
- package/dist/core/plugins/index.js.map +1 -0
- package/dist/core/plugins/plugin-builder.js +321 -0
- package/dist/core/plugins/plugin-builder.js.map +1 -0
- package/dist/core/preset/config.js +54 -0
- package/dist/core/preset/config.js.map +1 -0
- package/dist/core/preset/index.js +6 -0
- package/dist/core/preset/index.js.map +1 -0
- package/dist/core/preset/preset-builder.js +63 -0
- package/dist/core/preset/preset-builder.js.map +1 -0
- package/dist/core/session-lifecycle.integration.test.js +159 -0
- package/dist/core/session-lifecycle.integration.test.js.map +1 -0
- package/dist/core/sessions/apply-event.js +41 -0
- package/dist/core/sessions/apply-event.js.map +1 -0
- package/dist/core/sessions/context.js +2 -0
- package/dist/core/sessions/context.js.map +1 -0
- package/dist/core/sessions/fork-utils.js +42 -0
- package/dist/core/sessions/fork-utils.js.map +1 -0
- package/dist/core/sessions/fork-utils.test.js +129 -0
- package/dist/core/sessions/fork-utils.test.js.map +1 -0
- package/dist/core/sessions/reducer.js +55 -0
- package/dist/core/sessions/reducer.js.map +1 -0
- package/dist/core/sessions/schema.js +66 -0
- package/dist/core/sessions/schema.js.map +1 -0
- package/dist/core/sessions/session-environment.js +2 -0
- package/dist/core/sessions/session-environment.js.map +1 -0
- package/dist/core/sessions/session-manager.js +650 -0
- package/dist/core/sessions/session-manager.js.map +1 -0
- package/dist/core/sessions/session-store.js +118 -0
- package/dist/core/sessions/session-store.js.map +1 -0
- package/dist/core/sessions/session.js +675 -0
- package/dist/core/sessions/session.js.map +1 -0
- package/dist/core/sessions/session.test.js +1095 -0
- package/dist/core/sessions/session.test.js.map +1 -0
- package/dist/core/sessions/state.js +377 -0
- package/dist/core/sessions/state.js.map +1 -0
- package/dist/core/system.js +66 -0
- package/dist/core/system.js.map +1 -0
- package/dist/core/tools/context.js +2 -0
- package/dist/core/tools/context.js.map +1 -0
- package/dist/core/tools/definition.js +4 -0
- package/dist/core/tools/definition.js.map +1 -0
- package/dist/core/tools/executor.js +82 -0
- package/dist/core/tools/executor.js.map +1 -0
- package/dist/core/tools/executor.test.js +143 -0
- package/dist/core/tools/executor.test.js.map +1 -0
- package/dist/core/tools/index.js +4 -0
- package/dist/core/tools/index.js.map +1 -0
- package/dist/core/tools/schema.js +20 -0
- package/dist/core/tools/schema.js.map +1 -0
- package/dist/core/tools/state.js +29 -0
- package/dist/core/tools/state.js.map +1 -0
- package/dist/index.js +70 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/json/index.js +5 -0
- package/dist/lib/json/index.js.map +1 -0
- package/dist/lib/logger/console.js +147 -0
- package/dist/lib/logger/console.js.map +1 -0
- package/dist/lib/logger/console.test.js +258 -0
- package/dist/lib/logger/console.test.js.map +1 -0
- package/dist/lib/logger/file.js +54 -0
- package/dist/lib/logger/file.js.map +1 -0
- package/dist/lib/logger/index.js +4 -0
- package/dist/lib/logger/index.js.map +1 -0
- package/dist/lib/logger/logger.js +28 -0
- package/dist/lib/logger/logger.js.map +1 -0
- package/dist/lib/logger/ring-buffer.js +61 -0
- package/dist/lib/logger/ring-buffer.js.map +1 -0
- package/dist/lib/logger/tee.js +43 -0
- package/dist/lib/logger/tee.js.map +1 -0
- package/dist/lib/mime.js +22 -0
- package/dist/lib/mime.js.map +1 -0
- package/dist/lib/never.js +4 -0
- package/dist/lib/never.js.map +1 -0
- package/dist/lib/utils/hash.js +35 -0
- package/dist/lib/utils/hash.js.map +1 -0
- package/dist/lib/utils/result.js +21 -0
- package/dist/lib/utils/result.js.map +1 -0
- package/dist/platform/fs.js +8 -0
- package/dist/platform/fs.js.map +1 -0
- package/dist/platform/index.js +9 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/process.js +8 -0
- package/dist/platform/process.js.map +1 -0
- package/dist/plugins/agent-status/plugin.js +77 -0
- package/dist/plugins/agent-status/plugin.js.map +1 -0
- package/dist/plugins/agents/agents.integration.test.js +683 -0
- package/dist/plugins/agents/agents.integration.test.js.map +1 -0
- package/dist/plugins/agents/index.js +2 -0
- package/dist/plugins/agents/index.js.map +1 -0
- package/dist/plugins/agents/plugin.js +199 -0
- package/dist/plugins/agents/plugin.js.map +1 -0
- package/dist/plugins/context-compact/context-compact.integration.test.js +174 -0
- package/dist/plugins/context-compact/context-compact.integration.test.js.map +1 -0
- package/dist/plugins/context-compact/context-compactor.js +238 -0
- package/dist/plugins/context-compact/context-compactor.js.map +1 -0
- package/dist/plugins/context-compact/context-compactor.test.js +763 -0
- package/dist/plugins/context-compact/context-compactor.test.js.map +1 -0
- package/dist/plugins/context-compact/history-offloader.js +42 -0
- package/dist/plugins/context-compact/history-offloader.js.map +1 -0
- package/dist/plugins/context-compact/history-offloader.test.js +77 -0
- package/dist/plugins/context-compact/history-offloader.test.js.map +1 -0
- package/dist/plugins/context-compact/index.js +4 -0
- package/dist/plugins/context-compact/index.js.map +1 -0
- package/dist/plugins/context-compact/plugin.js +37 -0
- package/dist/plugins/context-compact/plugin.js.map +1 -0
- package/dist/plugins/filesystem/filesystem.integration.test.js +411 -0
- package/dist/plugins/filesystem/filesystem.integration.test.js.map +1 -0
- package/dist/plugins/filesystem/helpers.js +170 -0
- package/dist/plugins/filesystem/helpers.js.map +1 -0
- package/dist/plugins/filesystem/index.js +3 -0
- package/dist/plugins/filesystem/index.js.map +1 -0
- package/dist/plugins/filesystem/listing.js +247 -0
- package/dist/plugins/filesystem/listing.js.map +1 -0
- package/dist/plugins/filesystem/plugin.js +364 -0
- package/dist/plugins/filesystem/plugin.js.map +1 -0
- package/dist/plugins/filesystem/schema.js +2 -0
- package/dist/plugins/filesystem/schema.js.map +1 -0
- package/dist/plugins/git-status/index.js +2 -0
- package/dist/plugins/git-status/index.js.map +1 -0
- package/dist/plugins/git-status/plugin.js +144 -0
- package/dist/plugins/git-status/plugin.js.map +1 -0
- package/dist/plugins/limits-guard/config.js +5 -0
- package/dist/plugins/limits-guard/config.js.map +1 -0
- package/dist/plugins/limits-guard/index.js +3 -0
- package/dist/plugins/limits-guard/index.js.map +1 -0
- package/dist/plugins/limits-guard/limit-guard.js +125 -0
- package/dist/plugins/limits-guard/limit-guard.js.map +1 -0
- package/dist/plugins/limits-guard/limit-guard.test.js +121 -0
- package/dist/plugins/limits-guard/limit-guard.test.js.map +1 -0
- package/dist/plugins/limits-guard/limits-guard.integration.test.js +378 -0
- package/dist/plugins/limits-guard/limits-guard.integration.test.js.map +1 -0
- package/dist/plugins/limits-guard/plugin.js +240 -0
- package/dist/plugins/limits-guard/plugin.js.map +1 -0
- package/dist/plugins/llm-debug/index.js +2 -0
- package/dist/plugins/llm-debug/index.js.map +1 -0
- package/dist/plugins/llm-debug/llm-debug.integration.test.js +157 -0
- package/dist/plugins/llm-debug/llm-debug.integration.test.js.map +1 -0
- package/dist/plugins/llm-debug/plugin.js +148 -0
- package/dist/plugins/llm-debug/plugin.js.map +1 -0
- package/dist/plugins/logs/index.js +2 -0
- package/dist/plugins/logs/index.js.map +1 -0
- package/dist/plugins/logs/plugin.js +38 -0
- package/dist/plugins/logs/plugin.js.map +1 -0
- package/dist/plugins/mailbox/helpers.js +66 -0
- package/dist/plugins/mailbox/helpers.js.map +1 -0
- package/dist/plugins/mailbox/index.js +9 -0
- package/dist/plugins/mailbox/index.js.map +1 -0
- package/dist/plugins/mailbox/mailbox.integration.test.js +605 -0
- package/dist/plugins/mailbox/mailbox.integration.test.js.map +1 -0
- package/dist/plugins/mailbox/plugin.js +204 -0
- package/dist/plugins/mailbox/plugin.js.map +1 -0
- package/dist/plugins/mailbox/prompts.js +93 -0
- package/dist/plugins/mailbox/prompts.js.map +1 -0
- package/dist/plugins/mailbox/query.js +38 -0
- package/dist/plugins/mailbox/query.js.map +1 -0
- package/dist/plugins/mailbox/schema.js +32 -0
- package/dist/plugins/mailbox/schema.js.map +1 -0
- package/dist/plugins/mailbox/state.js +41 -0
- package/dist/plugins/mailbox/state.js.map +1 -0
- package/dist/plugins/resources/index.js +4 -0
- package/dist/plugins/resources/index.js.map +1 -0
- package/dist/plugins/resources/manifest.js +20 -0
- package/dist/plugins/resources/manifest.js.map +1 -0
- package/dist/plugins/resources/plugin.js +171 -0
- package/dist/plugins/resources/plugin.js.map +1 -0
- package/dist/plugins/resources/post-inject.js +32 -0
- package/dist/plugins/resources/post-inject.js.map +1 -0
- package/dist/plugins/resources/state.js +16 -0
- package/dist/plugins/resources/state.js.map +1 -0
- package/dist/plugins/result-eviction/index.js +2 -0
- package/dist/plugins/result-eviction/index.js.map +1 -0
- package/dist/plugins/result-eviction/plugin.js +43 -0
- package/dist/plugins/result-eviction/plugin.js.map +1 -0
- package/dist/plugins/result-eviction/result-eviction.integration.test.js +217 -0
- package/dist/plugins/result-eviction/result-eviction.integration.test.js.map +1 -0
- package/dist/plugins/services/plugin.js +453 -0
- package/dist/plugins/services/plugin.js.map +1 -0
- package/dist/plugins/services/port-pool.js +70 -0
- package/dist/plugins/services/port-pool.js.map +1 -0
- package/dist/plugins/services/prompt.js +40 -0
- package/dist/plugins/services/prompt.js.map +1 -0
- package/dist/plugins/services/schema.js +9 -0
- package/dist/plugins/services/schema.js.map +1 -0
- package/dist/plugins/services/service.js +470 -0
- package/dist/plugins/services/service.js.map +1 -0
- package/dist/plugins/services/services.integration.test.js +485 -0
- package/dist/plugins/services/services.integration.test.js.map +1 -0
- package/dist/plugins/session-lifecycle/index.js +2 -0
- package/dist/plugins/session-lifecycle/index.js.map +1 -0
- package/dist/plugins/session-lifecycle/plugin.js +273 -0
- package/dist/plugins/session-lifecycle/plugin.js.map +1 -0
- package/dist/plugins/session-lifecycle/session-lifecycle.integration.test.js +498 -0
- package/dist/plugins/session-lifecycle/session-lifecycle.integration.test.js.map +1 -0
- package/dist/plugins/session-state/plugin.js +159 -0
- package/dist/plugins/session-state/plugin.js.map +1 -0
- package/dist/plugins/session-stats/index.js +3 -0
- package/dist/plugins/session-stats/index.js.map +1 -0
- package/dist/plugins/session-stats/plugin.js +81 -0
- package/dist/plugins/session-stats/plugin.js.map +1 -0
- package/dist/plugins/shell/executor.js +339 -0
- package/dist/plugins/shell/executor.js.map +1 -0
- package/dist/plugins/shell/index.js +6 -0
- package/dist/plugins/shell/index.js.map +1 -0
- package/dist/plugins/shell/plugin.js +66 -0
- package/dist/plugins/shell/plugin.js.map +1 -0
- package/dist/plugins/shell/shell.integration.test.js +234 -0
- package/dist/plugins/shell/shell.integration.test.js.map +1 -0
- package/dist/plugins/shell/shell.test.js +236 -0
- package/dist/plugins/shell/shell.test.js.map +1 -0
- package/dist/plugins/skills/discovery.js +205 -0
- package/dist/plugins/skills/discovery.js.map +1 -0
- package/dist/plugins/skills/discovery.test.js +312 -0
- package/dist/plugins/skills/discovery.test.js.map +1 -0
- package/dist/plugins/skills/index.js +12 -0
- package/dist/plugins/skills/index.js.map +1 -0
- package/dist/plugins/skills/plugin.js +293 -0
- package/dist/plugins/skills/plugin.js.map +1 -0
- package/dist/plugins/skills/prompts.js +70 -0
- package/dist/plugins/skills/prompts.js.map +1 -0
- package/dist/plugins/skills/schema.js +18 -0
- package/dist/plugins/skills/schema.js.map +1 -0
- package/dist/plugins/skills/skills.integration.test.js +475 -0
- package/dist/plugins/skills/skills.integration.test.js.map +1 -0
- package/dist/plugins/snapshotting/index.js +3 -0
- package/dist/plugins/snapshotting/index.js.map +1 -0
- package/dist/plugins/snapshotting/jj-snapshotter.js +106 -0
- package/dist/plugins/snapshotting/jj-snapshotter.js.map +1 -0
- package/dist/plugins/snapshotting/plugin.js +28 -0
- package/dist/plugins/snapshotting/plugin.js.map +1 -0
- package/dist/plugins/snapshotting/snapshotter.js +2 -0
- package/dist/plugins/snapshotting/snapshotter.js.map +1 -0
- package/dist/plugins/todo/index.js +7 -0
- package/dist/plugins/todo/index.js.map +1 -0
- package/dist/plugins/todo/plugin.js +319 -0
- package/dist/plugins/todo/plugin.js.map +1 -0
- package/dist/plugins/todo/prompts.js +54 -0
- package/dist/plugins/todo/prompts.js.map +1 -0
- package/dist/plugins/todo/schema.js +18 -0
- package/dist/plugins/todo/schema.js.map +1 -0
- package/dist/plugins/todo/todo.integration.test.js +605 -0
- package/dist/plugins/todo/todo.integration.test.js.map +1 -0
- package/dist/plugins/uploads/index.js +8 -0
- package/dist/plugins/uploads/index.js.map +1 -0
- package/dist/plugins/uploads/plugin.js +346 -0
- package/dist/plugins/uploads/plugin.js.map +1 -0
- package/dist/plugins/uploads/preprocessor.js +44 -0
- package/dist/plugins/uploads/preprocessor.js.map +1 -0
- package/dist/plugins/uploads/preprocessors/image-classifier.js +127 -0
- package/dist/plugins/uploads/preprocessors/image-classifier.js.map +1 -0
- package/dist/plugins/uploads/preprocessors/index.js +7 -0
- package/dist/plugins/uploads/preprocessors/index.js.map +1 -0
- package/dist/plugins/uploads/preprocessors/markitdown-preprocessor.js +204 -0
- package/dist/plugins/uploads/preprocessors/markitdown-preprocessor.js.map +1 -0
- package/dist/plugins/uploads/preprocessors/zip-preprocessor.js +172 -0
- package/dist/plugins/uploads/preprocessors/zip-preprocessor.js.map +1 -0
- package/dist/plugins/uploads/schema.js +20 -0
- package/dist/plugins/uploads/schema.js.map +1 -0
- package/dist/plugins/uploads/state.js +22 -0
- package/dist/plugins/uploads/state.js.map +1 -0
- package/dist/plugins/uploads/uploads.integration.test.js +496 -0
- package/dist/plugins/uploads/uploads.integration.test.js.map +1 -0
- package/dist/plugins/user-chat/index.js +5 -0
- package/dist/plugins/user-chat/index.js.map +1 -0
- package/dist/plugins/user-chat/plugin.js +544 -0
- package/dist/plugins/user-chat/plugin.js.map +1 -0
- package/dist/plugins/user-chat/prompts.js +29 -0
- package/dist/plugins/user-chat/prompts.js.map +1 -0
- package/dist/plugins/user-chat/schema.js +46 -0
- package/dist/plugins/user-chat/schema.js.map +1 -0
- package/dist/plugins/user-chat/user-chat.integration.test.js +668 -0
- package/dist/plugins/user-chat/user-chat.integration.test.js.map +1 -0
- package/dist/plugins/workers/context.js +143 -0
- package/dist/plugins/workers/context.js.map +1 -0
- package/dist/plugins/workers/definition.js +30 -0
- package/dist/plugins/workers/definition.js.map +1 -0
- package/dist/plugins/workers/index.js +7 -0
- package/dist/plugins/workers/index.js.map +1 -0
- package/dist/plugins/workers/plugin.js +578 -0
- package/dist/plugins/workers/plugin.js.map +1 -0
- package/dist/plugins/workers/worker.js +18 -0
- package/dist/plugins/workers/worker.js.map +1 -0
- package/dist/plugins/workers/workers.integration.test.js +629 -0
- package/dist/plugins/workers/workers.integration.test.js.map +1 -0
- package/dist/prompts/base.js +239 -0
- package/dist/prompts/base.js.map +1 -0
- package/dist/prompts/builder.js +131 -0
- package/dist/prompts/builder.js.map +1 -0
- package/dist/prompts/index.js +20 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/macros.js +26 -0
- package/dist/prompts/macros.js.map +1 -0
- package/dist/prompts/macros.test.js +80 -0
- package/dist/prompts/macros.test.js.map +1 -0
- package/dist/testing/bootstrap-for-testing.js +28 -0
- package/dist/testing/bootstrap-for-testing.js.map +1 -0
- package/dist/testing/index.js +7 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/node-platform.js +65 -0
- package/dist/testing/node-platform.js.map +1 -0
- package/dist/testing/notification-collector.js +82 -0
- package/dist/testing/notification-collector.js.map +1 -0
- package/dist/testing/preset-helpers.js +37 -0
- package/dist/testing/preset-helpers.js.map +1 -0
- package/dist/testing/test-harness.js +226 -0
- package/dist/testing/test-harness.js.map +1 -0
- package/dist/testing/test-harness.test.js +51 -0
- package/dist/testing/test-harness.test.js.map +1 -0
- package/dist/testing/wait-helpers.js +64 -0
- package/dist/testing/wait-helpers.js.map +1 -0
- package/dist/transport/adapter/client-adapter.js +64 -0
- package/dist/transport/adapter/client-adapter.js.map +1 -0
- package/dist/transport/adapter/index.js +24 -0
- package/dist/transport/adapter/index.js.map +1 -0
- package/dist/transport/adapter/server-adapter.js +73 -0
- package/dist/transport/adapter/server-adapter.js.map +1 -0
- package/dist/transport/adapter/types.js +8 -0
- package/dist/transport/adapter/types.js.map +1 -0
- package/dist/transport/http/app.js +86 -0
- package/dist/transport/http/app.js.map +1 -0
- package/dist/transport/http/index.js +6 -0
- package/dist/transport/http/index.js.map +1 -0
- package/dist/transport/http/middleware/bearer-auth.js +33 -0
- package/dist/transport/http/middleware/bearer-auth.js.map +1 -0
- package/dist/transport/http/middleware/error-handler.js +56 -0
- package/dist/transport/http/middleware/error-handler.js.map +1 -0
- package/dist/transport/http/routes/files.js +237 -0
- package/dist/transport/http/routes/files.js.map +1 -0
- package/dist/transport/http/routes/resources.js +77 -0
- package/dist/transport/http/routes/resources.js.map +1 -0
- package/dist/transport/http/routes/rpc.integration.test.js +189 -0
- package/dist/transport/http/routes/rpc.integration.test.js.map +1 -0
- package/dist/transport/http/routes/rpc.js +110 -0
- package/dist/transport/http/routes/rpc.js.map +1 -0
- package/dist/transport/http/routes/rpc.test.js +316 -0
- package/dist/transport/http/routes/rpc.test.js.map +1 -0
- package/dist/transport/http/routes/upload.js +205 -0
- package/dist/transport/http/routes/upload.js.map +1 -0
- package/dist/transport/rpc/index.js +7 -0
- package/dist/transport/rpc/index.js.map +1 -0
- package/dist/transport/rpc/methods.js +8 -0
- package/dist/transport/rpc/methods.js.map +1 -0
- package/dist/user-config.js +14 -0
- package/dist/user-config.js.map +1 -0
- package/package.json +47 -57
|
@@ -0,0 +1,763 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from 'bun:test';
|
|
2
|
+
import { generateTestAgentId } from '../../core/agents/schema.js';
|
|
3
|
+
import { ModelId } from '../../core/llm/schema.js';
|
|
4
|
+
import { generateSessionId } from '../../core/sessions/schema.js';
|
|
5
|
+
import { generateToolCallId } from '../../core/tools/schema.js';
|
|
6
|
+
import { Err, Ok } from '../../lib/utils/result.js';
|
|
7
|
+
import { silentLogger } from '../../lib/logger/logger.js';
|
|
8
|
+
import { ContextCompactor, createContextCompactedEvent, formatMessageForSummary } from './context-compactor.js';
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Test Constants
|
|
11
|
+
// ============================================================================
|
|
12
|
+
const TEST_MODEL_ID = ModelId('test/model');
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Mock LLM Provider
|
|
15
|
+
// ============================================================================
|
|
16
|
+
class MockLLMProvider {
|
|
17
|
+
name = 'mock';
|
|
18
|
+
responses = [];
|
|
19
|
+
responseIndex = 0;
|
|
20
|
+
calls = [];
|
|
21
|
+
setResponses(responses) {
|
|
22
|
+
this.responses = responses;
|
|
23
|
+
this.responseIndex = 0;
|
|
24
|
+
}
|
|
25
|
+
async inference(request) {
|
|
26
|
+
this.calls.push(request);
|
|
27
|
+
if (this.responseIndex >= this.responses.length) {
|
|
28
|
+
return Err({ type: 'server_error', message: 'No more mock responses' });
|
|
29
|
+
}
|
|
30
|
+
return Ok(this.responses[this.responseIndex++]);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// Tests: ContextCompactor.needsCompaction
|
|
35
|
+
// ============================================================================
|
|
36
|
+
describe('ContextCompactor.needsCompaction', () => {
|
|
37
|
+
let mockLLM;
|
|
38
|
+
let compactor;
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
mockLLM = new MockLLMProvider();
|
|
41
|
+
compactor = new ContextCompactor(mockLLM, silentLogger, {
|
|
42
|
+
model: TEST_MODEL_ID,
|
|
43
|
+
maxTokens: 100,
|
|
44
|
+
keepRecentMessages: 2,
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
it('returns false when below threshold', () => {
|
|
48
|
+
const messages = [{ role: 'user', content: 'short' }];
|
|
49
|
+
expect(compactor.needsCompaction(messages)).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
it('returns true when above threshold', () => {
|
|
52
|
+
// Create messages with > 100 tokens
|
|
53
|
+
const messages = [
|
|
54
|
+
{ role: 'user', content: 'a'.repeat(200) }, // ~50 tokens + overhead
|
|
55
|
+
{ role: 'assistant', content: 'b'.repeat(200) }, // ~50 tokens + overhead
|
|
56
|
+
{ role: 'user', content: 'c'.repeat(200) }, // ~50 tokens + overhead
|
|
57
|
+
];
|
|
58
|
+
expect(compactor.needsCompaction(messages)).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Tests: ContextCompactor.compact
|
|
63
|
+
// ============================================================================
|
|
64
|
+
describe('ContextCompactor.compact', () => {
|
|
65
|
+
let mockLLM;
|
|
66
|
+
let compactor;
|
|
67
|
+
let sessionId;
|
|
68
|
+
let agentId;
|
|
69
|
+
beforeEach(() => {
|
|
70
|
+
mockLLM = new MockLLMProvider();
|
|
71
|
+
compactor = new ContextCompactor(mockLLM, silentLogger, {
|
|
72
|
+
model: TEST_MODEL_ID,
|
|
73
|
+
maxTokens: 100,
|
|
74
|
+
keepRecentMessages: 2,
|
|
75
|
+
});
|
|
76
|
+
sessionId = generateSessionId();
|
|
77
|
+
agentId = generateTestAgentId();
|
|
78
|
+
});
|
|
79
|
+
it('returns empty summary when no messages to compact', async () => {
|
|
80
|
+
// Only 2 messages, keepRecentMessages = 2
|
|
81
|
+
const messages = [
|
|
82
|
+
{ role: 'user', content: 'message 1' },
|
|
83
|
+
{ role: 'assistant', content: 'message 2' },
|
|
84
|
+
];
|
|
85
|
+
const result = await compactor.compact(sessionId, agentId, messages);
|
|
86
|
+
expect(result.ok).toBe(true);
|
|
87
|
+
if (!result.ok)
|
|
88
|
+
return;
|
|
89
|
+
expect(result.value.summary).toBe('');
|
|
90
|
+
expect(result.value.messagesRemoved).toBe(0);
|
|
91
|
+
expect(result.value.compactedMessages).toEqual(messages);
|
|
92
|
+
});
|
|
93
|
+
it('compacts old messages and keeps recent ones', async () => {
|
|
94
|
+
mockLLM.setResponses([
|
|
95
|
+
{
|
|
96
|
+
content: 'Summary of the conversation',
|
|
97
|
+
toolCalls: [],
|
|
98
|
+
finishReason: 'stop',
|
|
99
|
+
metrics: {
|
|
100
|
+
promptTokens: 50,
|
|
101
|
+
completionTokens: 20,
|
|
102
|
+
totalTokens: 70,
|
|
103
|
+
latencyMs: 100,
|
|
104
|
+
model: 'mock',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
]);
|
|
108
|
+
const messages = [
|
|
109
|
+
{ role: 'user', content: 'old message 1' },
|
|
110
|
+
{ role: 'assistant', content: 'old message 2' },
|
|
111
|
+
{ role: 'user', content: 'old message 3' },
|
|
112
|
+
{ role: 'user', content: 'recent message 1' },
|
|
113
|
+
{ role: 'assistant', content: 'recent message 2' },
|
|
114
|
+
];
|
|
115
|
+
const result = await compactor.compact(sessionId, agentId, messages);
|
|
116
|
+
expect(result.ok).toBe(true);
|
|
117
|
+
if (!result.ok)
|
|
118
|
+
return;
|
|
119
|
+
expect(result.value.summary).toBe('Summary of the conversation');
|
|
120
|
+
expect(result.value.messagesRemoved).toBe(3);
|
|
121
|
+
expect(result.value.compactedMessages.length).toBe(3); // summary + 2 recent
|
|
122
|
+
// First message is summary
|
|
123
|
+
expect(result.value.compactedMessages[0].role).toBe('system');
|
|
124
|
+
expect(result.value.compactedMessages[0].content).toContain('[CONVERSATION SUMMARY]');
|
|
125
|
+
expect(result.value.compactedMessages[0].content).toContain('Summary of the conversation');
|
|
126
|
+
// Recent messages preserved
|
|
127
|
+
expect(result.value.compactedMessages[1].content).toBe('recent message 1');
|
|
128
|
+
expect(result.value.compactedMessages[2].content).toBe('recent message 2');
|
|
129
|
+
});
|
|
130
|
+
it('calls LLM with formatted conversation and configured model', async () => {
|
|
131
|
+
mockLLM.setResponses([
|
|
132
|
+
{
|
|
133
|
+
content: 'Summary',
|
|
134
|
+
toolCalls: [],
|
|
135
|
+
finishReason: 'stop',
|
|
136
|
+
metrics: {
|
|
137
|
+
promptTokens: 50,
|
|
138
|
+
completionTokens: 20,
|
|
139
|
+
totalTokens: 70,
|
|
140
|
+
latencyMs: 100,
|
|
141
|
+
model: 'mock',
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
]);
|
|
145
|
+
const messages = [
|
|
146
|
+
{ role: 'user', content: 'user message' },
|
|
147
|
+
{ role: 'assistant', content: 'assistant message' },
|
|
148
|
+
{ role: 'user', content: 'recent' },
|
|
149
|
+
{ role: 'assistant', content: 'recent too' },
|
|
150
|
+
];
|
|
151
|
+
await compactor.compact(sessionId, agentId, messages);
|
|
152
|
+
expect(mockLLM.calls.length).toBe(1);
|
|
153
|
+
const request = mockLLM.calls[0];
|
|
154
|
+
// Verify model from config is used
|
|
155
|
+
expect(request.model).toBe(TEST_MODEL_ID);
|
|
156
|
+
expect(request.messages[0].role).toBe('user');
|
|
157
|
+
expect(request.messages[0].content).toContain('User: user message');
|
|
158
|
+
expect(request.messages[0].content).toContain('Agent: assistant message');
|
|
159
|
+
// Recent messages should not be in the summarization request
|
|
160
|
+
expect(request.messages[0].content).not.toContain('recent');
|
|
161
|
+
});
|
|
162
|
+
it('returns error when LLM fails', async () => {
|
|
163
|
+
// No responses set, so LLM will fail
|
|
164
|
+
const messages = [
|
|
165
|
+
{ role: 'user', content: 'old 1' },
|
|
166
|
+
{ role: 'assistant', content: 'old 2' },
|
|
167
|
+
{ role: 'user', content: 'old 3' },
|
|
168
|
+
{ role: 'user', content: 'recent 1' },
|
|
169
|
+
{ role: 'assistant', content: 'recent 2' },
|
|
170
|
+
];
|
|
171
|
+
const result = await compactor.compact(sessionId, agentId, messages);
|
|
172
|
+
expect(result.ok).toBe(false);
|
|
173
|
+
if (result.ok)
|
|
174
|
+
return;
|
|
175
|
+
expect(result.error.message).toContain('Compaction failed');
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
// ============================================================================
|
|
179
|
+
// Tests: ContextCompactor.compactIfNeeded
|
|
180
|
+
// ============================================================================
|
|
181
|
+
describe('ContextCompactor.compactIfNeeded', () => {
|
|
182
|
+
let mockLLM;
|
|
183
|
+
let compactor;
|
|
184
|
+
let sessionId;
|
|
185
|
+
let agentId;
|
|
186
|
+
beforeEach(() => {
|
|
187
|
+
mockLLM = new MockLLMProvider();
|
|
188
|
+
compactor = new ContextCompactor(mockLLM, silentLogger, {
|
|
189
|
+
model: TEST_MODEL_ID,
|
|
190
|
+
maxTokens: 50,
|
|
191
|
+
keepRecentMessages: 1,
|
|
192
|
+
});
|
|
193
|
+
sessionId = generateSessionId();
|
|
194
|
+
agentId = generateTestAgentId();
|
|
195
|
+
});
|
|
196
|
+
it('returns null when compaction not needed', async () => {
|
|
197
|
+
const messages = [{ role: 'user', content: 'short' }];
|
|
198
|
+
const result = await compactor.compactIfNeeded(sessionId, agentId, messages);
|
|
199
|
+
expect(result.ok).toBe(true);
|
|
200
|
+
if (!result.ok)
|
|
201
|
+
return;
|
|
202
|
+
expect(result.value).toBeNull();
|
|
203
|
+
expect(mockLLM.calls.length).toBe(0);
|
|
204
|
+
});
|
|
205
|
+
it('compacts when needed', async () => {
|
|
206
|
+
mockLLM.setResponses([
|
|
207
|
+
{
|
|
208
|
+
content: 'Summary',
|
|
209
|
+
toolCalls: [],
|
|
210
|
+
finishReason: 'stop',
|
|
211
|
+
metrics: {
|
|
212
|
+
promptTokens: 50,
|
|
213
|
+
completionTokens: 20,
|
|
214
|
+
totalTokens: 70,
|
|
215
|
+
latencyMs: 100,
|
|
216
|
+
model: 'mock',
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
]);
|
|
220
|
+
// Create messages exceeding threshold
|
|
221
|
+
const messages = [
|
|
222
|
+
{ role: 'user', content: 'a'.repeat(100) },
|
|
223
|
+
{ role: 'assistant', content: 'b'.repeat(100) },
|
|
224
|
+
{ role: 'user', content: 'c'.repeat(100) },
|
|
225
|
+
];
|
|
226
|
+
const result = await compactor.compactIfNeeded(sessionId, agentId, messages);
|
|
227
|
+
expect(result.ok).toBe(true);
|
|
228
|
+
if (!result.ok)
|
|
229
|
+
return;
|
|
230
|
+
expect(result.value).not.toBeNull();
|
|
231
|
+
expect(result.value.summary).toBe('Summary');
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
// ============================================================================
|
|
235
|
+
// Tests: createContextCompactedEvent
|
|
236
|
+
// ============================================================================
|
|
237
|
+
describe('createContextCompactedEvent', () => {
|
|
238
|
+
it('creates event with correct fields', () => {
|
|
239
|
+
const sessionId = generateSessionId();
|
|
240
|
+
const agentId = generateTestAgentId();
|
|
241
|
+
const result = {
|
|
242
|
+
compactedMessages: [
|
|
243
|
+
{ role: 'system', content: 'summary' },
|
|
244
|
+
{ role: 'user', content: 'recent' },
|
|
245
|
+
],
|
|
246
|
+
summary: 'The summary',
|
|
247
|
+
originalTokens: 1000,
|
|
248
|
+
compactedTokens: 200,
|
|
249
|
+
messagesRemoved: 5,
|
|
250
|
+
};
|
|
251
|
+
const event = createContextCompactedEvent(sessionId, agentId, result);
|
|
252
|
+
expect(event.type).toBe('context_compacted');
|
|
253
|
+
expect(event.sessionId).toBe(sessionId);
|
|
254
|
+
expect(event.agentId).toBe(agentId);
|
|
255
|
+
expect(event.compactedContent).toBe('The summary');
|
|
256
|
+
expect(event.originalTokens).toBe(1000);
|
|
257
|
+
expect(event.compactedTokens).toBe(200);
|
|
258
|
+
expect(event.messagesRemoved).toBe(5);
|
|
259
|
+
expect(event.newConversationHistory.length).toBe(2);
|
|
260
|
+
expect(event.newConversationHistory[0].role).toBe('system');
|
|
261
|
+
expect(event.newConversationHistory[0].content).toBe('summary');
|
|
262
|
+
expect(event.timestamp).toBeDefined();
|
|
263
|
+
});
|
|
264
|
+
it('converts tool role to system in history', () => {
|
|
265
|
+
const sessionId = generateSessionId();
|
|
266
|
+
const agentId = generateTestAgentId();
|
|
267
|
+
const toolCallId = generateToolCallId();
|
|
268
|
+
const result = {
|
|
269
|
+
compactedMessages: [{ role: 'tool', content: 'tool result', toolCallId }],
|
|
270
|
+
summary: '',
|
|
271
|
+
originalTokens: 100,
|
|
272
|
+
compactedTokens: 50,
|
|
273
|
+
messagesRemoved: 0,
|
|
274
|
+
};
|
|
275
|
+
const event = createContextCompactedEvent(sessionId, agentId, result);
|
|
276
|
+
// tool role should be converted to system
|
|
277
|
+
expect(event.newConversationHistory[0].role).toBe('system');
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
// ============================================================================
|
|
281
|
+
// Tests: Default config
|
|
282
|
+
// ============================================================================
|
|
283
|
+
describe('ContextCompactor with custom config', () => {
|
|
284
|
+
it('respects custom maxTokens', () => {
|
|
285
|
+
const mockLLM = new MockLLMProvider();
|
|
286
|
+
const config = {
|
|
287
|
+
model: TEST_MODEL_ID,
|
|
288
|
+
maxTokens: 20,
|
|
289
|
+
keepRecentMessages: 1,
|
|
290
|
+
};
|
|
291
|
+
const compactor = new ContextCompactor(mockLLM, silentLogger, config);
|
|
292
|
+
const smallMessages = [{ role: 'user', content: 'hi' }];
|
|
293
|
+
expect(compactor.needsCompaction(smallMessages)).toBe(false);
|
|
294
|
+
const largeMessages = [
|
|
295
|
+
{ role: 'user', content: 'a'.repeat(100) },
|
|
296
|
+
];
|
|
297
|
+
expect(compactor.needsCompaction(largeMessages)).toBe(true);
|
|
298
|
+
});
|
|
299
|
+
it('respects custom keepRecentMessages', async () => {
|
|
300
|
+
const mockLLM = new MockLLMProvider();
|
|
301
|
+
mockLLM.setResponses([
|
|
302
|
+
{
|
|
303
|
+
content: 'Summary',
|
|
304
|
+
toolCalls: [],
|
|
305
|
+
finishReason: 'stop',
|
|
306
|
+
metrics: {
|
|
307
|
+
promptTokens: 50,
|
|
308
|
+
completionTokens: 20,
|
|
309
|
+
totalTokens: 70,
|
|
310
|
+
latencyMs: 100,
|
|
311
|
+
model: 'mock',
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
]);
|
|
315
|
+
const config = {
|
|
316
|
+
model: TEST_MODEL_ID,
|
|
317
|
+
maxTokens: 10,
|
|
318
|
+
keepRecentMessages: 3,
|
|
319
|
+
};
|
|
320
|
+
const compactor = new ContextCompactor(mockLLM, silentLogger, config);
|
|
321
|
+
const messages = [
|
|
322
|
+
{ role: 'user', content: 'old 1' },
|
|
323
|
+
{ role: 'assistant', content: 'old 2' },
|
|
324
|
+
{ role: 'user', content: 'recent 1' },
|
|
325
|
+
{ role: 'assistant', content: 'recent 2' },
|
|
326
|
+
{ role: 'user', content: 'recent 3' },
|
|
327
|
+
];
|
|
328
|
+
const result = await compactor.compact(generateSessionId(), generateTestAgentId(), messages);
|
|
329
|
+
expect(result.ok).toBe(true);
|
|
330
|
+
if (!result.ok)
|
|
331
|
+
return;
|
|
332
|
+
// 2 old messages removed, 3 kept + 1 summary = 4 total
|
|
333
|
+
expect(result.value.messagesRemoved).toBe(2);
|
|
334
|
+
expect(result.value.compactedMessages.length).toBe(4);
|
|
335
|
+
});
|
|
336
|
+
it('uses custom summaryPrompt', async () => {
|
|
337
|
+
const mockLLM = new MockLLMProvider();
|
|
338
|
+
mockLLM.setResponses([
|
|
339
|
+
{
|
|
340
|
+
content: 'Summary',
|
|
341
|
+
toolCalls: [],
|
|
342
|
+
finishReason: 'stop',
|
|
343
|
+
metrics: {
|
|
344
|
+
promptTokens: 50,
|
|
345
|
+
completionTokens: 20,
|
|
346
|
+
totalTokens: 70,
|
|
347
|
+
latencyMs: 100,
|
|
348
|
+
model: 'mock',
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
]);
|
|
352
|
+
const customPrompt = 'Custom summary instructions';
|
|
353
|
+
const config = {
|
|
354
|
+
model: TEST_MODEL_ID,
|
|
355
|
+
maxTokens: 10,
|
|
356
|
+
keepRecentMessages: 1,
|
|
357
|
+
summaryPrompt: customPrompt,
|
|
358
|
+
};
|
|
359
|
+
const compactor = new ContextCompactor(mockLLM, silentLogger, config);
|
|
360
|
+
const messages = [
|
|
361
|
+
{ role: 'user', content: 'old' },
|
|
362
|
+
{ role: 'user', content: 'recent' },
|
|
363
|
+
];
|
|
364
|
+
await compactor.compact(generateSessionId(), generateTestAgentId(), messages);
|
|
365
|
+
expect(mockLLM.calls.length).toBe(1);
|
|
366
|
+
expect(mockLLM.calls[0].systemPrompt).toBe(customPrompt);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
// ============================================================================
|
|
370
|
+
// Tests: formatMessageForSummary
|
|
371
|
+
// ============================================================================
|
|
372
|
+
describe('formatMessageForSummary', () => {
|
|
373
|
+
it('formats user message', () => {
|
|
374
|
+
const msg = { role: 'user', content: 'Hello world' };
|
|
375
|
+
expect(formatMessageForSummary(msg)).toBe('User: Hello world');
|
|
376
|
+
});
|
|
377
|
+
it('formats user message with multimodal content', () => {
|
|
378
|
+
const msg = {
|
|
379
|
+
role: 'user',
|
|
380
|
+
content: [{ type: 'text', text: 'Check this image' }],
|
|
381
|
+
};
|
|
382
|
+
const result = formatMessageForSummary(msg);
|
|
383
|
+
expect(result).toContain('User:');
|
|
384
|
+
expect(result).toContain('Check this image');
|
|
385
|
+
});
|
|
386
|
+
it('formats assistant message with text only', () => {
|
|
387
|
+
const msg = { role: 'assistant', content: 'Sure, I can help' };
|
|
388
|
+
expect(formatMessageForSummary(msg)).toBe('Agent: Sure, I can help');
|
|
389
|
+
});
|
|
390
|
+
it('formats assistant message with tool calls', () => {
|
|
391
|
+
const msg = {
|
|
392
|
+
role: 'assistant',
|
|
393
|
+
content: '',
|
|
394
|
+
toolCalls: [{ id: generateToolCallId(), name: 'read', input: { path: '/src/index.ts' } }],
|
|
395
|
+
};
|
|
396
|
+
const result = formatMessageForSummary(msg);
|
|
397
|
+
expect(result).toBe('Agent: [Called tools: read(path)]');
|
|
398
|
+
});
|
|
399
|
+
it('formats assistant message with text and tool calls', () => {
|
|
400
|
+
const msg = {
|
|
401
|
+
role: 'assistant',
|
|
402
|
+
content: 'Let me read that file.',
|
|
403
|
+
toolCalls: [{ id: generateToolCallId(), name: 'read', input: { path: '/src/index.ts' } }],
|
|
404
|
+
};
|
|
405
|
+
const result = formatMessageForSummary(msg);
|
|
406
|
+
expect(result).toContain('Agent: Let me read that file.');
|
|
407
|
+
expect(result).toContain('[Called tools: read(path)]');
|
|
408
|
+
});
|
|
409
|
+
it('formats assistant message with multiple tool calls', () => {
|
|
410
|
+
const msg = {
|
|
411
|
+
role: 'assistant',
|
|
412
|
+
content: '',
|
|
413
|
+
toolCalls: [
|
|
414
|
+
{ id: generateToolCallId(), name: 'read', input: { path: '/a.ts' } },
|
|
415
|
+
{ id: generateToolCallId(), name: 'edit', input: { path: '/b.ts', old_string: 'x', new_string: 'y' } },
|
|
416
|
+
],
|
|
417
|
+
};
|
|
418
|
+
const result = formatMessageForSummary(msg);
|
|
419
|
+
expect(result).toBe('Agent: [Called tools: read(path), edit(path, old_string, new_string)]');
|
|
420
|
+
});
|
|
421
|
+
it('formats tool result with tool name', () => {
|
|
422
|
+
const msg = {
|
|
423
|
+
role: 'tool',
|
|
424
|
+
content: 'export function main() {}',
|
|
425
|
+
toolCallId: generateToolCallId(),
|
|
426
|
+
toolName: 'read',
|
|
427
|
+
};
|
|
428
|
+
const result = formatMessageForSummary(msg);
|
|
429
|
+
expect(result).toBe('Tool(read): export function main() {}');
|
|
430
|
+
});
|
|
431
|
+
it('formats tool result without tool name as unknown', () => {
|
|
432
|
+
const msg = {
|
|
433
|
+
role: 'tool',
|
|
434
|
+
content: 'some result',
|
|
435
|
+
toolCallId: generateToolCallId(),
|
|
436
|
+
};
|
|
437
|
+
const result = formatMessageForSummary(msg);
|
|
438
|
+
expect(result).toBe('Tool(unknown): some result');
|
|
439
|
+
});
|
|
440
|
+
it('truncates large tool results', () => {
|
|
441
|
+
const largeContent = 'x'.repeat(1000);
|
|
442
|
+
const msg = {
|
|
443
|
+
role: 'tool',
|
|
444
|
+
content: largeContent,
|
|
445
|
+
toolCallId: generateToolCallId(),
|
|
446
|
+
toolName: 'read',
|
|
447
|
+
};
|
|
448
|
+
const result = formatMessageForSummary(msg);
|
|
449
|
+
expect(result).toContain('Tool(read):');
|
|
450
|
+
expect(result).toContain('...(truncated)...');
|
|
451
|
+
expect(result.length).toBeLessThan(largeContent.length);
|
|
452
|
+
});
|
|
453
|
+
it('does not truncate small tool results', () => {
|
|
454
|
+
const smallContent = 'small result';
|
|
455
|
+
const msg = {
|
|
456
|
+
role: 'tool',
|
|
457
|
+
content: smallContent,
|
|
458
|
+
toolCallId: generateToolCallId(),
|
|
459
|
+
toolName: 'read',
|
|
460
|
+
};
|
|
461
|
+
const result = formatMessageForSummary(msg);
|
|
462
|
+
expect(result).toBe('Tool(read): small result');
|
|
463
|
+
expect(result).not.toContain('truncated');
|
|
464
|
+
});
|
|
465
|
+
it('formats system message', () => {
|
|
466
|
+
const msg = { role: 'system', content: 'You are an assistant.' };
|
|
467
|
+
expect(formatMessageForSummary(msg)).toBe('System: You are an assistant.');
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
// ============================================================================
|
|
471
|
+
// Tests: Tool calls in compaction
|
|
472
|
+
// ============================================================================
|
|
473
|
+
describe('ContextCompactor with tool calls', () => {
|
|
474
|
+
let mockLLM;
|
|
475
|
+
let compactor;
|
|
476
|
+
let sessionId;
|
|
477
|
+
let agentId;
|
|
478
|
+
beforeEach(() => {
|
|
479
|
+
mockLLM = new MockLLMProvider();
|
|
480
|
+
compactor = new ContextCompactor(mockLLM, silentLogger, {
|
|
481
|
+
model: TEST_MODEL_ID,
|
|
482
|
+
maxTokens: 100,
|
|
483
|
+
keepRecentMessages: 1,
|
|
484
|
+
});
|
|
485
|
+
sessionId = generateSessionId();
|
|
486
|
+
agentId = generateTestAgentId();
|
|
487
|
+
});
|
|
488
|
+
it('does not leave orphaned tool results at the start of kept messages', async () => {
|
|
489
|
+
mockLLM.setResponses([
|
|
490
|
+
{
|
|
491
|
+
content: 'Summary',
|
|
492
|
+
toolCalls: [],
|
|
493
|
+
finishReason: 'stop',
|
|
494
|
+
metrics: { promptTokens: 50, completionTokens: 20, totalTokens: 70, latencyMs: 100, model: 'mock' },
|
|
495
|
+
},
|
|
496
|
+
]);
|
|
497
|
+
const toolCallId = generateToolCallId();
|
|
498
|
+
// keepRecentMessages=1 would split between the assistant (tool call) and tool result
|
|
499
|
+
const messages = [
|
|
500
|
+
{ role: 'user', content: 'old message' },
|
|
501
|
+
{ role: 'assistant', content: 'old response' },
|
|
502
|
+
{ role: 'user', content: 'Read the file' },
|
|
503
|
+
{
|
|
504
|
+
role: 'assistant',
|
|
505
|
+
content: '',
|
|
506
|
+
toolCalls: [{ id: toolCallId, name: 'read', input: { path: '/src/index.ts' } }],
|
|
507
|
+
},
|
|
508
|
+
{ role: 'tool', content: 'export const foo = 1', toolCallId, toolName: 'read' },
|
|
509
|
+
];
|
|
510
|
+
const result = await compactor.compact(sessionId, agentId, messages);
|
|
511
|
+
expect(result.ok).toBe(true);
|
|
512
|
+
if (!result.ok)
|
|
513
|
+
return;
|
|
514
|
+
// The tool result must NOT be the first kept message — it should be compacted along with its tool call
|
|
515
|
+
const keptMessages = result.value.compactedMessages.filter(m => m.role !== 'system');
|
|
516
|
+
for (const msg of keptMessages) {
|
|
517
|
+
expect(msg.role).not.toBe('tool');
|
|
518
|
+
}
|
|
519
|
+
// All 5 original messages should be compacted (none kept except summary)
|
|
520
|
+
expect(result.value.messagesRemoved).toBe(5);
|
|
521
|
+
});
|
|
522
|
+
it('includes tool calls in summarization request', async () => {
|
|
523
|
+
mockLLM.setResponses([
|
|
524
|
+
{
|
|
525
|
+
content: 'Summary',
|
|
526
|
+
toolCalls: [],
|
|
527
|
+
finishReason: 'stop',
|
|
528
|
+
metrics: { promptTokens: 50, completionTokens: 20, totalTokens: 70, latencyMs: 100, model: 'mock' },
|
|
529
|
+
},
|
|
530
|
+
]);
|
|
531
|
+
const toolCallId = generateToolCallId();
|
|
532
|
+
const messages = [
|
|
533
|
+
{ role: 'user', content: 'Read the file' },
|
|
534
|
+
{
|
|
535
|
+
role: 'assistant',
|
|
536
|
+
content: '',
|
|
537
|
+
toolCalls: [{ id: toolCallId, name: 'read', input: { path: '/src/index.ts' } }],
|
|
538
|
+
},
|
|
539
|
+
{ role: 'tool', content: 'export const foo = 1', toolCallId, toolName: 'read' },
|
|
540
|
+
{ role: 'user', content: 'recent message' },
|
|
541
|
+
];
|
|
542
|
+
await compactor.compact(sessionId, agentId, messages);
|
|
543
|
+
expect(mockLLM.calls.length).toBe(1);
|
|
544
|
+
const request = mockLLM.calls[0];
|
|
545
|
+
const summaryContent = request.messages[0].content;
|
|
546
|
+
// Verify tool call is included
|
|
547
|
+
expect(summaryContent).toContain('[Called tools: read(path)]');
|
|
548
|
+
// Verify tool result includes tool name
|
|
549
|
+
expect(summaryContent).toContain('Tool(read):');
|
|
550
|
+
expect(summaryContent).toContain('export const foo = 1');
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
// ============================================================================
|
|
554
|
+
// Tests: History offloading
|
|
555
|
+
// ============================================================================
|
|
556
|
+
describe('ContextCompactor with history offloading', () => {
|
|
557
|
+
let mockLLM;
|
|
558
|
+
let sessionId;
|
|
559
|
+
let agentId;
|
|
560
|
+
beforeEach(() => {
|
|
561
|
+
mockLLM = new MockLLMProvider();
|
|
562
|
+
sessionId = generateSessionId();
|
|
563
|
+
agentId = generateTestAgentId();
|
|
564
|
+
});
|
|
565
|
+
it('offloads history when enabled and offloader is provided', async () => {
|
|
566
|
+
const offloadedPaths = [];
|
|
567
|
+
const mockOffloader = {
|
|
568
|
+
async offload(agentId, content, pathPrefix) {
|
|
569
|
+
offloadedPaths.push({ agentId, content, pathPrefix });
|
|
570
|
+
return `${pathPrefix}${agentId}/history.md`;
|
|
571
|
+
},
|
|
572
|
+
};
|
|
573
|
+
mockLLM.setResponses([
|
|
574
|
+
{
|
|
575
|
+
content: 'Summary',
|
|
576
|
+
toolCalls: [],
|
|
577
|
+
finishReason: 'stop',
|
|
578
|
+
metrics: { promptTokens: 50, completionTokens: 20, totalTokens: 70, latencyMs: 100, model: 'mock' },
|
|
579
|
+
},
|
|
580
|
+
]);
|
|
581
|
+
const compactor = new ContextCompactor(mockLLM, silentLogger, {
|
|
582
|
+
model: TEST_MODEL_ID,
|
|
583
|
+
maxTokens: 100,
|
|
584
|
+
keepRecentMessages: 1,
|
|
585
|
+
offloadHistory: true,
|
|
586
|
+
}, mockOffloader);
|
|
587
|
+
const messages = [
|
|
588
|
+
{ role: 'user', content: 'old message 1' },
|
|
589
|
+
{ role: 'assistant', content: 'old message 2' },
|
|
590
|
+
{ role: 'user', content: 'recent message' },
|
|
591
|
+
];
|
|
592
|
+
const result = await compactor.compact(sessionId, agentId, messages);
|
|
593
|
+
expect(result.ok).toBe(true);
|
|
594
|
+
if (!result.ok)
|
|
595
|
+
return;
|
|
596
|
+
// Verify offloader was called
|
|
597
|
+
expect(offloadedPaths.length).toBe(1);
|
|
598
|
+
expect(offloadedPaths[0].agentId).toBe(agentId);
|
|
599
|
+
expect(offloadedPaths[0].content).toContain('User: old message 1');
|
|
600
|
+
expect(offloadedPaths[0].pathPrefix).toBe('/session/.history/');
|
|
601
|
+
// Verify result contains historyPath
|
|
602
|
+
expect(result.value.historyPath).toBe(`/session/.history/${agentId}/history.md`);
|
|
603
|
+
// Verify summary message contains history reference
|
|
604
|
+
expect(result.value.compactedMessages[0].content).toContain('has been saved to');
|
|
605
|
+
expect(result.value.compactedMessages[0].content).toContain(`/session/.history/${agentId}/history.md`);
|
|
606
|
+
});
|
|
607
|
+
it('does not offload history when disabled', async () => {
|
|
608
|
+
const mockOffloader = {
|
|
609
|
+
async offload() {
|
|
610
|
+
throw new Error('Should not be called');
|
|
611
|
+
},
|
|
612
|
+
};
|
|
613
|
+
mockLLM.setResponses([
|
|
614
|
+
{
|
|
615
|
+
content: 'Summary',
|
|
616
|
+
toolCalls: [],
|
|
617
|
+
finishReason: 'stop',
|
|
618
|
+
metrics: { promptTokens: 50, completionTokens: 20, totalTokens: 70, latencyMs: 100, model: 'mock' },
|
|
619
|
+
},
|
|
620
|
+
]);
|
|
621
|
+
const compactor = new ContextCompactor(mockLLM, silentLogger, {
|
|
622
|
+
model: TEST_MODEL_ID,
|
|
623
|
+
maxTokens: 100,
|
|
624
|
+
keepRecentMessages: 1,
|
|
625
|
+
offloadHistory: false, // explicitly disabled
|
|
626
|
+
}, mockOffloader);
|
|
627
|
+
const messages = [
|
|
628
|
+
{ role: 'user', content: 'old' },
|
|
629
|
+
{ role: 'user', content: 'recent' },
|
|
630
|
+
];
|
|
631
|
+
const result = await compactor.compact(sessionId, agentId, messages);
|
|
632
|
+
expect(result.ok).toBe(true);
|
|
633
|
+
if (!result.ok)
|
|
634
|
+
return;
|
|
635
|
+
expect(result.value.historyPath).toBeUndefined();
|
|
636
|
+
expect(result.value.compactedMessages[0].content).not.toContain('has been saved to');
|
|
637
|
+
});
|
|
638
|
+
it('does not offload history when offloader is not provided', async () => {
|
|
639
|
+
mockLLM.setResponses([
|
|
640
|
+
{
|
|
641
|
+
content: 'Summary',
|
|
642
|
+
toolCalls: [],
|
|
643
|
+
finishReason: 'stop',
|
|
644
|
+
metrics: { promptTokens: 50, completionTokens: 20, totalTokens: 70, latencyMs: 100, model: 'mock' },
|
|
645
|
+
},
|
|
646
|
+
]);
|
|
647
|
+
const compactor = new ContextCompactor(mockLLM, silentLogger, {
|
|
648
|
+
model: TEST_MODEL_ID,
|
|
649
|
+
maxTokens: 100,
|
|
650
|
+
keepRecentMessages: 1,
|
|
651
|
+
offloadHistory: true, // enabled but no offloader
|
|
652
|
+
});
|
|
653
|
+
const messages = [
|
|
654
|
+
{ role: 'user', content: 'old' },
|
|
655
|
+
{ role: 'user', content: 'recent' },
|
|
656
|
+
];
|
|
657
|
+
const result = await compactor.compact(sessionId, agentId, messages);
|
|
658
|
+
expect(result.ok).toBe(true);
|
|
659
|
+
if (!result.ok)
|
|
660
|
+
return;
|
|
661
|
+
expect(result.value.historyPath).toBeUndefined();
|
|
662
|
+
});
|
|
663
|
+
it('uses custom historyPathPrefix', async () => {
|
|
664
|
+
const offloadedPaths = [];
|
|
665
|
+
const mockOffloader = {
|
|
666
|
+
async offload(_agentId, _content, pathPrefix) {
|
|
667
|
+
offloadedPaths.push({ pathPrefix });
|
|
668
|
+
return `/custom/path/history.md`;
|
|
669
|
+
},
|
|
670
|
+
};
|
|
671
|
+
mockLLM.setResponses([
|
|
672
|
+
{
|
|
673
|
+
content: 'Summary',
|
|
674
|
+
toolCalls: [],
|
|
675
|
+
finishReason: 'stop',
|
|
676
|
+
metrics: { promptTokens: 50, completionTokens: 20, totalTokens: 70, latencyMs: 100, model: 'mock' },
|
|
677
|
+
},
|
|
678
|
+
]);
|
|
679
|
+
const compactor = new ContextCompactor(mockLLM, silentLogger, {
|
|
680
|
+
model: TEST_MODEL_ID,
|
|
681
|
+
maxTokens: 100,
|
|
682
|
+
keepRecentMessages: 1,
|
|
683
|
+
offloadHistory: true,
|
|
684
|
+
historyPathPrefix: '/session/.custom-history/',
|
|
685
|
+
}, mockOffloader);
|
|
686
|
+
const messages = [
|
|
687
|
+
{ role: 'user', content: 'old' },
|
|
688
|
+
{ role: 'user', content: 'recent' },
|
|
689
|
+
];
|
|
690
|
+
await compactor.compact(sessionId, agentId, messages);
|
|
691
|
+
expect(offloadedPaths.length).toBe(1);
|
|
692
|
+
expect(offloadedPaths[0].pathPrefix).toBe('/session/.custom-history/');
|
|
693
|
+
});
|
|
694
|
+
it('continues compaction even if offloading fails', async () => {
|
|
695
|
+
const mockOffloader = {
|
|
696
|
+
async offload() {
|
|
697
|
+
throw new Error('Disk full');
|
|
698
|
+
},
|
|
699
|
+
};
|
|
700
|
+
mockLLM.setResponses([
|
|
701
|
+
{
|
|
702
|
+
content: 'Summary despite offload failure',
|
|
703
|
+
toolCalls: [],
|
|
704
|
+
finishReason: 'stop',
|
|
705
|
+
metrics: { promptTokens: 50, completionTokens: 20, totalTokens: 70, latencyMs: 100, model: 'mock' },
|
|
706
|
+
},
|
|
707
|
+
]);
|
|
708
|
+
const compactor = new ContextCompactor(mockLLM, silentLogger, {
|
|
709
|
+
model: TEST_MODEL_ID,
|
|
710
|
+
maxTokens: 100,
|
|
711
|
+
keepRecentMessages: 1,
|
|
712
|
+
offloadHistory: true,
|
|
713
|
+
}, mockOffloader);
|
|
714
|
+
const messages = [
|
|
715
|
+
{ role: 'user', content: 'old' },
|
|
716
|
+
{ role: 'user', content: 'recent' },
|
|
717
|
+
];
|
|
718
|
+
const result = await compactor.compact(sessionId, agentId, messages);
|
|
719
|
+
// Compaction should succeed despite offload failure
|
|
720
|
+
expect(result.ok).toBe(true);
|
|
721
|
+
if (!result.ok)
|
|
722
|
+
return;
|
|
723
|
+
expect(result.value.summary).toBe('Summary despite offload failure');
|
|
724
|
+
expect(result.value.historyPath).toBeUndefined();
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
// ============================================================================
|
|
728
|
+
// Tests: createContextCompactedEvent with historyPath
|
|
729
|
+
// ============================================================================
|
|
730
|
+
describe('createContextCompactedEvent with historyPath', () => {
|
|
731
|
+
it('includes historyPath in event when provided', () => {
|
|
732
|
+
const sessionId = generateSessionId();
|
|
733
|
+
const agentId = generateTestAgentId();
|
|
734
|
+
const result = {
|
|
735
|
+
compactedMessages: [
|
|
736
|
+
{ role: 'system', content: 'summary' },
|
|
737
|
+
],
|
|
738
|
+
summary: 'The summary',
|
|
739
|
+
originalTokens: 1000,
|
|
740
|
+
compactedTokens: 200,
|
|
741
|
+
messagesRemoved: 5,
|
|
742
|
+
historyPath: '/session/.history/agent-1/history.md',
|
|
743
|
+
};
|
|
744
|
+
const event = createContextCompactedEvent(sessionId, agentId, result);
|
|
745
|
+
expect(event.historyPath).toBe('/session/.history/agent-1/history.md');
|
|
746
|
+
});
|
|
747
|
+
it('does not include historyPath when not provided', () => {
|
|
748
|
+
const sessionId = generateSessionId();
|
|
749
|
+
const agentId = generateTestAgentId();
|
|
750
|
+
const result = {
|
|
751
|
+
compactedMessages: [
|
|
752
|
+
{ role: 'system', content: 'summary' },
|
|
753
|
+
],
|
|
754
|
+
summary: 'The summary',
|
|
755
|
+
originalTokens: 1000,
|
|
756
|
+
compactedTokens: 200,
|
|
757
|
+
messagesRemoved: 5,
|
|
758
|
+
};
|
|
759
|
+
const event = createContextCompactedEvent(sessionId, agentId, result);
|
|
760
|
+
expect(event.historyPath).toBeUndefined();
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
//# sourceMappingURL=context-compactor.test.js.map
|