@clinebot/core 0.0.35 → 0.0.37
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 +1 -2
- package/dist/ClineCore.d.ts +362 -39
- package/dist/ClineCore.d.ts.map +1 -1
- package/dist/account/cline-account-service.d.ts.map +1 -1
- package/dist/account/index.d.ts +1 -1
- package/dist/account/index.d.ts.map +1 -1
- package/dist/account/rpc.d.ts +6 -6
- package/dist/account/rpc.d.ts.map +1 -1
- package/dist/cron/cron-event-ingress.d.ts +38 -0
- package/dist/cron/cron-event-ingress.d.ts.map +1 -0
- package/dist/cron/cron-materializer.d.ts +36 -0
- package/dist/cron/cron-materializer.d.ts.map +1 -0
- package/dist/cron/cron-reconciler.d.ts +62 -0
- package/dist/cron/cron-reconciler.d.ts.map +1 -0
- package/dist/cron/cron-report-writer.d.ts +41 -0
- package/dist/cron/cron-report-writer.d.ts.map +1 -0
- package/dist/cron/cron-runner.d.ts +43 -0
- package/dist/cron/cron-runner.d.ts.map +1 -0
- package/dist/cron/cron-schema.d.ts +3 -0
- package/dist/cron/cron-schema.d.ts.map +1 -0
- package/dist/cron/cron-service.d.ts +57 -0
- package/dist/cron/cron-service.d.ts.map +1 -0
- package/dist/cron/cron-spec-parser.d.ts +27 -0
- package/dist/cron/cron-spec-parser.d.ts.map +1 -0
- package/dist/cron/cron-watcher.d.ts +23 -0
- package/dist/cron/cron-watcher.d.ts.map +1 -0
- package/dist/cron/resource-limiter.d.ts +9 -0
- package/dist/cron/resource-limiter.d.ts.map +1 -0
- package/dist/cron/schedule-command-service.d.ts +10 -0
- package/dist/cron/schedule-command-service.d.ts.map +1 -0
- package/dist/cron/schedule-service.d.ts +100 -0
- package/dist/cron/schedule-service.d.ts.map +1 -0
- package/dist/cron/scheduler.d.ts +68 -0
- package/dist/cron/scheduler.d.ts.map +1 -0
- package/dist/cron/sqlite-cron-store.d.ts +230 -0
- package/dist/cron/sqlite-cron-store.d.ts.map +1 -0
- package/dist/cron/sqlite-schedule-store.d.ts +52 -0
- package/dist/cron/sqlite-schedule-store.d.ts.map +1 -0
- package/dist/extensions/config/agent-config-loader.d.ts +4 -3
- package/dist/extensions/config/agent-config-loader.d.ts.map +1 -1
- package/dist/extensions/config/runtime-commands.d.ts +1 -0
- package/dist/extensions/config/runtime-commands.d.ts.map +1 -1
- package/dist/extensions/config/user-instruction-config-loader.d.ts +1 -0
- package/dist/extensions/config/user-instruction-config-loader.d.ts.map +1 -1
- package/dist/extensions/context/agentic-compaction.d.ts +2 -2
- package/dist/extensions/context/agentic-compaction.d.ts.map +1 -1
- package/dist/extensions/context/compaction-shared.d.ts +5 -4
- package/dist/extensions/context/compaction-shared.d.ts.map +1 -1
- package/dist/extensions/context/compaction.d.ts.map +1 -1
- package/dist/extensions/plugin/plugin-config-loader.d.ts +15 -2
- package/dist/extensions/plugin/plugin-config-loader.d.ts.map +1 -1
- package/dist/extensions/plugin/plugin-loader.d.ts +13 -7
- package/dist/extensions/plugin/plugin-loader.d.ts.map +1 -1
- package/dist/extensions/plugin/plugin-module-import.d.ts.map +1 -1
- package/dist/extensions/plugin/plugin-sandbox.d.ts +21 -2
- package/dist/extensions/plugin/plugin-sandbox.d.ts.map +1 -1
- package/dist/extensions/plugin/plugin-targeting.d.ts +7 -0
- package/dist/extensions/plugin/plugin-targeting.d.ts.map +1 -0
- package/dist/extensions/plugin-sandbox-bootstrap.js +237 -276
- package/dist/extensions/tools/constants.d.ts +1 -0
- package/dist/extensions/tools/constants.d.ts.map +1 -1
- package/dist/extensions/tools/definitions.d.ts +3 -4
- package/dist/extensions/tools/definitions.d.ts.map +1 -1
- package/dist/extensions/tools/executors/apply-patch.d.ts +3 -1
- package/dist/extensions/tools/executors/apply-patch.d.ts.map +1 -1
- package/dist/extensions/tools/executors/editor.d.ts.map +1 -1
- package/dist/extensions/tools/executors/search.d.ts +1 -1
- package/dist/extensions/tools/executors/search.d.ts.map +1 -1
- package/dist/extensions/tools/helpers.d.ts +1 -0
- package/dist/extensions/tools/helpers.d.ts.map +1 -1
- package/dist/extensions/tools/index.d.ts +3 -2
- package/dist/extensions/tools/index.d.ts.map +1 -1
- package/dist/extensions/tools/presets.d.ts +27 -44
- package/dist/extensions/tools/presets.d.ts.map +1 -1
- package/dist/extensions/tools/runtime.d.ts +25 -0
- package/dist/extensions/tools/runtime.d.ts.map +1 -0
- package/dist/extensions/tools/schemas.d.ts +25 -3
- package/dist/extensions/tools/schemas.d.ts.map +1 -1
- package/dist/extensions/tools/team/delegated-agent.d.ts +2 -2
- package/dist/extensions/tools/team/delegated-agent.d.ts.map +1 -1
- package/dist/extensions/tools/team/multi-agent.d.ts +7 -3
- package/dist/extensions/tools/team/multi-agent.d.ts.map +1 -1
- package/dist/extensions/tools/team/team-tools.d.ts +1 -0
- package/dist/extensions/tools/team/team-tools.d.ts.map +1 -1
- package/dist/extensions/tools/types.d.ts +0 -5
- package/dist/extensions/tools/types.d.ts.map +1 -1
- package/dist/hooks/hook-bridge.d.ts +118 -0
- package/dist/hooks/hook-bridge.d.ts.map +1 -0
- package/dist/hooks/hook-file-hooks.d.ts +6 -2
- package/dist/hooks/hook-file-hooks.d.ts.map +1 -1
- package/dist/hooks/hook-registry.d.ts +16 -0
- package/dist/hooks/hook-registry.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +0 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/subprocess.d.ts +8 -1
- package/dist/hooks/subprocess.d.ts.map +1 -1
- package/dist/hub/browser-websocket.d.ts +18 -0
- package/dist/hub/browser-websocket.d.ts.map +1 -0
- package/dist/hub/client.d.ts +51 -0
- package/dist/hub/client.d.ts.map +1 -0
- package/dist/hub/connect.d.ts +15 -0
- package/dist/hub/connect.d.ts.map +1 -0
- package/dist/hub/daemon-entry.d.ts +2 -0
- package/dist/hub/daemon-entry.d.ts.map +1 -0
- package/dist/hub/daemon-entry.js +1305 -0
- package/dist/hub/daemon.d.ts +5 -0
- package/dist/hub/daemon.d.ts.map +1 -0
- package/dist/hub/defaults.d.ts +17 -0
- package/dist/hub/defaults.d.ts.map +1 -0
- package/dist/hub/discovery.d.ts +29 -0
- package/dist/hub/discovery.d.ts.map +1 -0
- package/dist/hub/index.d.ts +15 -0
- package/dist/hub/index.d.ts.map +1 -0
- package/dist/hub/index.js +1294 -0
- package/dist/hub/native-transport.d.ts +17 -0
- package/dist/hub/native-transport.d.ts.map +1 -0
- package/dist/hub/runtime-handlers.d.ts +11 -0
- package/dist/hub/runtime-handlers.d.ts.map +1 -0
- package/dist/hub/server.d.ts +104 -0
- package/dist/hub/server.d.ts.map +1 -0
- package/dist/hub/session-client.d.ts +90 -0
- package/dist/hub/session-client.d.ts.map +1 -0
- package/dist/hub/start-shared-server.d.ts +19 -0
- package/dist/hub/start-shared-server.d.ts.map +1 -0
- package/dist/hub/transport.d.ts +8 -0
- package/dist/hub/transport.d.ts.map +1 -0
- package/dist/hub/ui-client.d.ts +45 -0
- package/dist/hub/ui-client.d.ts.map +1 -0
- package/dist/hub/workspace.d.ts +4 -0
- package/dist/hub/workspace.d.ts.map +1 -0
- package/dist/index.d.ts +29 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +782 -471
- package/dist/llms/cline-recommended-models.d.ts +20 -0
- package/dist/llms/cline-recommended-models.d.ts.map +1 -0
- package/dist/llms/configured-provider-registry.d.ts +28 -0
- package/dist/llms/configured-provider-registry.d.ts.map +1 -0
- package/dist/llms/handler-factory.d.ts +16 -0
- package/dist/llms/handler-factory.d.ts.map +1 -0
- package/dist/llms/provider-defaults.d.ts +27 -0
- package/dist/llms/provider-defaults.d.ts.map +1 -0
- package/dist/llms/provider-settings.d.ts +245 -0
- package/dist/llms/provider-settings.d.ts.map +1 -0
- package/dist/llms/runtime-config.d.ts +4 -0
- package/dist/llms/runtime-config.d.ts.map +1 -0
- package/dist/llms/runtime-registry.d.ts +20 -0
- package/dist/llms/runtime-registry.d.ts.map +1 -0
- package/dist/llms/runtime-types.d.ts +85 -0
- package/dist/llms/runtime-types.d.ts.map +1 -0
- package/dist/runtime/agent-config-adapter.d.ts +148 -0
- package/dist/runtime/agent-config-adapter.d.ts.map +1 -0
- package/dist/runtime/agent-runtime-config-builder.d.ts +96 -0
- package/dist/runtime/agent-runtime-config-builder.d.ts.map +1 -0
- package/dist/runtime/history.d.ts +6 -0
- package/dist/runtime/history.d.ts.map +1 -1
- package/dist/runtime/host.d.ts +1 -2
- package/dist/runtime/host.d.ts.map +1 -1
- package/dist/runtime/loop-detection.d.ts +59 -0
- package/dist/runtime/loop-detection.d.ts.map +1 -0
- package/dist/runtime/mistake-tracker.d.ts +69 -0
- package/dist/runtime/mistake-tracker.d.ts.map +1 -0
- package/dist/runtime/rules.d.ts +1 -0
- package/dist/runtime/rules.d.ts.map +1 -1
- package/dist/runtime/runtime-builder.d.ts.map +1 -1
- package/dist/runtime/runtime-event-adapter.d.ts +102 -0
- package/dist/runtime/runtime-event-adapter.d.ts.map +1 -0
- package/dist/runtime/runtime-host.d.ts +49 -26
- package/dist/runtime/runtime-host.d.ts.map +1 -1
- package/dist/runtime/runtime-oauth-token-manager.d.ts.map +1 -1
- package/dist/runtime/session-runtime-orchestrator.d.ts +261 -0
- package/dist/runtime/session-runtime-orchestrator.d.ts.map +1 -0
- package/dist/runtime/session-runtime.d.ts +16 -21
- package/dist/runtime/session-runtime.d.ts.map +1 -1
- package/dist/runtime/user-input-builder.d.ts +24 -0
- package/dist/runtime/user-input-builder.d.ts.map +1 -0
- package/dist/services/global-settings.d.ts +12 -0
- package/dist/services/global-settings.d.ts.map +1 -0
- package/dist/services/index.js +28 -0
- package/dist/services/local-runtime-bootstrap.d.ts +9 -3
- package/dist/services/local-runtime-bootstrap.d.ts.map +1 -1
- package/dist/services/plugin-tools.d.ts +16 -0
- package/dist/services/plugin-tools.d.ts.map +1 -0
- package/dist/services/providers/local-provider-registry.d.ts +199 -23
- package/dist/services/providers/local-provider-registry.d.ts.map +1 -1
- package/dist/services/providers/local-provider-service.d.ts +15 -13
- package/dist/services/providers/local-provider-service.d.ts.map +1 -1
- package/dist/services/session-data.d.ts +1 -1
- package/dist/services/session-data.d.ts.map +1 -1
- package/dist/services/session-telemetry.d.ts +7 -2
- package/dist/services/session-telemetry.d.ts.map +1 -1
- package/dist/services/storage/file-team-store.d.ts.map +1 -1
- package/dist/services/storage/provider-settings-legacy-migration.d.ts +1 -1
- package/dist/services/storage/provider-settings-legacy-migration.d.ts.map +1 -1
- package/dist/services/storage/provider-settings-manager.d.ts +1 -0
- package/dist/services/storage/provider-settings-manager.d.ts.map +1 -1
- package/dist/services/storage/sqlite-team-store.d.ts.map +1 -1
- package/dist/services/workspace-manifest.d.ts +11 -0
- package/dist/services/workspace-manifest.d.ts.map +1 -1
- package/dist/session/conversation-store.d.ts +30 -0
- package/dist/session/conversation-store.d.ts.map +1 -0
- package/dist/session/message-builder.d.ts +65 -0
- package/dist/session/message-builder.d.ts.map +1 -0
- package/dist/session/persistence-service.d.ts +11 -23
- package/dist/session/persistence-service.d.ts.map +1 -1
- package/dist/session/session-manifest-store.d.ts +22 -0
- package/dist/session/session-manifest-store.d.ts.map +1 -0
- package/dist/session/session-manifest.d.ts +1 -1
- package/dist/session/session-row.d.ts +93 -0
- package/dist/session/session-row.d.ts.map +1 -0
- package/dist/session/session-service.d.ts +2 -102
- package/dist/session/session-service.d.ts.map +1 -1
- package/dist/session/subagent-session-manager.d.ts +36 -0
- package/dist/session/subagent-session-manager.d.ts.map +1 -0
- package/dist/session/team-persistence-store.d.ts +24 -0
- package/dist/session/team-persistence-store.d.ts.map +1 -0
- package/dist/transports/hub.d.ts +58 -0
- package/dist/transports/hub.d.ts.map +1 -0
- package/dist/transports/local.d.ts +23 -9
- package/dist/transports/local.d.ts.map +1 -1
- package/dist/transports/remote.d.ts +10 -0
- package/dist/transports/remote.d.ts.map +1 -0
- package/dist/transports/runtime-host-support.d.ts +3 -2
- package/dist/transports/runtime-host-support.d.ts.map +1 -1
- package/dist/types/chat-schema.d.ts +15 -17
- package/dist/types/chat-schema.d.ts.map +1 -1
- package/dist/types/config.d.ts +17 -7
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/events.d.ts +7 -6
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/provider-settings.d.ts +4 -5
- package/dist/types/provider-settings.d.ts.map +1 -1
- package/dist/types/session.d.ts +7 -3
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types.d.ts +11 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +20 -6
- package/src/ClineCore.ts +757 -44
- package/src/account/cline-account-service.ts +44 -6
- package/src/account/index.ts +3 -3
- package/src/account/rpc.ts +12 -12
- package/src/cron/cron-event-ingress.ts +357 -0
- package/src/cron/cron-materializer.ts +97 -0
- package/src/cron/cron-reconciler.ts +241 -0
- package/src/cron/cron-report-writer.ts +153 -0
- package/src/cron/cron-runner.ts +495 -0
- package/src/cron/cron-schema.ts +127 -0
- package/src/cron/cron-service.ts +163 -0
- package/src/cron/cron-spec-parser.ts +489 -0
- package/src/cron/cron-watcher.ts +102 -0
- package/src/cron/index.ts +15 -0
- package/src/cron/resource-limiter.ts +46 -0
- package/src/cron/schedule-command-service.ts +193 -0
- package/src/cron/schedule-service.ts +703 -0
- package/src/cron/scheduler.ts +772 -0
- package/src/cron/sqlite-cron-store.ts +1286 -0
- package/src/cron/sqlite-schedule-store.ts +708 -0
- package/src/extensions/config/agent-config-loader.ts +17 -7
- package/src/extensions/config/runtime-commands.ts +6 -0
- package/src/extensions/config/user-instruction-config-loader.ts +1 -0
- package/src/extensions/context/agentic-compaction.ts +3 -3
- package/src/extensions/context/basic-compaction.ts +2 -2
- package/src/extensions/context/compaction-shared.ts +5 -4
- package/src/extensions/context/compaction.ts +3 -3
- package/src/extensions/plugin/plugin-config-loader.ts +37 -2
- package/src/extensions/plugin/plugin-loader.ts +69 -9
- package/src/extensions/plugin/plugin-module-import.ts +0 -2
- package/src/extensions/plugin/plugin-sandbox-bootstrap.ts +243 -39
- package/src/extensions/plugin/plugin-sandbox.ts +173 -29
- package/src/extensions/plugin/plugin-targeting.ts +32 -0
- package/src/extensions/tools/constants.ts +2 -0
- package/src/extensions/tools/definitions.ts +61 -71
- package/src/extensions/tools/executors/apply-patch.ts +69 -80
- package/src/extensions/tools/executors/editor.ts +4 -3
- package/src/extensions/tools/executors/search.ts +195 -3
- package/src/extensions/tools/helpers.ts +24 -0
- package/src/extensions/tools/index.ts +11 -2
- package/src/extensions/tools/presets.ts +32 -47
- package/src/extensions/tools/runtime.ts +261 -0
- package/src/extensions/tools/schemas.ts +17 -20
- package/src/extensions/tools/team/delegated-agent.ts +8 -3
- package/src/extensions/tools/team/multi-agent.ts +135 -19
- package/src/extensions/tools/team/team-tools.ts +172 -91
- package/src/extensions/tools/types.ts +0 -6
- package/src/hooks/hook-bridge.ts +489 -0
- package/src/hooks/hook-file-hooks.ts +66 -5
- package/src/hooks/hook-registry.ts +257 -0
- package/src/hooks/index.ts +0 -7
- package/src/hooks/subprocess-runner.ts +1 -1
- package/src/hooks/subprocess.ts +9 -0
- package/src/hub/browser-websocket.ts +159 -0
- package/src/hub/client.ts +633 -0
- package/src/hub/connect.ts +156 -0
- package/src/hub/daemon-entry.ts +122 -0
- package/src/hub/daemon.ts +284 -0
- package/src/hub/defaults.ts +70 -0
- package/src/hub/discovery.ts +247 -0
- package/src/hub/index.ts +14 -0
- package/src/hub/native-transport.ts +31 -0
- package/src/hub/runtime-handlers.ts +141 -0
- package/src/hub/server.ts +2317 -0
- package/src/hub/session-client.ts +502 -0
- package/src/hub/start-shared-server.ts +61 -0
- package/src/hub/transport.ts +14 -0
- package/src/hub/ui-client.ts +126 -0
- package/src/hub/workspace.ts +19 -0
- package/src/index.ts +169 -68
- package/src/llms/cline-recommended-models.ts +167 -0
- package/src/llms/configured-provider-registry.ts +193 -0
- package/src/llms/handler-factory.ts +56 -0
- package/src/llms/provider-defaults.ts +653 -0
- package/src/llms/provider-settings.ts +310 -0
- package/src/llms/runtime-config.ts +43 -0
- package/src/llms/runtime-registry.ts +172 -0
- package/src/llms/runtime-types.ts +121 -0
- package/src/runtime/agent-config-adapter.ts +636 -0
- package/src/runtime/agent-runtime-config-builder.ts +205 -0
- package/src/runtime/error-feedback.ts +142 -0
- package/src/runtime/history.ts +137 -0
- package/src/runtime/host.ts +127 -267
- package/src/runtime/index.ts +1 -0
- package/src/runtime/loop-detection.ts +162 -0
- package/src/runtime/mistake-tracker.ts +221 -0
- package/src/runtime/rules.ts +12 -0
- package/src/runtime/runtime-builder.ts +85 -13
- package/src/runtime/runtime-event-adapter.ts +412 -0
- package/src/runtime/runtime-host.ts +134 -62
- package/src/runtime/runtime-oauth-token-manager.ts +11 -15
- package/src/runtime/session-runtime-orchestrator.ts +1253 -0
- package/src/runtime/session-runtime.ts +16 -26
- package/src/runtime/user-input-builder.ts +167 -0
- package/src/services/global-settings.ts +122 -0
- package/src/services/local-runtime-bootstrap.ts +175 -31
- package/src/services/plugin-tools.ts +86 -0
- package/src/services/providers/local-provider-registry.ts +277 -61
- package/src/services/providers/local-provider-service.ts +109 -44
- package/src/services/session-data.ts +18 -10
- package/src/services/session-telemetry.ts +6 -15
- package/src/services/storage/file-team-store.ts +1 -5
- package/src/services/storage/provider-settings-legacy-migration.ts +14 -51
- package/src/services/storage/provider-settings-manager.ts +17 -2
- package/src/services/storage/sqlite-team-store.ts +1 -5
- package/src/services/workspace-manifest.ts +18 -0
- package/src/session/conversation-store.ts +77 -0
- package/src/session/file-session-service.ts +1 -1
- package/src/session/index.ts +6 -27
- package/src/session/message-builder.ts +941 -0
- package/src/session/persistence-service.ts +119 -504
- package/src/session/session-manifest-store.ts +158 -0
- package/src/session/session-row.ts +199 -0
- package/src/session/session-service.ts +17 -376
- package/src/session/session-team-coordination.ts +1 -1
- package/src/session/subagent-session-manager.ts +397 -0
- package/src/session/team-persistence-store.ts +176 -0
- package/src/transports/hub.ts +1081 -0
- package/src/transports/local.ts +419 -93
- package/src/transports/remote.ts +27 -0
- package/src/transports/runtime-host-support.ts +63 -9
- package/src/types/chat-schema.ts +4 -5
- package/src/types/config.ts +17 -7
- package/src/types/events.ts +8 -6
- package/src/types/index.ts +3 -0
- package/src/types/provider-settings.ts +18 -7
- package/src/types/session.ts +7 -6
- package/src/types.ts +42 -2
- package/dist/hooks/persistent.d.ts +0 -64
- package/dist/hooks/persistent.d.ts.map +0 -1
- package/dist/runtime/rpc-runtime-ensure.d.ts +0 -65
- package/dist/runtime/rpc-runtime-ensure.d.ts.map +0 -1
- package/dist/runtime/rpc-spawn-lease.d.ts +0 -8
- package/dist/runtime/rpc-spawn-lease.d.ts.map +0 -1
- package/dist/services/telemetry/index.js +0 -15
- package/dist/session/rpc-session-service.d.ts +0 -16
- package/dist/session/rpc-session-service.d.ts.map +0 -1
- package/dist/session/sqlite-rpc-session-backend.d.ts +0 -31
- package/dist/session/sqlite-rpc-session-backend.d.ts.map +0 -1
- package/dist/transports/rpc.d.ts +0 -51
- package/dist/transports/rpc.d.ts.map +0 -1
- package/src/ClineCore.test.ts +0 -226
- package/src/account/cline-account-service.test.ts +0 -185
- package/src/account/featurebase-token.test.ts +0 -175
- package/src/account/rpc.test.ts +0 -63
- package/src/auth/bounded-ttl-cache.test.ts +0 -38
- package/src/auth/client.test.ts +0 -69
- package/src/auth/cline.test.ts +0 -267
- package/src/auth/codex.test.ts +0 -170
- package/src/auth/oca.test.ts +0 -340
- package/src/auth/server.test.ts +0 -287
- package/src/auth/utils.test.ts +0 -128
- package/src/extensions/config/agent-config-loader.test.ts +0 -236
- package/src/extensions/config/hooks-config-loader.test.ts +0 -20
- package/src/extensions/config/runtime-commands.test.ts +0 -115
- package/src/extensions/config/unified-config-file-watcher.test.ts +0 -196
- package/src/extensions/config/user-instruction-config-loader.test.ts +0 -246
- package/src/extensions/context/compaction.test.ts +0 -483
- package/src/extensions/mcp/config-loader.test.ts +0 -238
- package/src/extensions/mcp/manager.test.ts +0 -105
- package/src/extensions/plugin/plugin-config-loader.test.ts +0 -184
- package/src/extensions/plugin/plugin-loader.test.ts +0 -292
- package/src/extensions/plugin/plugin-sandbox.test.ts +0 -423
- package/src/extensions/tools/definitions.test.ts +0 -780
- package/src/extensions/tools/executors/bash.test.ts +0 -87
- package/src/extensions/tools/executors/editor.test.ts +0 -35
- package/src/extensions/tools/executors/file-read.test.ts +0 -125
- package/src/extensions/tools/model-tool-routing.test.ts +0 -86
- package/src/extensions/tools/presets.test.ts +0 -70
- package/src/extensions/tools/team/multi-agent.lifecycle.test.ts +0 -455
- package/src/extensions/tools/team/spawn-agent-tool.test.ts +0 -381
- package/src/extensions/tools/team/team-tools.test.ts +0 -918
- package/src/hooks/checkpoint-hooks.test.ts +0 -168
- package/src/hooks/hook-file-hooks.test.ts +0 -311
- package/src/hooks/persistent.ts +0 -661
- package/src/runtime/history.test.ts +0 -114
- package/src/runtime/host.test.ts +0 -230
- package/src/runtime/rpc-runtime-ensure.test.ts +0 -123
- package/src/runtime/rpc-runtime-ensure.ts +0 -659
- package/src/runtime/rpc-spawn-lease.test.ts +0 -81
- package/src/runtime/rpc-spawn-lease.ts +0 -156
- package/src/runtime/runtime-builder.team-persistence.test.ts +0 -245
- package/src/runtime/runtime-builder.test.ts +0 -615
- package/src/runtime/runtime-oauth-token-manager.test.ts +0 -137
- package/src/runtime/runtime-parity.test.ts +0 -143
- package/src/services/providers/local-provider-service.test.ts +0 -1062
- package/src/services/session-data.test.ts +0 -160
- package/src/services/storage/provider-settings-legacy-migration.test.ts +0 -424
- package/src/services/storage/provider-settings-manager.test.ts +0 -191
- package/src/services/telemetry/OpenTelemetryAdapter.test.ts +0 -157
- package/src/services/telemetry/OpenTelemetryProvider.test.ts +0 -326
- package/src/services/telemetry/TelemetryLoggerSink.test.ts +0 -42
- package/src/services/telemetry/TelemetryService.test.ts +0 -134
- package/src/services/telemetry/distinct-id.test.ts +0 -57
- package/src/services/workspace/file-indexer.d.ts +0 -11
- package/src/services/workspace/file-indexer.test.ts +0 -156
- package/src/services/workspace/mention-enricher.test.ts +0 -106
- package/src/session/persistence-service.test.ts +0 -300
- package/src/session/rpc-session-service.ts +0 -114
- package/src/session/session-service.team-persistence.test.ts +0 -48
- package/src/session/sqlite-rpc-session-backend.ts +0 -301
- package/src/transports/local.e2e.test.ts +0 -380
- package/src/transports/local.test.ts +0 -2559
- package/src/transports/rpc.test.ts +0 -82
- package/src/transports/rpc.ts +0 -665
|
@@ -0,0 +1,941 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API-safe message builder for provider payloads.
|
|
3
|
+
*
|
|
4
|
+
* @see PLAN.md §3.1 — moved from `packages/agents/src/context/message-builder.ts`.
|
|
5
|
+
* @see PLAN.md §3.2.3 — public surface of `MessageBuilder`.
|
|
6
|
+
*
|
|
7
|
+
* Verbatim port: walks the conversation to produce provider-ready
|
|
8
|
+
* messages, handles tool-result truncation and outdated-file-content
|
|
9
|
+
* rewrite for compaction. Per-instance caches make this host state.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type * as LlmsProviders from "@clinebot/llms";
|
|
13
|
+
import { normalizeUserInput } from "@clinebot/shared";
|
|
14
|
+
|
|
15
|
+
const DEFAULT_MAX_TOOL_RESULT_CHARS = 50_000;
|
|
16
|
+
const DEFAULT_MAX_TOTAL_TEXT_BYTES = 6_000_000;
|
|
17
|
+
const MIN_TOTAL_BUDGET_TOOL_RESULT_BYTES = 8_000;
|
|
18
|
+
const TARGET_TOOL_NAMES = new Set([
|
|
19
|
+
"read",
|
|
20
|
+
"read_files",
|
|
21
|
+
"search",
|
|
22
|
+
"search_codebase",
|
|
23
|
+
"bash",
|
|
24
|
+
"run_commands",
|
|
25
|
+
]);
|
|
26
|
+
const READ_TOOL_NAMES = new Set(["read", "read_files"]);
|
|
27
|
+
const OUTDATED_FILE_CONTENT = "[outdated - see the latest file content]";
|
|
28
|
+
|
|
29
|
+
interface ReadResultRecord {
|
|
30
|
+
toolUseId: string;
|
|
31
|
+
locators: ReadLocator[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface ReadLocator {
|
|
35
|
+
path: string;
|
|
36
|
+
startLine: number | null;
|
|
37
|
+
endLine: number | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Builds an API-safe message copy without mutating original conversation history.
|
|
42
|
+
*/
|
|
43
|
+
export class MessageBuilder {
|
|
44
|
+
private indexedMessageCount = 0;
|
|
45
|
+
private indexedTailRef: LlmsProviders.Message | undefined;
|
|
46
|
+
private readonly toolNameByIdCache = new Map<string, string>();
|
|
47
|
+
private readonly readLocatorsByToolUseIdCache = new Map<
|
|
48
|
+
string,
|
|
49
|
+
ReadLocator[]
|
|
50
|
+
>();
|
|
51
|
+
private readonly latestReadToolUseByLocatorCache = new Map<string, string>();
|
|
52
|
+
private readonly latestFullContentOwnerByPathCache = new Map<
|
|
53
|
+
string,
|
|
54
|
+
string
|
|
55
|
+
>();
|
|
56
|
+
private readResultLocatorCache = new WeakMap<object, ReadLocator[]>();
|
|
57
|
+
|
|
58
|
+
constructor(
|
|
59
|
+
private readonly maxToolResultChars = DEFAULT_MAX_TOOL_RESULT_CHARS,
|
|
60
|
+
private readonly targetToolNames = TARGET_TOOL_NAMES,
|
|
61
|
+
private readonly maxTotalTextBytes = DEFAULT_MAX_TOTAL_TEXT_BYTES,
|
|
62
|
+
) {}
|
|
63
|
+
|
|
64
|
+
buildForApi(messages: LlmsProviders.Message[]): LlmsProviders.Message[] {
|
|
65
|
+
this.reindex(messages);
|
|
66
|
+
const toolNameById = this.toolNameByIdCache;
|
|
67
|
+
const readLocatorsByToolUseId = this.readLocatorsByToolUseIdCache;
|
|
68
|
+
const latestReadToolUseByLocator = this.latestReadToolUseByLocatorCache;
|
|
69
|
+
const latestFullContentOwnerByPath = this.latestFullContentOwnerByPathCache;
|
|
70
|
+
|
|
71
|
+
const prepared = messages.map((message) => {
|
|
72
|
+
if (!Array.isArray(message.content)) {
|
|
73
|
+
if (message.role === "user" && typeof message.content === "string") {
|
|
74
|
+
const normalized = normalizeUserInput(message.content);
|
|
75
|
+
if (normalized !== message.content) {
|
|
76
|
+
return {
|
|
77
|
+
...message,
|
|
78
|
+
content: normalized,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return message;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const content = message.content.map((block) => {
|
|
86
|
+
if (
|
|
87
|
+
message.role === "user" &&
|
|
88
|
+
block.type === "text" &&
|
|
89
|
+
typeof block.text === "string"
|
|
90
|
+
) {
|
|
91
|
+
const normalized = normalizeUserInput(block.text);
|
|
92
|
+
if (normalized !== block.text) {
|
|
93
|
+
return {
|
|
94
|
+
...block,
|
|
95
|
+
text: normalized,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (block.type === "file") {
|
|
101
|
+
const truncated = this.truncateMiddle(block.content);
|
|
102
|
+
if (truncated === block.content) {
|
|
103
|
+
return block;
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
...block,
|
|
107
|
+
content: truncated,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (block.type !== "tool_result") {
|
|
112
|
+
return block;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const toolName = toolNameById.get(block.tool_use_id);
|
|
116
|
+
let nextContent = block.content;
|
|
117
|
+
|
|
118
|
+
if (this.isReadTool(toolName)) {
|
|
119
|
+
const readRecord = this.getReadResultRecord(
|
|
120
|
+
block,
|
|
121
|
+
readLocatorsByToolUseId.get(block.tool_use_id),
|
|
122
|
+
);
|
|
123
|
+
if (readRecord) {
|
|
124
|
+
const outdatedLocators = readRecord.locators.filter((locator) =>
|
|
125
|
+
this.isOutdatedReadLocator(
|
|
126
|
+
locator,
|
|
127
|
+
block.tool_use_id,
|
|
128
|
+
latestReadToolUseByLocator,
|
|
129
|
+
latestFullContentOwnerByPath,
|
|
130
|
+
),
|
|
131
|
+
);
|
|
132
|
+
if (outdatedLocators.length > 0) {
|
|
133
|
+
nextContent = this.replaceOutdatedReadContent(
|
|
134
|
+
nextContent,
|
|
135
|
+
outdatedLocators,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (this.shouldTruncateTool(toolName)) {
|
|
142
|
+
nextContent = this.truncateToolResultContent(nextContent);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (nextContent === block.content) {
|
|
146
|
+
return block;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
...block,
|
|
151
|
+
content: nextContent,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
role: message.role,
|
|
157
|
+
content,
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return this.truncateToTotalTextBudget(prepared);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private reindex(messages: LlmsProviders.Message[]): void {
|
|
165
|
+
if (messages.length < this.indexedMessageCount) {
|
|
166
|
+
this.resetIndexes();
|
|
167
|
+
}
|
|
168
|
+
if (
|
|
169
|
+
this.indexedMessageCount > 0 &&
|
|
170
|
+
messages.length >= this.indexedMessageCount &&
|
|
171
|
+
messages[this.indexedMessageCount - 1] !== this.indexedTailRef
|
|
172
|
+
) {
|
|
173
|
+
this.resetIndexes();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (let i = this.indexedMessageCount; i < messages.length; i++) {
|
|
177
|
+
const message = messages[i];
|
|
178
|
+
if (!Array.isArray(message.content)) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
for (let j = 0; j < message.content.length; j++) {
|
|
183
|
+
const block = message.content[j];
|
|
184
|
+
if (block.type === "file") {
|
|
185
|
+
this.latestFullContentOwnerByPathCache.set(
|
|
186
|
+
block.path,
|
|
187
|
+
this.toFileBlockOwnerKey(i, j),
|
|
188
|
+
);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (block.type === "tool_use") {
|
|
192
|
+
const normalizedName = block.name.toLowerCase();
|
|
193
|
+
this.toolNameByIdCache.set(block.id, normalizedName);
|
|
194
|
+
if (this.isReadTool(normalizedName)) {
|
|
195
|
+
const locators = this.extractLocatorsFromReadToolInput(block.input);
|
|
196
|
+
if (locators.length > 0) {
|
|
197
|
+
this.readLocatorsByToolUseIdCache.set(block.id, locators);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (block.type === "tool_result") {
|
|
203
|
+
const toolName = this.toolNameByIdCache.get(block.tool_use_id);
|
|
204
|
+
if (!this.isReadTool(toolName)) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const readRecord = this.getReadResultRecord(
|
|
208
|
+
block,
|
|
209
|
+
this.readLocatorsByToolUseIdCache.get(block.tool_use_id),
|
|
210
|
+
);
|
|
211
|
+
if (!readRecord) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
for (const locator of readRecord.locators) {
|
|
215
|
+
this.latestReadToolUseByLocatorCache.set(
|
|
216
|
+
this.toReadLocatorKey(locator),
|
|
217
|
+
readRecord.toolUseId,
|
|
218
|
+
);
|
|
219
|
+
if (this.isFullFileRead(locator)) {
|
|
220
|
+
this.latestFullContentOwnerByPathCache.set(
|
|
221
|
+
locator.path,
|
|
222
|
+
readRecord.toolUseId,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
this.indexedMessageCount = messages.length;
|
|
230
|
+
this.indexedTailRef =
|
|
231
|
+
messages.length > 0 ? messages[messages.length - 1] : undefined;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private resetIndexes(): void {
|
|
235
|
+
this.indexedMessageCount = 0;
|
|
236
|
+
this.indexedTailRef = undefined;
|
|
237
|
+
this.toolNameByIdCache.clear();
|
|
238
|
+
this.readLocatorsByToolUseIdCache.clear();
|
|
239
|
+
this.latestReadToolUseByLocatorCache.clear();
|
|
240
|
+
this.latestFullContentOwnerByPathCache.clear();
|
|
241
|
+
this.readResultLocatorCache = new WeakMap<object, ReadLocator[]>();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private getReadResultRecord(
|
|
245
|
+
block: LlmsProviders.ToolResultContent,
|
|
246
|
+
fallbackLocators: ReadLocator[] | undefined,
|
|
247
|
+
): ReadResultRecord | undefined {
|
|
248
|
+
const blockRef = block as unknown as object;
|
|
249
|
+
const cachedParsedLocators = this.readResultLocatorCache.get(blockRef);
|
|
250
|
+
const parsedLocators =
|
|
251
|
+
cachedParsedLocators ??
|
|
252
|
+
this.extractReadLocatorsFromToolResultContent(block.content);
|
|
253
|
+
if (!cachedParsedLocators) {
|
|
254
|
+
this.readResultLocatorCache.set(blockRef, parsedLocators);
|
|
255
|
+
}
|
|
256
|
+
const locators =
|
|
257
|
+
parsedLocators.length > 0 ? parsedLocators : (fallbackLocators ?? []);
|
|
258
|
+
if (locators.length === 0) {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
toolUseId: block.tool_use_id,
|
|
264
|
+
locators,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private extractLocatorsFromReadToolInput(input: unknown): ReadLocator[] {
|
|
269
|
+
if (!input || typeof input !== "object") {
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const record = input as Record<string, unknown>;
|
|
274
|
+
const locators: ReadLocator[] = [];
|
|
275
|
+
const directLocator = this.extractLocatorFromReadRequest(record);
|
|
276
|
+
if (directLocator) {
|
|
277
|
+
locators.push(directLocator);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const maybeFiles = record.files;
|
|
281
|
+
if (Array.isArray(maybeFiles)) {
|
|
282
|
+
for (const value of maybeFiles) {
|
|
283
|
+
const locator = this.extractLocatorFromReadRequest(value);
|
|
284
|
+
if (locator) {
|
|
285
|
+
locators.push(locator);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const maybeFilePaths = record.file_paths;
|
|
291
|
+
if (Array.isArray(maybeFilePaths)) {
|
|
292
|
+
for (const value of maybeFilePaths) {
|
|
293
|
+
if (typeof value === "string" && value.length > 0) {
|
|
294
|
+
locators.push({
|
|
295
|
+
path: value,
|
|
296
|
+
startLine: null,
|
|
297
|
+
endLine: null,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return this.dedupeReadLocators(locators);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private extractReadLocatorsFromToolResultContent(
|
|
307
|
+
content: LlmsProviders.ToolResultContent["content"],
|
|
308
|
+
): ReadLocator[] {
|
|
309
|
+
if (typeof content === "string") {
|
|
310
|
+
try {
|
|
311
|
+
const parsed = JSON.parse(content);
|
|
312
|
+
return this.extractLocatorsFromParsedReadResult(parsed);
|
|
313
|
+
} catch {
|
|
314
|
+
return [];
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
for (const entry of content) {
|
|
319
|
+
if (entry.type !== "text") {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
try {
|
|
323
|
+
const parsed = JSON.parse(entry.text);
|
|
324
|
+
return this.extractLocatorsFromParsedReadResult(parsed);
|
|
325
|
+
} catch {
|
|
326
|
+
// no-op
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return [];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private extractLocatorsFromParsedReadResult(value: unknown): ReadLocator[] {
|
|
334
|
+
if (Array.isArray(value)) {
|
|
335
|
+
return this.dedupeReadLocators(
|
|
336
|
+
value
|
|
337
|
+
.map((item) => this.extractLocatorFromResultEntry(item))
|
|
338
|
+
.filter((locator): locator is ReadLocator => locator !== undefined),
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const locator = this.extractLocatorFromResultEntry(value);
|
|
343
|
+
return locator ? [locator] : [];
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private extractLocatorFromReadRequest(
|
|
347
|
+
value: unknown,
|
|
348
|
+
): ReadLocator | undefined {
|
|
349
|
+
if (!value || typeof value !== "object") {
|
|
350
|
+
return undefined;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const record = value as Record<string, unknown>;
|
|
354
|
+
const path = this.extractPath(record);
|
|
355
|
+
if (!path) {
|
|
356
|
+
return undefined;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
path,
|
|
361
|
+
startLine: this.extractLineNumber(record.start_line),
|
|
362
|
+
endLine: this.extractLineNumber(record.end_line),
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
private extractLocatorFromResultEntry(
|
|
367
|
+
value: unknown,
|
|
368
|
+
): ReadLocator | undefined {
|
|
369
|
+
if (!value || typeof value !== "object") {
|
|
370
|
+
return undefined;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const record = value as Record<string, unknown>;
|
|
374
|
+
const directPath = this.extractPath(record);
|
|
375
|
+
if (directPath) {
|
|
376
|
+
return {
|
|
377
|
+
path: directPath,
|
|
378
|
+
startLine: this.extractLineNumber(record.start_line),
|
|
379
|
+
endLine: this.extractLineNumber(record.end_line),
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (typeof record.query === "string" && record.query.length > 0) {
|
|
384
|
+
return this.parseReadQuery(record.query);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return undefined;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
private extractPath(record: Record<string, unknown>): string | undefined {
|
|
391
|
+
const candidates = [record.path, record.file_path, record.filePath];
|
|
392
|
+
for (const candidate of candidates) {
|
|
393
|
+
if (typeof candidate === "string" && candidate.length > 0) {
|
|
394
|
+
return candidate;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return undefined;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
private extractLineNumber(value: unknown): number | null {
|
|
402
|
+
return typeof value === "number" && Number.isInteger(value) ? value : null;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
private parseReadQuery(query: string): ReadLocator {
|
|
406
|
+
const match = /^(.*):(\d+)-(EOF|\d+)$/.exec(query);
|
|
407
|
+
if (!match) {
|
|
408
|
+
return { path: query, startLine: null, endLine: null };
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return {
|
|
412
|
+
path: match[1],
|
|
413
|
+
startLine: Number(match[2]),
|
|
414
|
+
endLine: match[3] === "EOF" ? null : Number(match[3]),
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
private dedupeReadLocators(locators: ReadLocator[]): ReadLocator[] {
|
|
419
|
+
const unique = new Map<string, ReadLocator>();
|
|
420
|
+
for (const locator of locators) {
|
|
421
|
+
unique.set(this.toReadLocatorKey(locator), locator);
|
|
422
|
+
}
|
|
423
|
+
return Array.from(unique.values());
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
private toReadLocatorKey(locator: ReadLocator): string {
|
|
427
|
+
if (this.isFullFileRead(locator)) {
|
|
428
|
+
return locator.path;
|
|
429
|
+
}
|
|
430
|
+
return `${locator.path}:${locator.startLine ?? 1}-${locator.endLine ?? "EOF"}`;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private isFullFileRead(locator: ReadLocator): boolean {
|
|
434
|
+
return locator.startLine == null && locator.endLine == null;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
private isOutdatedReadLocator(
|
|
438
|
+
locator: ReadLocator,
|
|
439
|
+
toolUseId: string,
|
|
440
|
+
latestReadToolUseByLocator: Map<string, string>,
|
|
441
|
+
latestFullContentOwnerByPath: Map<string, string>,
|
|
442
|
+
): boolean {
|
|
443
|
+
const latestFullContentOwner = latestFullContentOwnerByPath.get(
|
|
444
|
+
locator.path,
|
|
445
|
+
);
|
|
446
|
+
if (latestFullContentOwner && latestFullContentOwner !== toolUseId) {
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return (
|
|
451
|
+
latestReadToolUseByLocator.get(this.toReadLocatorKey(locator)) !==
|
|
452
|
+
toolUseId
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
private toFileBlockOwnerKey(
|
|
457
|
+
messageIndex: number,
|
|
458
|
+
blockIndex: number,
|
|
459
|
+
): string {
|
|
460
|
+
return `file:${messageIndex}:${blockIndex}`;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
private replaceOutdatedReadContent(
|
|
464
|
+
content: LlmsProviders.ToolResultContent["content"],
|
|
465
|
+
outdatedLocators: ReadLocator[],
|
|
466
|
+
): LlmsProviders.ToolResultContent["content"] {
|
|
467
|
+
const outdatedLocatorKeySet = new Set(
|
|
468
|
+
outdatedLocators.map((locator) => this.toReadLocatorKey(locator)),
|
|
469
|
+
);
|
|
470
|
+
const outdatedPathSet = new Set(
|
|
471
|
+
outdatedLocators.map((locator) => locator.path),
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
if (typeof content === "string") {
|
|
475
|
+
const replaced = this.replaceOutdatedReadContentInString(
|
|
476
|
+
content,
|
|
477
|
+
outdatedLocatorKeySet,
|
|
478
|
+
);
|
|
479
|
+
return replaced ?? OUTDATED_FILE_CONTENT;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
let outdatedImageCount = content.reduce((count, entry) => {
|
|
483
|
+
if (entry.type !== "text") {
|
|
484
|
+
return count;
|
|
485
|
+
}
|
|
486
|
+
return (
|
|
487
|
+
count +
|
|
488
|
+
this.countOutdatedImageEntries(entry.text, outdatedLocatorKeySet)
|
|
489
|
+
);
|
|
490
|
+
}, 0);
|
|
491
|
+
|
|
492
|
+
return content.map((entry) => {
|
|
493
|
+
if (entry.type === "file") {
|
|
494
|
+
if (!outdatedPathSet.has(entry.path)) {
|
|
495
|
+
return entry;
|
|
496
|
+
}
|
|
497
|
+
return {
|
|
498
|
+
...(entry as LlmsProviders.FileContent),
|
|
499
|
+
content: OUTDATED_FILE_CONTENT,
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (entry.type === "image") {
|
|
504
|
+
if (outdatedImageCount === 0) {
|
|
505
|
+
return entry;
|
|
506
|
+
}
|
|
507
|
+
outdatedImageCount -= 1;
|
|
508
|
+
return {
|
|
509
|
+
type: "text",
|
|
510
|
+
text: OUTDATED_FILE_CONTENT,
|
|
511
|
+
} satisfies LlmsProviders.TextContent;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (entry.type !== "text") {
|
|
515
|
+
return entry;
|
|
516
|
+
}
|
|
517
|
+
const replaced = this.replaceOutdatedReadContentInString(
|
|
518
|
+
entry.text,
|
|
519
|
+
outdatedLocatorKeySet,
|
|
520
|
+
);
|
|
521
|
+
if (replaced === null) {
|
|
522
|
+
return {
|
|
523
|
+
...(entry as LlmsProviders.TextContent),
|
|
524
|
+
text: OUTDATED_FILE_CONTENT,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
if (replaced === entry.text) {
|
|
528
|
+
return entry;
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
...(entry as LlmsProviders.TextContent),
|
|
532
|
+
text: replaced,
|
|
533
|
+
};
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
private countOutdatedImageEntries(
|
|
538
|
+
text: string,
|
|
539
|
+
outdatedLocatorKeySet: Set<string>,
|
|
540
|
+
): number {
|
|
541
|
+
try {
|
|
542
|
+
const parsed = JSON.parse(text);
|
|
543
|
+
return this.countOutdatedImageEntriesInParsed(
|
|
544
|
+
parsed,
|
|
545
|
+
outdatedLocatorKeySet,
|
|
546
|
+
);
|
|
547
|
+
} catch {
|
|
548
|
+
return 0;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
private countOutdatedImageEntriesInParsed(
|
|
553
|
+
value: unknown,
|
|
554
|
+
outdatedLocatorKeySet: Set<string>,
|
|
555
|
+
): number {
|
|
556
|
+
if (Array.isArray(value)) {
|
|
557
|
+
return value.reduce(
|
|
558
|
+
(count, entry) =>
|
|
559
|
+
count +
|
|
560
|
+
this.countOutdatedImageEntriesInEntry(entry, outdatedLocatorKeySet),
|
|
561
|
+
0,
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return this.countOutdatedImageEntriesInEntry(value, outdatedLocatorKeySet);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
private countOutdatedImageEntriesInEntry(
|
|
569
|
+
entry: unknown,
|
|
570
|
+
outdatedLocatorKeySet: Set<string>,
|
|
571
|
+
): number {
|
|
572
|
+
if (!entry || typeof entry !== "object") {
|
|
573
|
+
return 0;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const record = entry as Record<string, unknown>;
|
|
577
|
+
const locator = this.extractLocatorFromResultEntry(record);
|
|
578
|
+
if (!locator) {
|
|
579
|
+
return 0;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const locatorKey = this.toReadLocatorKey(locator);
|
|
583
|
+
if (!outdatedLocatorKeySet.has(locatorKey)) {
|
|
584
|
+
return 0;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
return this.isImageReadSummaryEntry(record) ? 1 : 0;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
private isImageReadSummaryEntry(record: Record<string, unknown>): boolean {
|
|
591
|
+
return (
|
|
592
|
+
record.result === "Successfully read image" ||
|
|
593
|
+
record.content === "Successfully read image"
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
private replaceOutdatedReadContentInString(
|
|
598
|
+
text: string,
|
|
599
|
+
outdatedLocatorKeySet: Set<string>,
|
|
600
|
+
): string | null {
|
|
601
|
+
try {
|
|
602
|
+
const parsed = JSON.parse(text);
|
|
603
|
+
const replaced = this.replaceOutdatedReadContentInParsed(
|
|
604
|
+
parsed,
|
|
605
|
+
outdatedLocatorKeySet,
|
|
606
|
+
);
|
|
607
|
+
return JSON.stringify(replaced);
|
|
608
|
+
} catch {
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
private replaceOutdatedReadContentInParsed(
|
|
614
|
+
value: unknown,
|
|
615
|
+
outdatedLocatorKeySet: Set<string>,
|
|
616
|
+
): unknown {
|
|
617
|
+
if (Array.isArray(value)) {
|
|
618
|
+
return value.map((entry) =>
|
|
619
|
+
this.replaceOutdatedReadEntry(entry, outdatedLocatorKeySet),
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
return this.replaceOutdatedReadEntry(value, outdatedLocatorKeySet);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
private replaceOutdatedReadEntry(
|
|
627
|
+
entry: unknown,
|
|
628
|
+
outdatedLocatorKeySet: Set<string>,
|
|
629
|
+
): unknown {
|
|
630
|
+
if (!entry || typeof entry !== "object") {
|
|
631
|
+
return entry;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const record = { ...(entry as Record<string, unknown>) };
|
|
635
|
+
const locator = this.extractLocatorFromResultEntry(record);
|
|
636
|
+
if (!locator) {
|
|
637
|
+
return entry;
|
|
638
|
+
}
|
|
639
|
+
const locatorKey = this.toReadLocatorKey(locator);
|
|
640
|
+
if (!outdatedLocatorKeySet.has(locatorKey)) {
|
|
641
|
+
return entry;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
if (typeof record.result === "string") {
|
|
645
|
+
record.result = OUTDATED_FILE_CONTENT;
|
|
646
|
+
} else if (typeof record.content === "string") {
|
|
647
|
+
record.content = OUTDATED_FILE_CONTENT;
|
|
648
|
+
} else {
|
|
649
|
+
record.result = OUTDATED_FILE_CONTENT;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
return record;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
private isReadTool(toolName: string | undefined): boolean {
|
|
656
|
+
if (!toolName) {
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
return READ_TOOL_NAMES.has(toolName.toLowerCase());
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
private shouldTruncateTool(toolName: string | undefined): boolean {
|
|
663
|
+
if (!toolName) {
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
return this.targetToolNames.has(toolName.toLowerCase());
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
private truncateToolResultContent(
|
|
670
|
+
content: LlmsProviders.ToolResultContent["content"],
|
|
671
|
+
): LlmsProviders.ToolResultContent["content"] {
|
|
672
|
+
if (typeof content === "string") {
|
|
673
|
+
return this.truncateMiddle(content);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
return content.map((entry) => {
|
|
677
|
+
if (entry.type === "file") {
|
|
678
|
+
const fileContent = this.truncateMiddle(entry.content);
|
|
679
|
+
if (fileContent === entry.content) {
|
|
680
|
+
return entry;
|
|
681
|
+
}
|
|
682
|
+
return {
|
|
683
|
+
...(entry as LlmsProviders.FileContent),
|
|
684
|
+
content: fileContent,
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
if (entry.type !== "text") {
|
|
689
|
+
return entry;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
const text = this.truncateMiddle(entry.text);
|
|
693
|
+
if (text === entry.text) {
|
|
694
|
+
return entry;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
return {
|
|
698
|
+
...(entry as LlmsProviders.TextContent),
|
|
699
|
+
text,
|
|
700
|
+
};
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
private truncateMiddle(text: string): string {
|
|
705
|
+
if (text.length <= this.maxToolResultChars) {
|
|
706
|
+
return text;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const marker = `\n\n...[truncated ${Math.max(0, text.length - this.maxToolResultChars)} chars]...\n\n`;
|
|
710
|
+
const availableChars = Math.max(0, this.maxToolResultChars - marker.length);
|
|
711
|
+
const keepCharsPerSide = Math.floor(availableChars / 2);
|
|
712
|
+
const retainedChars = keepCharsPerSide * 2;
|
|
713
|
+
const removedChars = Math.max(0, text.length - retainedChars);
|
|
714
|
+
const effectiveMarker = `\n\n...[truncated ${removedChars} chars]...\n\n`;
|
|
715
|
+
const effectiveAvailableChars = Math.max(
|
|
716
|
+
0,
|
|
717
|
+
this.maxToolResultChars - effectiveMarker.length,
|
|
718
|
+
);
|
|
719
|
+
const effectiveKeepCharsPerSide = Math.floor(effectiveAvailableChars / 2);
|
|
720
|
+
|
|
721
|
+
const start = text.slice(0, effectiveKeepCharsPerSide);
|
|
722
|
+
const end =
|
|
723
|
+
effectiveKeepCharsPerSide > 0
|
|
724
|
+
? text.slice(-effectiveKeepCharsPerSide)
|
|
725
|
+
: "";
|
|
726
|
+
|
|
727
|
+
return `${start}${effectiveMarker}${end}`;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
private truncateToTotalTextBudget(
|
|
731
|
+
messages: LlmsProviders.Message[],
|
|
732
|
+
): LlmsProviders.Message[] {
|
|
733
|
+
if (this.maxTotalTextBytes <= 0) {
|
|
734
|
+
return messages;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
let totalBytes = this.countMessageTextBytes(messages);
|
|
738
|
+
if (totalBytes <= this.maxTotalTextBytes) {
|
|
739
|
+
return messages;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const next = messages.map((message) => {
|
|
743
|
+
if (!Array.isArray(message.content)) {
|
|
744
|
+
return message;
|
|
745
|
+
}
|
|
746
|
+
return {
|
|
747
|
+
...message,
|
|
748
|
+
content: message.content.map((block) =>
|
|
749
|
+
this.cloneContentBlockForMutation(block),
|
|
750
|
+
),
|
|
751
|
+
};
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
const candidates = this.collectTruncationCandidates(next);
|
|
755
|
+
for (const candidate of candidates) {
|
|
756
|
+
if (totalBytes <= this.maxTotalTextBytes) {
|
|
757
|
+
break;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
const current = candidate.get();
|
|
761
|
+
const currentBytes = candidate.byteLength;
|
|
762
|
+
if (currentBytes <= MIN_TOTAL_BUDGET_TOOL_RESULT_BYTES) {
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
const overflowBytes = totalBytes - this.maxTotalTextBytes;
|
|
767
|
+
const targetBytes = Math.max(
|
|
768
|
+
MIN_TOTAL_BUDGET_TOOL_RESULT_BYTES,
|
|
769
|
+
currentBytes - overflowBytes,
|
|
770
|
+
);
|
|
771
|
+
const truncated = this.truncateMiddleToBytes(current, targetBytes);
|
|
772
|
+
candidate.set(truncated);
|
|
773
|
+
totalBytes -= currentBytes - this.utf8ByteLength(truncated);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
return next;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
private countMessageTextBytes(messages: LlmsProviders.Message[]): number {
|
|
780
|
+
let total = 0;
|
|
781
|
+
for (const message of messages) {
|
|
782
|
+
if (typeof message.content === "string") {
|
|
783
|
+
total += this.utf8ByteLength(message.content);
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
for (const block of message.content) {
|
|
787
|
+
if (block.type === "text") {
|
|
788
|
+
total += this.utf8ByteLength(block.text);
|
|
789
|
+
} else if (block.type === "thinking") {
|
|
790
|
+
total += this.utf8ByteLength(block.thinking);
|
|
791
|
+
} else if (block.type === "file") {
|
|
792
|
+
total += this.utf8ByteLength(block.content);
|
|
793
|
+
} else if (block.type === "tool_result") {
|
|
794
|
+
total += this.countToolResultTextBytes(block.content);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
return total;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
private countToolResultTextBytes(
|
|
802
|
+
content: LlmsProviders.ToolResultContent["content"],
|
|
803
|
+
): number {
|
|
804
|
+
if (typeof content === "string") {
|
|
805
|
+
return this.utf8ByteLength(content);
|
|
806
|
+
}
|
|
807
|
+
return content.reduce((total, entry) => {
|
|
808
|
+
if (entry.type === "text") {
|
|
809
|
+
return total + this.utf8ByteLength(entry.text);
|
|
810
|
+
}
|
|
811
|
+
if (entry.type === "file") {
|
|
812
|
+
return total + this.utf8ByteLength(entry.content);
|
|
813
|
+
}
|
|
814
|
+
return total;
|
|
815
|
+
}, 0);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
private collectTruncationCandidates(
|
|
819
|
+
messages: LlmsProviders.Message[],
|
|
820
|
+
): Array<{
|
|
821
|
+
byteLength: number;
|
|
822
|
+
get: () => string;
|
|
823
|
+
set: (value: string) => void;
|
|
824
|
+
}> {
|
|
825
|
+
const candidates: Array<{
|
|
826
|
+
byteLength: number;
|
|
827
|
+
get: () => string;
|
|
828
|
+
set: (value: string) => void;
|
|
829
|
+
}> = [];
|
|
830
|
+
|
|
831
|
+
for (const message of messages) {
|
|
832
|
+
if (!Array.isArray(message.content)) {
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
for (const block of message.content) {
|
|
836
|
+
if (block.type !== "tool_result") {
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
const toolName = this.toolNameByIdCache.get(block.tool_use_id);
|
|
840
|
+
if (!this.shouldTruncateTool(toolName)) {
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
if (typeof block.content === "string") {
|
|
844
|
+
candidates.push({
|
|
845
|
+
byteLength: this.utf8ByteLength(block.content),
|
|
846
|
+
get: () => block.content as string,
|
|
847
|
+
set: (value) => {
|
|
848
|
+
block.content = value;
|
|
849
|
+
},
|
|
850
|
+
});
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
for (const entry of block.content) {
|
|
854
|
+
if (entry.type === "text") {
|
|
855
|
+
candidates.push({
|
|
856
|
+
byteLength: this.utf8ByteLength(entry.text),
|
|
857
|
+
get: () => entry.text,
|
|
858
|
+
set: (value) => {
|
|
859
|
+
entry.text = value;
|
|
860
|
+
},
|
|
861
|
+
});
|
|
862
|
+
} else if (entry.type === "file") {
|
|
863
|
+
candidates.push({
|
|
864
|
+
byteLength: this.utf8ByteLength(entry.content),
|
|
865
|
+
get: () => entry.content,
|
|
866
|
+
set: (value) => {
|
|
867
|
+
entry.content = value;
|
|
868
|
+
},
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
return candidates.sort((left, right) => right.byteLength - left.byteLength);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
private truncateMiddleToLength(text: string, maxChars: number): string {
|
|
879
|
+
if (text.length <= maxChars) {
|
|
880
|
+
return text;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
const marker = `\n\n...[truncated ${Math.max(0, text.length - maxChars)} chars to fit provider request budget]...\n\n`;
|
|
884
|
+
const availableChars = Math.max(0, maxChars - marker.length);
|
|
885
|
+
const keepCharsPerSide = Math.floor(availableChars / 2);
|
|
886
|
+
const retainedChars = keepCharsPerSide * 2;
|
|
887
|
+
const removedChars = Math.max(0, text.length - retainedChars);
|
|
888
|
+
const effectiveMarker = `\n\n...[truncated ${removedChars} chars to fit provider request budget]...\n\n`;
|
|
889
|
+
const effectiveAvailableChars = Math.max(
|
|
890
|
+
0,
|
|
891
|
+
maxChars - effectiveMarker.length,
|
|
892
|
+
);
|
|
893
|
+
const effectiveKeepCharsPerSide = Math.floor(effectiveAvailableChars / 2);
|
|
894
|
+
|
|
895
|
+
const start = text.slice(0, effectiveKeepCharsPerSide);
|
|
896
|
+
const end =
|
|
897
|
+
effectiveKeepCharsPerSide > 0
|
|
898
|
+
? text.slice(-effectiveKeepCharsPerSide)
|
|
899
|
+
: "";
|
|
900
|
+
|
|
901
|
+
return `${start}${effectiveMarker}${end}`;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
private truncateMiddleToBytes(text: string, maxBytes: number): string {
|
|
905
|
+
if (this.utf8ByteLength(text) <= maxBytes) {
|
|
906
|
+
return text;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
let low = 0;
|
|
910
|
+
let high = text.length;
|
|
911
|
+
let best = this.truncateMiddleToLength(text, 0);
|
|
912
|
+
while (low <= high) {
|
|
913
|
+
const mid = Math.floor((low + high) / 2);
|
|
914
|
+
const candidate = this.truncateMiddleToLength(text, mid);
|
|
915
|
+
if (this.utf8ByteLength(candidate) <= maxBytes) {
|
|
916
|
+
best = candidate;
|
|
917
|
+
low = mid + 1;
|
|
918
|
+
} else {
|
|
919
|
+
high = mid - 1;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return best;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
private utf8ByteLength(text: string): number {
|
|
926
|
+
return Buffer.byteLength(text, "utf8");
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
private cloneContentBlockForMutation(
|
|
930
|
+
block: LlmsProviders.ContentBlock,
|
|
931
|
+
): LlmsProviders.ContentBlock {
|
|
932
|
+
if (block.type !== "tool_result" || typeof block.content === "string") {
|
|
933
|
+
return { ...block };
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return {
|
|
937
|
+
...block,
|
|
938
|
+
content: block.content.map((entry) => ({ ...entry })),
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
}
|