@defai.digital/ax-cli 4.4.7 → 4.4.11
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/bin/ax-cli +1 -5
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +158 -9
- package/dist/index.js.map +1 -1
- package/dist/setup.d.ts +27 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +591 -0
- package/dist/setup.js.map +1 -0
- package/package.json +43 -135
- package/LICENSE +0 -22
- package/README.md +0 -420
- package/config-defaults/messages.yaml +0 -75
- package/config-defaults/models.yaml +0 -57
- package/config-defaults/prompts.yaml +0 -948
- package/config-defaults/settings.yaml +0 -157
- package/dist/agent/agent-executor.d.ts +0 -61
- package/dist/agent/agent-executor.js +0 -194
- package/dist/agent/agent-executor.js.map +0 -1
- package/dist/agent/agent-router.d.ts +0 -68
- package/dist/agent/agent-router.js +0 -242
- package/dist/agent/agent-router.js.map +0 -1
- package/dist/agent/context-manager.d.ts +0 -122
- package/dist/agent/context-manager.js +0 -406
- package/dist/agent/context-manager.js.map +0 -1
- package/dist/agent/core/index.d.ts +0 -8
- package/dist/agent/core/index.js +0 -9
- package/dist/agent/core/index.js.map +0 -1
- package/dist/agent/core/types.d.ts +0 -92
- package/dist/agent/core/types.js +0 -11
- package/dist/agent/core/types.js.map +0 -1
- package/dist/agent/dependency-resolver.d.ts +0 -90
- package/dist/agent/dependency-resolver.js +0 -366
- package/dist/agent/dependency-resolver.js.map +0 -1
- package/dist/agent/execution/index.d.ts +0 -9
- package/dist/agent/execution/index.js +0 -9
- package/dist/agent/execution/index.js.map +0 -1
- package/dist/agent/execution/tool-executor.d.ts +0 -93
- package/dist/agent/execution/tool-executor.js +0 -552
- package/dist/agent/execution/tool-executor.js.map +0 -1
- package/dist/agent/index.d.ts +0 -14
- package/dist/agent/index.js +0 -145
- package/dist/agent/index.js.map +0 -1
- package/dist/agent/llm-agent.d.ts +0 -368
- package/dist/agent/llm-agent.js +0 -1931
- package/dist/agent/llm-agent.js.map +0 -1
- package/dist/agent/loop-detector.d.ts +0 -72
- package/dist/agent/loop-detector.js +0 -335
- package/dist/agent/loop-detector.js.map +0 -1
- package/dist/agent/parallel-tools.d.ts +0 -69
- package/dist/agent/parallel-tools.js +0 -188
- package/dist/agent/parallel-tools.js.map +0 -1
- package/dist/agent/planning/index.d.ts +0 -9
- package/dist/agent/planning/index.js +0 -9
- package/dist/agent/planning/index.js.map +0 -1
- package/dist/agent/planning/plan-executor.d.ts +0 -79
- package/dist/agent/planning/plan-executor.js +0 -240
- package/dist/agent/planning/plan-executor.js.map +0 -1
- package/dist/agent/progress-tracker.d.ts +0 -94
- package/dist/agent/progress-tracker.js +0 -225
- package/dist/agent/progress-tracker.js.map +0 -1
- package/dist/agent/specialized/analysis-agent.d.ts +0 -11
- package/dist/agent/specialized/analysis-agent.js +0 -24
- package/dist/agent/specialized/analysis-agent.js.map +0 -1
- package/dist/agent/specialized/debug-agent.d.ts +0 -11
- package/dist/agent/specialized/debug-agent.js +0 -46
- package/dist/agent/specialized/debug-agent.js.map +0 -1
- package/dist/agent/specialized/documentation-agent.d.ts +0 -11
- package/dist/agent/specialized/documentation-agent.js +0 -24
- package/dist/agent/specialized/documentation-agent.js.map +0 -1
- package/dist/agent/specialized/index.d.ts +0 -11
- package/dist/agent/specialized/index.js +0 -12
- package/dist/agent/specialized/index.js.map +0 -1
- package/dist/agent/specialized/performance-agent.d.ts +0 -11
- package/dist/agent/specialized/performance-agent.js +0 -24
- package/dist/agent/specialized/performance-agent.js.map +0 -1
- package/dist/agent/specialized/refactoring-agent.d.ts +0 -11
- package/dist/agent/specialized/refactoring-agent.js +0 -24
- package/dist/agent/specialized/refactoring-agent.js.map +0 -1
- package/dist/agent/specialized/testing-agent.d.ts +0 -11
- package/dist/agent/specialized/testing-agent.js +0 -24
- package/dist/agent/specialized/testing-agent.js.map +0 -1
- package/dist/agent/status-reporter.d.ts +0 -114
- package/dist/agent/status-reporter.js +0 -335
- package/dist/agent/status-reporter.js.map +0 -1
- package/dist/agent/streaming/index.d.ts +0 -9
- package/dist/agent/streaming/index.js +0 -9
- package/dist/agent/streaming/index.js.map +0 -1
- package/dist/agent/streaming/stream-handler.d.ts +0 -62
- package/dist/agent/streaming/stream-handler.js +0 -217
- package/dist/agent/streaming/stream-handler.js.map +0 -1
- package/dist/agent/subagent-orchestrator.d.ts +0 -166
- package/dist/agent/subagent-orchestrator.js +0 -487
- package/dist/agent/subagent-orchestrator.js.map +0 -1
- package/dist/agent/subagent-types.d.ts +0 -261
- package/dist/agent/subagent-types.js +0 -257
- package/dist/agent/subagent-types.js.map +0 -1
- package/dist/agent/subagent.d.ts +0 -116
- package/dist/agent/subagent.js +0 -507
- package/dist/agent/subagent.js.map +0 -1
- package/dist/checkpoint/index.d.ts +0 -9
- package/dist/checkpoint/index.js +0 -11
- package/dist/checkpoint/index.js.map +0 -1
- package/dist/checkpoint/manager.d.ts +0 -101
- package/dist/checkpoint/manager.js +0 -407
- package/dist/checkpoint/manager.js.map +0 -1
- package/dist/checkpoint/storage.d.ts +0 -39
- package/dist/checkpoint/storage.js +0 -350
- package/dist/checkpoint/storage.js.map +0 -1
- package/dist/checkpoint/types.d.ts +0 -111
- package/dist/checkpoint/types.js +0 -17
- package/dist/checkpoint/types.js.map +0 -1
- package/dist/commands/cache.d.ts +0 -7
- package/dist/commands/cache.js +0 -284
- package/dist/commands/cache.js.map +0 -1
- package/dist/commands/custom-commands.d.ts +0 -77
- package/dist/commands/custom-commands.js +0 -251
- package/dist/commands/custom-commands.js.map +0 -1
- package/dist/commands/design.d.ts +0 -18
- package/dist/commands/design.js +0 -511
- package/dist/commands/design.js.map +0 -1
- package/dist/commands/doctor.d.ts +0 -6
- package/dist/commands/doctor.js +0 -773
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/frontend.d.ts +0 -9
- package/dist/commands/frontend.js +0 -645
- package/dist/commands/frontend.js.map +0 -1
- package/dist/commands/init/wizard.d.ts +0 -55
- package/dist/commands/init/wizard.js +0 -189
- package/dist/commands/init/wizard.js.map +0 -1
- package/dist/commands/init.d.ts +0 -8
- package/dist/commands/init.js +0 -195
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/mcp-migrate.d.ts +0 -9
- package/dist/commands/mcp-migrate.js +0 -175
- package/dist/commands/mcp-migrate.js.map +0 -1
- package/dist/commands/mcp.d.ts +0 -2
- package/dist/commands/mcp.js +0 -1292
- package/dist/commands/mcp.js.map +0 -1
- package/dist/commands/memory.d.ts +0 -6
- package/dist/commands/memory.js +0 -555
- package/dist/commands/memory.js.map +0 -1
- package/dist/commands/models.d.ts +0 -5
- package/dist/commands/models.js +0 -213
- package/dist/commands/models.js.map +0 -1
- package/dist/commands/plan.d.ts +0 -43
- package/dist/commands/plan.js +0 -362
- package/dist/commands/plan.js.map +0 -1
- package/dist/commands/rewind.d.ts +0 -19
- package/dist/commands/rewind.js +0 -221
- package/dist/commands/rewind.js.map +0 -1
- package/dist/commands/setup.d.ts +0 -14
- package/dist/commands/setup.js +0 -733
- package/dist/commands/setup.js.map +0 -1
- package/dist/commands/status.d.ts +0 -7
- package/dist/commands/status.js +0 -437
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/templates.d.ts +0 -5
- package/dist/commands/templates.js +0 -245
- package/dist/commands/templates.js.map +0 -1
- package/dist/commands/update.d.ts +0 -49
- package/dist/commands/update.js +0 -366
- package/dist/commands/update.js.map +0 -1
- package/dist/commands/usage.d.ts +0 -8
- package/dist/commands/usage.js +0 -264
- package/dist/commands/usage.js.map +0 -1
- package/dist/commands/vscode.d.ts +0 -7
- package/dist/commands/vscode.js +0 -419
- package/dist/commands/vscode.js.map +0 -1
- package/dist/constants.d.ts +0 -236
- package/dist/constants.js +0 -288
- package/dist/constants.js.map +0 -1
- package/dist/design/figma-alias.d.ts +0 -170
- package/dist/design/figma-alias.js +0 -577
- package/dist/design/figma-alias.js.map +0 -1
- package/dist/design/figma-audit.d.ts +0 -40
- package/dist/design/figma-audit.js +0 -383
- package/dist/design/figma-audit.js.map +0 -1
- package/dist/design/figma-client.d.ts +0 -131
- package/dist/design/figma-client.js +0 -369
- package/dist/design/figma-client.js.map +0 -1
- package/dist/design/figma-map.d.ts +0 -29
- package/dist/design/figma-map.js +0 -346
- package/dist/design/figma-map.js.map +0 -1
- package/dist/design/figma-tokens.d.ts +0 -73
- package/dist/design/figma-tokens.js +0 -448
- package/dist/design/figma-tokens.js.map +0 -1
- package/dist/design/index.d.ts +0 -13
- package/dist/design/index.js +0 -20
- package/dist/design/index.js.map +0 -1
- package/dist/design/types.d.ts +0 -98
- package/dist/design/types.js +0 -9
- package/dist/design/types.js.map +0 -1
- package/dist/hooks/hook-runner.d.ts +0 -142
- package/dist/hooks/hook-runner.js +0 -436
- package/dist/hooks/hook-runner.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -9
- package/dist/hooks/index.js +0 -10
- package/dist/hooks/index.js.map +0 -1
- package/dist/hooks/manager.d.ts +0 -84
- package/dist/hooks/manager.js +0 -348
- package/dist/hooks/manager.js.map +0 -1
- package/dist/hooks/types.d.ts +0 -134
- package/dist/hooks/types.js +0 -9
- package/dist/hooks/types.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/ipc/index.d.ts +0 -9
- package/dist/ipc/index.js +0 -10
- package/dist/ipc/index.js.map +0 -1
- package/dist/ipc/vscode-client.d.ts +0 -200
- package/dist/ipc/vscode-client.js +0 -495
- package/dist/ipc/vscode-client.js.map +0 -1
- package/dist/llm/client.d.ts +0 -205
- package/dist/llm/client.js +0 -735
- package/dist/llm/client.js.map +0 -1
- package/dist/llm/tools.d.ts +0 -102
- package/dist/llm/tools.js +0 -275
- package/dist/llm/tools.js.map +0 -1
- package/dist/llm/types.d.ts +0 -428
- package/dist/llm/types.js +0 -194
- package/dist/llm/types.js.map +0 -1
- package/dist/mcp/automatosx-auto-discovery.d.ts +0 -66
- package/dist/mcp/automatosx-auto-discovery.js +0 -169
- package/dist/mcp/automatosx-auto-discovery.js.map +0 -1
- package/dist/mcp/automatosx-loader.d.ts +0 -99
- package/dist/mcp/automatosx-loader.js +0 -250
- package/dist/mcp/automatosx-loader.js.map +0 -1
- package/dist/mcp/cancellation.d.ts +0 -182
- package/dist/mcp/cancellation.js +0 -275
- package/dist/mcp/cancellation.js.map +0 -1
- package/dist/mcp/client-v2.d.ts +0 -500
- package/dist/mcp/client-v2.js +0 -1433
- package/dist/mcp/client-v2.js.map +0 -1
- package/dist/mcp/client.d.ts +0 -170
- package/dist/mcp/client.js +0 -232
- package/dist/mcp/client.js.map +0 -1
- package/dist/mcp/config-detector.d.ts +0 -90
- package/dist/mcp/config-detector.js +0 -250
- package/dist/mcp/config-detector.js.map +0 -1
- package/dist/mcp/config-migrator.d.ts +0 -68
- package/dist/mcp/config-migrator.js +0 -291
- package/dist/mcp/config-migrator.js.map +0 -1
- package/dist/mcp/config.d.ts +0 -24
- package/dist/mcp/config.js +0 -273
- package/dist/mcp/config.js.map +0 -1
- package/dist/mcp/constants.d.ts +0 -66
- package/dist/mcp/constants.js +0 -85
- package/dist/mcp/constants.js.map +0 -1
- package/dist/mcp/content-length-transport.d.ts +0 -106
- package/dist/mcp/content-length-transport.js +0 -413
- package/dist/mcp/content-length-transport.js.map +0 -1
- package/dist/mcp/debug.d.ts +0 -211
- package/dist/mcp/debug.js +0 -404
- package/dist/mcp/debug.js.map +0 -1
- package/dist/mcp/error-formatter.d.ts +0 -40
- package/dist/mcp/error-formatter.js +0 -207
- package/dist/mcp/error-formatter.js.map +0 -1
- package/dist/mcp/error-remediation.d.ts +0 -45
- package/dist/mcp/error-remediation.js +0 -291
- package/dist/mcp/error-remediation.js.map +0 -1
- package/dist/mcp/health.d.ts +0 -120
- package/dist/mcp/health.js +0 -267
- package/dist/mcp/health.js.map +0 -1
- package/dist/mcp/index.d.ts +0 -56
- package/dist/mcp/index.js +0 -89
- package/dist/mcp/index.js.map +0 -1
- package/dist/mcp/invariants.d.ts +0 -141
- package/dist/mcp/invariants.js +0 -243
- package/dist/mcp/invariants.js.map +0 -1
- package/dist/mcp/mutex-safe.d.ts +0 -151
- package/dist/mcp/mutex-safe.js +0 -260
- package/dist/mcp/mutex-safe.js.map +0 -1
- package/dist/mcp/progress.d.ts +0 -155
- package/dist/mcp/progress.js +0 -252
- package/dist/mcp/progress.js.map +0 -1
- package/dist/mcp/prompts.d.ts +0 -68
- package/dist/mcp/prompts.js +0 -129
- package/dist/mcp/prompts.js.map +0 -1
- package/dist/mcp/provider-mcp-loader.d.ts +0 -130
- package/dist/mcp/provider-mcp-loader.js +0 -292
- package/dist/mcp/provider-mcp-loader.js.map +0 -1
- package/dist/mcp/reconnection.d.ts +0 -101
- package/dist/mcp/reconnection.js +0 -253
- package/dist/mcp/reconnection.js.map +0 -1
- package/dist/mcp/registry.d.ts +0 -75
- package/dist/mcp/registry.js +0 -276
- package/dist/mcp/registry.js.map +0 -1
- package/dist/mcp/resources.d.ts +0 -58
- package/dist/mcp/resources.js +0 -144
- package/dist/mcp/resources.js.map +0 -1
- package/dist/mcp/schema-validator.d.ts +0 -82
- package/dist/mcp/schema-validator.js +0 -161
- package/dist/mcp/schema-validator.js.map +0 -1
- package/dist/mcp/ssrf-protection.d.ts +0 -86
- package/dist/mcp/ssrf-protection.js +0 -311
- package/dist/mcp/ssrf-protection.js.map +0 -1
- package/dist/mcp/subscriptions.d.ts +0 -168
- package/dist/mcp/subscriptions.js +0 -248
- package/dist/mcp/subscriptions.js.map +0 -1
- package/dist/mcp/templates.d.ts +0 -52
- package/dist/mcp/templates.js +0 -627
- package/dist/mcp/templates.js.map +0 -1
- package/dist/mcp/transports.d.ts +0 -80
- package/dist/mcp/transports.js +0 -237
- package/dist/mcp/transports.js.map +0 -1
- package/dist/mcp/type-safety.d.ts +0 -225
- package/dist/mcp/type-safety.js +0 -237
- package/dist/mcp/type-safety.js.map +0 -1
- package/dist/mcp/validation.d.ts +0 -29
- package/dist/mcp/validation.js +0 -339
- package/dist/mcp/validation.js.map +0 -1
- package/dist/mcp/zai-detector.d.ts +0 -63
- package/dist/mcp/zai-detector.js +0 -193
- package/dist/mcp/zai-detector.js.map +0 -1
- package/dist/mcp/zai-templates.d.ts +0 -90
- package/dist/mcp/zai-templates.js +0 -157
- package/dist/mcp/zai-templates.js.map +0 -1
- package/dist/memory/context-generator.d.ts +0 -84
- package/dist/memory/context-generator.js +0 -546
- package/dist/memory/context-generator.js.map +0 -1
- package/dist/memory/context-injector.d.ts +0 -97
- package/dist/memory/context-injector.js +0 -159
- package/dist/memory/context-injector.js.map +0 -1
- package/dist/memory/context-store.d.ts +0 -103
- package/dist/memory/context-store.js +0 -264
- package/dist/memory/context-store.js.map +0 -1
- package/dist/memory/index.d.ts +0 -43
- package/dist/memory/index.js +0 -49
- package/dist/memory/index.js.map +0 -1
- package/dist/memory/provider-context-store.d.ts +0 -127
- package/dist/memory/provider-context-store.js +0 -385
- package/dist/memory/provider-context-store.js.map +0 -1
- package/dist/memory/schemas.d.ts +0 -118
- package/dist/memory/schemas.js +0 -106
- package/dist/memory/schemas.js.map +0 -1
- package/dist/memory/stats-collector.d.ts +0 -73
- package/dist/memory/stats-collector.js +0 -170
- package/dist/memory/stats-collector.js.map +0 -1
- package/dist/memory/types.d.ts +0 -177
- package/dist/memory/types.js +0 -73
- package/dist/memory/types.js.map +0 -1
- package/dist/permissions/index.d.ts +0 -6
- package/dist/permissions/index.js +0 -7
- package/dist/permissions/index.js.map +0 -1
- package/dist/permissions/permission-manager.d.ts +0 -149
- package/dist/permissions/permission-manager.js +0 -410
- package/dist/permissions/permission-manager.js.map +0 -1
- package/dist/planner/dependency-resolver.d.ts +0 -72
- package/dist/planner/dependency-resolver.js +0 -272
- package/dist/planner/dependency-resolver.js.map +0 -1
- package/dist/planner/index.d.ts +0 -12
- package/dist/planner/index.js +0 -28
- package/dist/planner/index.js.map +0 -1
- package/dist/planner/plan-generator.d.ts +0 -74
- package/dist/planner/plan-generator.js +0 -244
- package/dist/planner/plan-generator.js.map +0 -1
- package/dist/planner/plan-storage.d.ts +0 -113
- package/dist/planner/plan-storage.js +0 -398
- package/dist/planner/plan-storage.js.map +0 -1
- package/dist/planner/prompts/planning-prompt.d.ts +0 -62
- package/dist/planner/prompts/planning-prompt.js +0 -414
- package/dist/planner/prompts/planning-prompt.js.map +0 -1
- package/dist/planner/task-planner.d.ts +0 -139
- package/dist/planner/task-planner.js +0 -532
- package/dist/planner/task-planner.js.map +0 -1
- package/dist/planner/token-estimator.d.ts +0 -63
- package/dist/planner/token-estimator.js +0 -295
- package/dist/planner/token-estimator.js.map +0 -1
- package/dist/planner/types.d.ts +0 -425
- package/dist/planner/types.js +0 -213
- package/dist/planner/types.js.map +0 -1
- package/dist/provider/config.d.ts +0 -227
- package/dist/provider/config.js +0 -430
- package/dist/provider/config.js.map +0 -1
- package/dist/schemas/api-schemas.d.ts +0 -45
- package/dist/schemas/api-schemas.js +0 -129
- package/dist/schemas/api-schemas.js.map +0 -1
- package/dist/schemas/confirmation-schemas.d.ts +0 -39
- package/dist/schemas/confirmation-schemas.js +0 -48
- package/dist/schemas/confirmation-schemas.js.map +0 -1
- package/dist/schemas/index-unified.d.ts +0 -12
- package/dist/schemas/index-unified.js +0 -17
- package/dist/schemas/index-unified.js.map +0 -1
- package/dist/schemas/index.d.ts +0 -83
- package/dist/schemas/index.js +0 -139
- package/dist/schemas/index.js.map +0 -1
- package/dist/schemas/settings-schemas.d.ts +0 -186
- package/dist/schemas/settings-schemas.js +0 -324
- package/dist/schemas/settings-schemas.js.map +0 -1
- package/dist/schemas/tool-schemas.d.ts +0 -127
- package/dist/schemas/tool-schemas.js +0 -84
- package/dist/schemas/tool-schemas.js.map +0 -1
- package/dist/schemas/yaml-schemas.d.ts +0 -231
- package/dist/schemas/yaml-schemas.js +0 -199
- package/dist/schemas/yaml-schemas.js.map +0 -1
- package/dist/sdk/errors.d.ts +0 -100
- package/dist/sdk/errors.js +0 -138
- package/dist/sdk/errors.js.map +0 -1
- package/dist/sdk/index.d.ts +0 -901
- package/dist/sdk/index.js +0 -1272
- package/dist/sdk/index.js.map +0 -1
- package/dist/sdk/progress-reporter.d.ts +0 -123
- package/dist/sdk/progress-reporter.js +0 -220
- package/dist/sdk/progress-reporter.js.map +0 -1
- package/dist/sdk/testing.d.ts +0 -427
- package/dist/sdk/testing.js +0 -725
- package/dist/sdk/testing.js.map +0 -1
- package/dist/sdk/tool-registry.d.ts +0 -194
- package/dist/sdk/tool-registry.js +0 -326
- package/dist/sdk/tool-registry.js.map +0 -1
- package/dist/sdk/types.d.ts +0 -53
- package/dist/sdk/types.js +0 -8
- package/dist/sdk/types.js.map +0 -1
- package/dist/sdk/unified-logger.d.ts +0 -173
- package/dist/sdk/unified-logger.js +0 -327
- package/dist/sdk/unified-logger.js.map +0 -1
- package/dist/sdk/version.d.ts +0 -163
- package/dist/sdk/version.js +0 -205
- package/dist/sdk/version.js.map +0 -1
- package/dist/tools/ask-user.d.ts +0 -126
- package/dist/tools/ask-user.js +0 -290
- package/dist/tools/ask-user.js.map +0 -1
- package/dist/tools/ax-agent.d.ts +0 -71
- package/dist/tools/ax-agent.js +0 -283
- package/dist/tools/ax-agent.js.map +0 -1
- package/dist/tools/bash-output.d.ts +0 -25
- package/dist/tools/bash-output.js +0 -146
- package/dist/tools/bash-output.js.map +0 -1
- package/dist/tools/bash.d.ts +0 -67
- package/dist/tools/bash.js +0 -522
- package/dist/tools/bash.js.map +0 -1
- package/dist/tools/confirmation-tool.d.ts +0 -16
- package/dist/tools/confirmation-tool.js +0 -76
- package/dist/tools/confirmation-tool.js.map +0 -1
- package/dist/tools/definitions/ask-user.d.ts +0 -8
- package/dist/tools/definitions/ask-user.js +0 -168
- package/dist/tools/definitions/ask-user.js.map +0 -1
- package/dist/tools/definitions/ax-agent.d.ts +0 -8
- package/dist/tools/definitions/ax-agent.js +0 -276
- package/dist/tools/definitions/ax-agent.js.map +0 -1
- package/dist/tools/definitions/bash-output.d.ts +0 -7
- package/dist/tools/definitions/bash-output.js +0 -78
- package/dist/tools/definitions/bash-output.js.map +0 -1
- package/dist/tools/definitions/bash.d.ts +0 -8
- package/dist/tools/definitions/bash.js +0 -152
- package/dist/tools/definitions/bash.js.map +0 -1
- package/dist/tools/definitions/create-file.d.ts +0 -7
- package/dist/tools/definitions/create-file.js +0 -129
- package/dist/tools/definitions/create-file.js.map +0 -1
- package/dist/tools/definitions/design.d.ts +0 -12
- package/dist/tools/definitions/design.js +0 -368
- package/dist/tools/definitions/design.js.map +0 -1
- package/dist/tools/definitions/index.d.ts +0 -49
- package/dist/tools/definitions/index.js +0 -87
- package/dist/tools/definitions/index.js.map +0 -1
- package/dist/tools/definitions/multi-edit.d.ts +0 -7
- package/dist/tools/definitions/multi-edit.js +0 -123
- package/dist/tools/definitions/multi-edit.js.map +0 -1
- package/dist/tools/definitions/search.d.ts +0 -7
- package/dist/tools/definitions/search.js +0 -159
- package/dist/tools/definitions/search.js.map +0 -1
- package/dist/tools/definitions/str-replace-editor.d.ts +0 -7
- package/dist/tools/definitions/str-replace-editor.js +0 -145
- package/dist/tools/definitions/str-replace-editor.js.map +0 -1
- package/dist/tools/definitions/todo.d.ts +0 -8
- package/dist/tools/definitions/todo.js +0 -261
- package/dist/tools/definitions/todo.js.map +0 -1
- package/dist/tools/definitions/view-file.d.ts +0 -7
- package/dist/tools/definitions/view-file.js +0 -111
- package/dist/tools/definitions/view-file.js.map +0 -1
- package/dist/tools/design-tool.d.ts +0 -68
- package/dist/tools/design-tool.js +0 -299
- package/dist/tools/design-tool.js.map +0 -1
- package/dist/tools/format-generators.d.ts +0 -62
- package/dist/tools/format-generators.js +0 -291
- package/dist/tools/format-generators.js.map +0 -1
- package/dist/tools/index.d.ts +0 -8
- package/dist/tools/index.js +0 -11
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/priority-registry.d.ts +0 -124
- package/dist/tools/priority-registry.js +0 -401
- package/dist/tools/priority-registry.js.map +0 -1
- package/dist/tools/priority.d.ts +0 -158
- package/dist/tools/priority.js +0 -350
- package/dist/tools/priority.js.map +0 -1
- package/dist/tools/registry.d.ts +0 -146
- package/dist/tools/registry.js +0 -171
- package/dist/tools/registry.js.map +0 -1
- package/dist/tools/search.d.ts +0 -85
- package/dist/tools/search.js +0 -430
- package/dist/tools/search.js.map +0 -1
- package/dist/tools/text-editor.d.ts +0 -87
- package/dist/tools/text-editor.js +0 -1369
- package/dist/tools/text-editor.js.map +0 -1
- package/dist/tools/todo-tool.d.ts +0 -20
- package/dist/tools/todo-tool.js +0 -186
- package/dist/tools/todo-tool.js.map +0 -1
- package/dist/tools/types.d.ts +0 -175
- package/dist/tools/types.js +0 -11
- package/dist/tools/types.js.map +0 -1
- package/dist/types/index.d.ts +0 -30
- package/dist/types/index.js +0 -2
- package/dist/types/index.js.map +0 -1
- package/dist/types/project-analysis.d.ts +0 -84
- package/dist/types/project-analysis.js +0 -5
- package/dist/types/project-analysis.js.map +0 -1
- package/dist/types/template.d.ts +0 -53
- package/dist/types/template.js +0 -5
- package/dist/types/template.js.map +0 -1
- package/dist/ui/app.d.ts +0 -7
- package/dist/ui/app.js +0 -102
- package/dist/ui/app.js.map +0 -1
- package/dist/ui/components/api-key-input.d.ts +0 -7
- package/dist/ui/components/api-key-input.js +0 -92
- package/dist/ui/components/api-key-input.js.map +0 -1
- package/dist/ui/components/chat-history.d.ts +0 -12
- package/dist/ui/components/chat-history.js +0 -391
- package/dist/ui/components/chat-history.js.map +0 -1
- package/dist/ui/components/chat-input.d.ts +0 -13
- package/dist/ui/components/chat-input.js +0 -179
- package/dist/ui/components/chat-input.js.map +0 -1
- package/dist/ui/components/chat-interface.d.ts +0 -11
- package/dist/ui/components/chat-interface.js +0 -830
- package/dist/ui/components/chat-interface.js.map +0 -1
- package/dist/ui/components/collapsible-tool-result.d.ts +0 -42
- package/dist/ui/components/collapsible-tool-result.js +0 -216
- package/dist/ui/components/collapsible-tool-result.js.map +0 -1
- package/dist/ui/components/command-suggestions.d.ts +0 -29
- package/dist/ui/components/command-suggestions.js +0 -88
- package/dist/ui/components/command-suggestions.js.map +0 -1
- package/dist/ui/components/confirmation-dialog.d.ts +0 -11
- package/dist/ui/components/confirmation-dialog.js +0 -100
- package/dist/ui/components/confirmation-dialog.js.map +0 -1
- package/dist/ui/components/context-breakdown.d.ts +0 -23
- package/dist/ui/components/context-breakdown.js +0 -124
- package/dist/ui/components/context-breakdown.js.map +0 -1
- package/dist/ui/components/diff-renderer.d.ts +0 -13
- package/dist/ui/components/diff-renderer.js +0 -192
- package/dist/ui/components/diff-renderer.js.map +0 -1
- package/dist/ui/components/index.d.ts +0 -18
- package/dist/ui/components/index.js +0 -20
- package/dist/ui/components/index.js.map +0 -1
- package/dist/ui/components/keyboard-help.d.ts +0 -17
- package/dist/ui/components/keyboard-help.js +0 -122
- package/dist/ui/components/keyboard-help.js.map +0 -1
- package/dist/ui/components/keyboard-hints.d.ts +0 -35
- package/dist/ui/components/keyboard-hints.js +0 -142
- package/dist/ui/components/keyboard-hints.js.map +0 -1
- package/dist/ui/components/loading-spinner.d.ts +0 -9
- package/dist/ui/components/loading-spinner.js +0 -120
- package/dist/ui/components/loading-spinner.js.map +0 -1
- package/dist/ui/components/mcp-dashboard.d.ts +0 -15
- package/dist/ui/components/mcp-dashboard.js +0 -520
- package/dist/ui/components/mcp-dashboard.js.map +0 -1
- package/dist/ui/components/mcp-status.d.ts +0 -5
- package/dist/ui/components/mcp-status.js +0 -58
- package/dist/ui/components/mcp-status.js.map +0 -1
- package/dist/ui/components/model-selection.d.ts +0 -12
- package/dist/ui/components/model-selection.js +0 -17
- package/dist/ui/components/model-selection.js.map +0 -1
- package/dist/ui/components/phase-progress.d.ts +0 -21
- package/dist/ui/components/phase-progress.js +0 -185
- package/dist/ui/components/phase-progress.js.map +0 -1
- package/dist/ui/components/question-dialog.d.ts +0 -17
- package/dist/ui/components/question-dialog.js +0 -181
- package/dist/ui/components/question-dialog.js.map +0 -1
- package/dist/ui/components/quick-actions.d.ts +0 -12
- package/dist/ui/components/quick-actions.js +0 -171
- package/dist/ui/components/quick-actions.js.map +0 -1
- package/dist/ui/components/reasoning-display.d.ts +0 -36
- package/dist/ui/components/reasoning-display.js +0 -46
- package/dist/ui/components/reasoning-display.js.map +0 -1
- package/dist/ui/components/status-bar.d.ts +0 -47
- package/dist/ui/components/status-bar.js +0 -310
- package/dist/ui/components/status-bar.js.map +0 -1
- package/dist/ui/components/subagent-monitor.d.ts +0 -41
- package/dist/ui/components/subagent-monitor.js +0 -122
- package/dist/ui/components/subagent-monitor.js.map +0 -1
- package/dist/ui/components/toast-notification.d.ts +0 -197
- package/dist/ui/components/toast-notification.js +0 -190
- package/dist/ui/components/toast-notification.js.map +0 -1
- package/dist/ui/components/tool-group-display.d.ts +0 -19
- package/dist/ui/components/tool-group-display.js +0 -222
- package/dist/ui/components/tool-group-display.js.map +0 -1
- package/dist/ui/components/virtualized-chat-history.d.ts +0 -33
- package/dist/ui/components/virtualized-chat-history.js +0 -182
- package/dist/ui/components/virtualized-chat-history.js.map +0 -1
- package/dist/ui/components/welcome-panel.d.ts +0 -11
- package/dist/ui/components/welcome-panel.js +0 -225
- package/dist/ui/components/welcome-panel.js.map +0 -1
- package/dist/ui/hooks/use-chat-reducer.d.ts +0 -69
- package/dist/ui/hooks/use-chat-reducer.js +0 -118
- package/dist/ui/hooks/use-chat-reducer.js.map +0 -1
- package/dist/ui/hooks/use-enhanced-input.d.ts +0 -53
- package/dist/ui/hooks/use-enhanced-input.js +0 -1275
- package/dist/ui/hooks/use-enhanced-input.js.map +0 -1
- package/dist/ui/hooks/use-input-handler.d.ts +0 -79
- package/dist/ui/hooks/use-input-handler.js +0 -2251
- package/dist/ui/hooks/use-input-handler.js.map +0 -1
- package/dist/ui/hooks/use-input-history.d.ts +0 -9
- package/dist/ui/hooks/use-input-history.js +0 -168
- package/dist/ui/hooks/use-input-history.js.map +0 -1
- package/dist/ui/shared/max-sized-box.d.ts +0 -17
- package/dist/ui/shared/max-sized-box.js +0 -14
- package/dist/ui/shared/max-sized-box.js.map +0 -1
- package/dist/ui/themes/index.d.ts +0 -5
- package/dist/ui/themes/index.js +0 -5
- package/dist/ui/themes/index.js.map +0 -1
- package/dist/ui/themes/theme-registry.d.ts +0 -55
- package/dist/ui/themes/theme-registry.js +0 -202
- package/dist/ui/themes/theme-registry.js.map +0 -1
- package/dist/ui/utils/bracketed-paste-handler.d.ts +0 -97
- package/dist/ui/utils/bracketed-paste-handler.js +0 -322
- package/dist/ui/utils/bracketed-paste-handler.js.map +0 -1
- package/dist/ui/utils/change-summarizer.d.ts +0 -20
- package/dist/ui/utils/change-summarizer.js +0 -282
- package/dist/ui/utils/change-summarizer.js.map +0 -1
- package/dist/ui/utils/code-colorizer.d.ts +0 -9
- package/dist/ui/utils/code-colorizer.js +0 -13
- package/dist/ui/utils/code-colorizer.js.map +0 -1
- package/dist/ui/utils/colors.d.ts +0 -41
- package/dist/ui/utils/colors.js +0 -80
- package/dist/ui/utils/colors.js.map +0 -1
- package/dist/ui/utils/image-handler.d.ts +0 -29
- package/dist/ui/utils/image-handler.js +0 -129
- package/dist/ui/utils/image-handler.js.map +0 -1
- package/dist/ui/utils/markdown-renderer.d.ts +0 -4
- package/dist/ui/utils/markdown-renderer.js +0 -40
- package/dist/ui/utils/markdown-renderer.js.map +0 -1
- package/dist/ui/utils/semantic-action-detector.d.ts +0 -49
- package/dist/ui/utils/semantic-action-detector.js +0 -339
- package/dist/ui/utils/semantic-action-detector.js.map +0 -1
- package/dist/ui/utils/tool-grouper.d.ts +0 -94
- package/dist/ui/utils/tool-grouper.js +0 -618
- package/dist/ui/utils/tool-grouper.js.map +0 -1
- package/dist/utils/api-error.d.ts +0 -61
- package/dist/utils/api-error.js +0 -176
- package/dist/utils/api-error.js.map +0 -1
- package/dist/utils/audit-logger.d.ts +0 -206
- package/dist/utils/audit-logger.js +0 -286
- package/dist/utils/audit-logger.js.map +0 -1
- package/dist/utils/auto-accept-logger.d.ts +0 -175
- package/dist/utils/auto-accept-logger.js +0 -423
- package/dist/utils/auto-accept-logger.js.map +0 -1
- package/dist/utils/automatosx-detector.d.ts +0 -19
- package/dist/utils/automatosx-detector.js +0 -52
- package/dist/utils/automatosx-detector.js.map +0 -1
- package/dist/utils/background-task-manager.d.ts +0 -114
- package/dist/utils/background-task-manager.js +0 -470
- package/dist/utils/background-task-manager.js.map +0 -1
- package/dist/utils/cache.d.ts +0 -77
- package/dist/utils/cache.js +0 -180
- package/dist/utils/cache.js.map +0 -1
- package/dist/utils/command-security.d.ts +0 -85
- package/dist/utils/command-security.js +0 -210
- package/dist/utils/command-security.js.map +0 -1
- package/dist/utils/config-loader.d.ts +0 -190
- package/dist/utils/config-loader.js +0 -108
- package/dist/utils/config-loader.js.map +0 -1
- package/dist/utils/confirmation-service.d.ts +0 -51
- package/dist/utils/confirmation-service.js +0 -220
- package/dist/utils/confirmation-service.js.map +0 -1
- package/dist/utils/console-messenger.d.ts +0 -80
- package/dist/utils/console-messenger.js +0 -142
- package/dist/utils/console-messenger.js.map +0 -1
- package/dist/utils/custom-instructions.d.ts +0 -1
- package/dist/utils/custom-instructions.js +0 -24
- package/dist/utils/custom-instructions.js.map +0 -1
- package/dist/utils/encryption.d.ts +0 -86
- package/dist/utils/encryption.js +0 -236
- package/dist/utils/encryption.js.map +0 -1
- package/dist/utils/enhanced-error-messages.d.ts +0 -33
- package/dist/utils/enhanced-error-messages.js +0 -440
- package/dist/utils/enhanced-error-messages.js.map +0 -1
- package/dist/utils/error-handler.d.ts +0 -65
- package/dist/utils/error-handler.js +0 -148
- package/dist/utils/error-handler.js.map +0 -1
- package/dist/utils/error-translator.d.ts +0 -25
- package/dist/utils/error-translator.js +0 -203
- package/dist/utils/error-translator.js.map +0 -1
- package/dist/utils/external-editor.d.ts +0 -47
- package/dist/utils/external-editor.js +0 -179
- package/dist/utils/external-editor.js.map +0 -1
- package/dist/utils/file-cache.d.ts +0 -148
- package/dist/utils/file-cache.js +0 -413
- package/dist/utils/file-cache.js.map +0 -1
- package/dist/utils/file-lock.d.ts +0 -141
- package/dist/utils/file-lock.js +0 -554
- package/dist/utils/file-lock.js.map +0 -1
- package/dist/utils/file-mentions.d.ts +0 -68
- package/dist/utils/file-mentions.js +0 -225
- package/dist/utils/file-mentions.js.map +0 -1
- package/dist/utils/history-manager.d.ts +0 -52
- package/dist/utils/history-manager.js +0 -211
- package/dist/utils/history-manager.js.map +0 -1
- package/dist/utils/history-migration.d.ts +0 -9
- package/dist/utils/history-migration.js +0 -37
- package/dist/utils/history-migration.js.map +0 -1
- package/dist/utils/image-processor.d.ts +0 -33
- package/dist/utils/image-processor.js +0 -124
- package/dist/utils/image-processor.js.map +0 -1
- package/dist/utils/index.d.ts +0 -92
- package/dist/utils/index.js +0 -111
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/init-previewer.d.ts +0 -56
- package/dist/utils/init-previewer.js +0 -239
- package/dist/utils/init-previewer.js.map +0 -1
- package/dist/utils/init-validator.d.ts +0 -65
- package/dist/utils/init-validator.js +0 -252
- package/dist/utils/init-validator.js.map +0 -1
- package/dist/utils/input-sanitizer.d.ts +0 -210
- package/dist/utils/input-sanitizer.js +0 -362
- package/dist/utils/input-sanitizer.js.map +0 -1
- package/dist/utils/instruction-generator.d.ts +0 -21
- package/dist/utils/instruction-generator.js +0 -233
- package/dist/utils/instruction-generator.js.map +0 -1
- package/dist/utils/json-utils.d.ts +0 -72
- package/dist/utils/json-utils.js +0 -226
- package/dist/utils/json-utils.js.map +0 -1
- package/dist/utils/llm-optimized-instruction-generator.d.ts +0 -36
- package/dist/utils/llm-optimized-instruction-generator.js +0 -365
- package/dist/utils/llm-optimized-instruction-generator.js.map +0 -1
- package/dist/utils/message-optimizer.d.ts +0 -100
- package/dist/utils/message-optimizer.js +0 -297
- package/dist/utils/message-optimizer.js.map +0 -1
- package/dist/utils/onboarding-manager.d.ts +0 -45
- package/dist/utils/onboarding-manager.js +0 -131
- package/dist/utils/onboarding-manager.js.map +0 -1
- package/dist/utils/parallel-analyzer.d.ts +0 -123
- package/dist/utils/parallel-analyzer.js +0 -241
- package/dist/utils/parallel-analyzer.js.map +0 -1
- package/dist/utils/paste-utils.d.ts +0 -99
- package/dist/utils/paste-utils.js +0 -295
- package/dist/utils/paste-utils.js.map +0 -1
- package/dist/utils/path-helpers.d.ts +0 -8
- package/dist/utils/path-helpers.js +0 -35
- package/dist/utils/path-helpers.js.map +0 -1
- package/dist/utils/path-security.d.ts +0 -92
- package/dist/utils/path-security.js +0 -300
- package/dist/utils/path-security.js.map +0 -1
- package/dist/utils/path-utils.d.ts +0 -83
- package/dist/utils/path-utils.js +0 -122
- package/dist/utils/path-utils.js.map +0 -1
- package/dist/utils/path-validator.d.ts +0 -66
- package/dist/utils/path-validator.js +0 -141
- package/dist/utils/path-validator.js.map +0 -1
- package/dist/utils/performance.d.ts +0 -74
- package/dist/utils/performance.js +0 -133
- package/dist/utils/performance.js.map +0 -1
- package/dist/utils/process-pool.d.ts +0 -109
- package/dist/utils/process-pool.js +0 -332
- package/dist/utils/process-pool.js.map +0 -1
- package/dist/utils/progress-tracker.d.ts +0 -51
- package/dist/utils/progress-tracker.js +0 -152
- package/dist/utils/progress-tracker.js.map +0 -1
- package/dist/utils/project-analyzer.d.ts +0 -49
- package/dist/utils/project-analyzer.js +0 -396
- package/dist/utils/project-analyzer.js.map +0 -1
- package/dist/utils/prompt-builder.d.ts +0 -14
- package/dist/utils/prompt-builder.js +0 -100
- package/dist/utils/prompt-builder.js.map +0 -1
- package/dist/utils/provider-context.d.ts +0 -243
- package/dist/utils/provider-context.js +0 -421
- package/dist/utils/provider-context.js.map +0 -1
- package/dist/utils/provider-file-cache.d.ts +0 -91
- package/dist/utils/provider-file-cache.js +0 -165
- package/dist/utils/provider-file-cache.js.map +0 -1
- package/dist/utils/provider-settings.d.ts +0 -181
- package/dist/utils/provider-settings.js +0 -450
- package/dist/utils/provider-settings.js.map +0 -1
- package/dist/utils/rate-limiter.d.ts +0 -222
- package/dist/utils/rate-limiter.js +0 -338
- package/dist/utils/rate-limiter.js.map +0 -1
- package/dist/utils/retry-helper.d.ts +0 -81
- package/dist/utils/retry-helper.js +0 -244
- package/dist/utils/retry-helper.js.map +0 -1
- package/dist/utils/safety-rules.d.ts +0 -64
- package/dist/utils/safety-rules.js +0 -225
- package/dist/utils/safety-rules.js.map +0 -1
- package/dist/utils/settings-manager.d.ts +0 -256
- package/dist/utils/settings-manager.js +0 -967
- package/dist/utils/settings-manager.js.map +0 -1
- package/dist/utils/setup-validator.d.ts +0 -47
- package/dist/utils/setup-validator.js +0 -304
- package/dist/utils/setup-validator.js.map +0 -1
- package/dist/utils/string-utils.d.ts +0 -19
- package/dist/utils/string-utils.js +0 -28
- package/dist/utils/string-utils.js.map +0 -1
- package/dist/utils/template-manager.d.ts +0 -62
- package/dist/utils/template-manager.js +0 -366
- package/dist/utils/template-manager.js.map +0 -1
- package/dist/utils/text-utils.d.ts +0 -82
- package/dist/utils/text-utils.js +0 -203
- package/dist/utils/text-utils.js.map +0 -1
- package/dist/utils/token-counter.d.ts +0 -76
- package/dist/utils/token-counter.js +0 -231
- package/dist/utils/token-counter.js.map +0 -1
- package/dist/utils/usage-tracker.d.ts +0 -78
- package/dist/utils/usage-tracker.js +0 -126
- package/dist/utils/usage-tracker.js.map +0 -1
- package/dist/utils/version.d.ts +0 -14
- package/dist/utils/version.js +0 -70
- package/dist/utils/version.js.map +0 -1
package/dist/agent/llm-agent.js
DELETED
|
@@ -1,1931 +0,0 @@
|
|
|
1
|
-
import { LLMClient } from "../llm/client.js";
|
|
2
|
-
import { getAllTools, getMCPManager, initializeMCPServers, } from "../llm/tools.js";
|
|
3
|
-
import { loadMCPConfig } from "../mcp/config.js";
|
|
4
|
-
import { EventEmitter } from "events";
|
|
5
|
-
import { AGENT_CONFIG, CACHE_CONFIG, TIMEOUT_CONFIG } from "../constants.js";
|
|
6
|
-
import { getTokenCounter } from "../utils/token-counter.js";
|
|
7
|
-
import { loadCustomInstructions } from "../utils/custom-instructions.js";
|
|
8
|
-
import { getSettingsManager } from "../utils/settings-manager.js";
|
|
9
|
-
import { ContextManager } from "./context-manager.js";
|
|
10
|
-
import { buildSystemPrompt } from "../utils/prompt-builder.js";
|
|
11
|
-
// Note: getUsageTracker is now used by StreamHandler (Phase 2 refactoring)
|
|
12
|
-
import { extractErrorMessage } from "../utils/error-handler.js";
|
|
13
|
-
import { getCheckpointManager } from "../checkpoint/index.js";
|
|
14
|
-
import { SubagentOrchestrator } from "./subagent-orchestrator.js";
|
|
15
|
-
import { getTaskPlanner, isComplexRequest, shouldUseThinkingMode, getComplexityScore, } from "../planner/index.js";
|
|
16
|
-
// Note: TaskPhase now used by PlanExecutor (Phase 2 refactoring)
|
|
17
|
-
import { PLANNER_CONFIG } from "../constants.js";
|
|
18
|
-
import { resolveMCPReferences, extractMCPReferences } from "../mcp/resources.js";
|
|
19
|
-
import { SDKError, SDKErrorCode } from "../sdk/errors.js";
|
|
20
|
-
import { getStatusReporter } from "./status-reporter.js";
|
|
21
|
-
import { getLoopDetector, resetLoopDetector } from "./loop-detector.js";
|
|
22
|
-
// Import from extracted modules (Phase 2 refactoring)
|
|
23
|
-
import { ToolExecutor } from "./execution/index.js";
|
|
24
|
-
import { StreamHandler } from "./streaming/index.js";
|
|
25
|
-
import { PlanExecutor } from "./planning/index.js";
|
|
26
|
-
import { partitionToolCalls } from "./parallel-tools.js";
|
|
27
|
-
/** Debug flag for loop detection logging (set DEBUG_LOOP_DETECTION=1 to enable) */
|
|
28
|
-
const DEBUG_LOOP = process.env.DEBUG_LOOP_DETECTION === '1';
|
|
29
|
-
/** Log debug message for loop detection (only when DEBUG_LOOP_DETECTION=1) */
|
|
30
|
-
function debugLoop(message) {
|
|
31
|
-
if (DEBUG_LOOP) {
|
|
32
|
-
console.error(`[LOOP DETECTION] ${message}`);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
export class LLMAgent extends EventEmitter {
|
|
36
|
-
llmClient;
|
|
37
|
-
// Tool execution delegated to ToolExecutor (Phase 2 refactoring)
|
|
38
|
-
toolExecutor;
|
|
39
|
-
// Stream processing delegated to StreamHandler (Phase 2 refactoring)
|
|
40
|
-
streamHandler;
|
|
41
|
-
// Plan execution delegated to PlanExecutor (Phase 2 refactoring)
|
|
42
|
-
planExecutor;
|
|
43
|
-
chatHistory = [];
|
|
44
|
-
messages = [];
|
|
45
|
-
tokenCounter;
|
|
46
|
-
contextManager;
|
|
47
|
-
abortController = null;
|
|
48
|
-
maxToolRounds;
|
|
49
|
-
toolCallIndexMap = new Map(); // O(1) lookup for tool call entries in chat history
|
|
50
|
-
toolCallArgsCache = new Map(); // Cache parsed tool arguments
|
|
51
|
-
checkpointManager;
|
|
52
|
-
subagentOrchestrator;
|
|
53
|
-
taskPlanner;
|
|
54
|
-
currentPlan = null;
|
|
55
|
-
planningEnabled = PLANNER_CONFIG.ENABLED;
|
|
56
|
-
/** Sampling configuration for deterministic/reproducible mode */
|
|
57
|
-
samplingConfig;
|
|
58
|
-
/** Thinking/reasoning mode configuration */
|
|
59
|
-
thinkingConfig;
|
|
60
|
-
/** Track if auto-thinking was enabled for current message (for UI indicator) */
|
|
61
|
-
autoThinkingEnabled = false;
|
|
62
|
-
/** User's explicit thinking preference (undefined = auto, true/false = explicit) */
|
|
63
|
-
userThinkingPreference = undefined;
|
|
64
|
-
/** Stored reference to context overflow listener for proper cleanup */
|
|
65
|
-
contextOverflowListener;
|
|
66
|
-
/** Track if agent has been disposed */
|
|
67
|
-
disposed = false;
|
|
68
|
-
/** Tool approval system for VSCode integration */
|
|
69
|
-
requireToolApproval = false;
|
|
70
|
-
toolApprovalCallbacks = new Map();
|
|
71
|
-
/** BUG FIX: Track approval timeouts for cleanup to prevent memory leaks */
|
|
72
|
-
toolApprovalTimeouts = new Map();
|
|
73
|
-
/** BUG FIX: Track resolved state to prevent double-resolution race condition */
|
|
74
|
-
toolApprovalResolved = new Map();
|
|
75
|
-
/** BUG FIX: Track cleanup timeouts for toolApprovalResolved to clear on dispose */
|
|
76
|
-
toolApprovalCleanupTimeouts = new Set();
|
|
77
|
-
constructor(apiKey, baseURL, model, maxToolRounds) {
|
|
78
|
-
super();
|
|
79
|
-
const manager = getSettingsManager();
|
|
80
|
-
const savedModel = manager.getCurrentModel();
|
|
81
|
-
const modelToUse = model || savedModel;
|
|
82
|
-
if (!modelToUse) {
|
|
83
|
-
throw new Error('No model configured. Please run "ax-cli setup" to configure your AI provider and model.');
|
|
84
|
-
}
|
|
85
|
-
this.maxToolRounds = maxToolRounds || 400;
|
|
86
|
-
this.llmClient = new LLMClient(apiKey, modelToUse, baseURL);
|
|
87
|
-
// Initialize ToolExecutor with checkpoint callback (Phase 2 refactoring)
|
|
88
|
-
this.toolExecutor = new ToolExecutor({
|
|
89
|
-
checkpointCallback: async (files, description) => {
|
|
90
|
-
// BUG FIX: Check if agent is disposed before creating checkpoint
|
|
91
|
-
if (this.disposed)
|
|
92
|
-
return;
|
|
93
|
-
// Create immutable snapshot of chat history at callback time (structuredClone is faster)
|
|
94
|
-
const chatHistorySnapshot = structuredClone(this.chatHistory);
|
|
95
|
-
await this.checkpointManager.createCheckpoint({
|
|
96
|
-
files,
|
|
97
|
-
conversationState: chatHistorySnapshot,
|
|
98
|
-
description,
|
|
99
|
-
metadata: {
|
|
100
|
-
model: this.llmClient.getCurrentModel(),
|
|
101
|
-
triggeredBy: 'auto',
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
|
-
},
|
|
105
|
-
// Emit events when ax_agent tool starts/ends for status bar display
|
|
106
|
-
onAxAgentStart: (agentName) => {
|
|
107
|
-
this.emit('ax_agent:start', { agent: agentName });
|
|
108
|
-
},
|
|
109
|
-
onAxAgentEnd: (agentName) => {
|
|
110
|
-
this.emit('ax_agent:end', { agent: agentName });
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
// Initialize StreamHandler with callbacks (Phase 2 refactoring)
|
|
114
|
-
this.streamHandler = new StreamHandler({
|
|
115
|
-
isCancelled: () => this.isCancelled(),
|
|
116
|
-
yieldCancellation: () => this.yieldCancellation(),
|
|
117
|
-
model: modelToUse,
|
|
118
|
-
});
|
|
119
|
-
this.tokenCounter = getTokenCounter(modelToUse);
|
|
120
|
-
this.contextManager = new ContextManager({ model: modelToUse });
|
|
121
|
-
this.checkpointManager = getCheckpointManager();
|
|
122
|
-
this.subagentOrchestrator = new SubagentOrchestrator({ maxConcurrentAgents: 5 });
|
|
123
|
-
// Forward subagent events for UI tracking (orchestrator event -> agent event)
|
|
124
|
-
const subagentEventMap = [
|
|
125
|
-
['subagent-start', 'subagent:start'],
|
|
126
|
-
['subagent-complete', 'subagent:complete'],
|
|
127
|
-
['spawn', 'subagent:spawn'],
|
|
128
|
-
['terminate', 'subagent:terminate'],
|
|
129
|
-
];
|
|
130
|
-
for (const [from, to] of subagentEventMap) {
|
|
131
|
-
this.subagentOrchestrator.on(from, (data) => this.emit(to, data));
|
|
132
|
-
}
|
|
133
|
-
this.taskPlanner = getTaskPlanner();
|
|
134
|
-
// Initialize PlanExecutor with callbacks (Phase 2 refactoring)
|
|
135
|
-
this.planExecutor = new PlanExecutor({
|
|
136
|
-
llmClient: this.llmClient,
|
|
137
|
-
tokenCounter: this.tokenCounter,
|
|
138
|
-
toolExecutor: this.toolExecutor,
|
|
139
|
-
getTools: () => getAllTools(),
|
|
140
|
-
executeTool: (toolCall) => this.executeTool(toolCall),
|
|
141
|
-
parseToolArgumentsCached: (toolCall) => this.parseToolArgumentsCached(toolCall),
|
|
142
|
-
buildChatOptions: (options) => this.buildChatOptions(options),
|
|
143
|
-
applyContextPruning: () => this.applyContextPruning(),
|
|
144
|
-
emitter: this,
|
|
145
|
-
maxToolRounds: Math.min(this.maxToolRounds, 50),
|
|
146
|
-
setPlanningEnabled: (enabled) => { this.planningEnabled = enabled; },
|
|
147
|
-
});
|
|
148
|
-
// Load sampling configuration from settings (supports env vars, project, and user settings)
|
|
149
|
-
this.samplingConfig = manager.getSamplingSettings();
|
|
150
|
-
// Initialize checkpoint manager
|
|
151
|
-
this.initializeCheckpointManager();
|
|
152
|
-
// Initialize MCP servers if configured (fire-and-forget - runs in background)
|
|
153
|
-
void this.initializeMCP();
|
|
154
|
-
// Build system prompt from YAML configuration
|
|
155
|
-
const customInstructions = loadCustomInstructions();
|
|
156
|
-
const systemPrompt = buildSystemPrompt({
|
|
157
|
-
customInstructions: customInstructions || undefined,
|
|
158
|
-
});
|
|
159
|
-
// Initialize with system message
|
|
160
|
-
// GLM 4.6 OPTIMIZATION: Merge static prompt with dynamic context in SINGLE message
|
|
161
|
-
// Z.AI caches by PREFIX matching - keeping static content first maximizes cache hits
|
|
162
|
-
// Dynamic content at END doesn't break cache prefix for the static portion
|
|
163
|
-
// See: https://docs.z.ai/guides/capabilities/cache
|
|
164
|
-
const dynamicContext = [
|
|
165
|
-
'',
|
|
166
|
-
'---',
|
|
167
|
-
'[Session Context]',
|
|
168
|
-
`Working Directory: ${process.cwd()}`,
|
|
169
|
-
`Session Start: ${new Date().toISOString().split('T')[0]}`,
|
|
170
|
-
].join('\n');
|
|
171
|
-
this.messages.push({
|
|
172
|
-
role: "system",
|
|
173
|
-
content: systemPrompt + dynamicContext,
|
|
174
|
-
});
|
|
175
|
-
// NEW: Listen for context pruning to generate summaries
|
|
176
|
-
// CRITICAL FIX: Wrap async callback to prevent uncaught promise rejections
|
|
177
|
-
// Event listeners don't handle async errors automatically, so we must catch them
|
|
178
|
-
// Store listener reference for proper cleanup in dispose()
|
|
179
|
-
this.contextOverflowListener = (data) => {
|
|
180
|
-
// Skip if agent is disposed to prevent operations on disposed resources
|
|
181
|
-
if (this.disposed)
|
|
182
|
-
return;
|
|
183
|
-
this.handleContextOverflow(data).catch((error) => {
|
|
184
|
-
// BUG FIX: Check disposed again in catch handler since async operation may complete after disposal
|
|
185
|
-
if (this.disposed)
|
|
186
|
-
return;
|
|
187
|
-
const errorMsg = extractErrorMessage(error);
|
|
188
|
-
console.error('Error handling context overflow:', errorMsg);
|
|
189
|
-
// Emit error event for monitoring - safe since we checked disposed above
|
|
190
|
-
this.emit('error', error);
|
|
191
|
-
});
|
|
192
|
-
};
|
|
193
|
-
this.contextManager.on('before_prune', this.contextOverflowListener);
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Run an async task in background with proper error handling
|
|
197
|
-
* Centralizes the common pattern of background initialization
|
|
198
|
-
*/
|
|
199
|
-
runBackgroundTask(taskName, task, options) {
|
|
200
|
-
Promise.resolve().then(async () => {
|
|
201
|
-
try {
|
|
202
|
-
await task();
|
|
203
|
-
if (options?.emitSuccess) {
|
|
204
|
-
this.emit('system', options.emitSuccess);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
catch (error) {
|
|
208
|
-
const errorMsg = extractErrorMessage(error);
|
|
209
|
-
if (options?.warnOnError !== false) {
|
|
210
|
-
console.warn(`${taskName} failed:`, errorMsg);
|
|
211
|
-
}
|
|
212
|
-
this.emit('system', `${taskName} failed: ${errorMsg}`);
|
|
213
|
-
}
|
|
214
|
-
}).catch((error) => {
|
|
215
|
-
console.error(`Unexpected error during ${taskName}:`, error);
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
initializeCheckpointManager() {
|
|
219
|
-
this.runBackgroundTask('Checkpoint initialization', async () => {
|
|
220
|
-
await this.checkpointManager.initialize();
|
|
221
|
-
}, { emitSuccess: 'Checkpoint system initialized' });
|
|
222
|
-
}
|
|
223
|
-
async initializeMCP() {
|
|
224
|
-
const config = loadMCPConfig();
|
|
225
|
-
if (config.servers.length === 0)
|
|
226
|
-
return; // Skip if no servers configured
|
|
227
|
-
this.runBackgroundTask('MCP initialization', async () => {
|
|
228
|
-
await initializeMCPServers();
|
|
229
|
-
// After MCP servers are initialized, update system prompt to include MCP tools
|
|
230
|
-
this.updateSystemPromptWithMCPTools();
|
|
231
|
-
}, { emitSuccess: 'MCP servers initialized successfully', warnOnError: true });
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Update the system prompt to include MCP tools after they're initialized
|
|
235
|
-
* This ensures the LLM knows about available MCP capabilities (web search, etc.)
|
|
236
|
-
*/
|
|
237
|
-
updateSystemPromptWithMCPTools() {
|
|
238
|
-
const mcpManager = getMCPManager();
|
|
239
|
-
const mcpTools = mcpManager?.getTools() || [];
|
|
240
|
-
if (mcpTools.length === 0)
|
|
241
|
-
return; // No MCP tools to add
|
|
242
|
-
// Find the system message
|
|
243
|
-
const systemMessage = this.messages.find(m => m.role === 'system');
|
|
244
|
-
if (!systemMessage || typeof systemMessage.content !== 'string')
|
|
245
|
-
return;
|
|
246
|
-
// Check if MCP tools are already in the prompt (avoid duplicate updates)
|
|
247
|
-
if (systemMessage.content.includes('MCP Tools (External Capabilities)'))
|
|
248
|
-
return;
|
|
249
|
-
// Build MCP tools section
|
|
250
|
-
const mcpToolsList = mcpTools
|
|
251
|
-
.map(tool => {
|
|
252
|
-
const friendlyName = tool.name.replace(/^mcp__[^_]+__/, '');
|
|
253
|
-
const description = tool.description?.split('\n')[0] || 'External tool';
|
|
254
|
-
return `- ${friendlyName}: ${description}`;
|
|
255
|
-
})
|
|
256
|
-
.join('\n');
|
|
257
|
-
const mcpSection = [
|
|
258
|
-
'\n\nMCP Tools (External Capabilities):',
|
|
259
|
-
mcpToolsList,
|
|
260
|
-
'\nIMPORTANT: Use MCP tools for web search, fetching URLs, and external data access. You HAVE network access through these tools.',
|
|
261
|
-
].join('\n');
|
|
262
|
-
// Append MCP tools section to system prompt
|
|
263
|
-
systemMessage.content += mcpSection;
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Build chat options with sampling and thinking configuration included
|
|
267
|
-
* Merges provided options with the agent's configurations
|
|
268
|
-
*/
|
|
269
|
-
buildChatOptions(options) {
|
|
270
|
-
const result = { ...options };
|
|
271
|
-
// Include sampling configuration if set and not overridden
|
|
272
|
-
if (this.samplingConfig && !result.sampling) {
|
|
273
|
-
result.sampling = this.samplingConfig;
|
|
274
|
-
}
|
|
275
|
-
// Include thinking configuration if set and not overridden
|
|
276
|
-
if (this.thinkingConfig && !result.thinking) {
|
|
277
|
-
result.thinking = this.thinkingConfig;
|
|
278
|
-
}
|
|
279
|
-
// Auto-switch to vision model if messages contain images
|
|
280
|
-
if (!result.model && this.hasMultimodalContent()) {
|
|
281
|
-
// Use glm-4.6v (latest vision model with 128K context)
|
|
282
|
-
result.model = 'glm-4.6v';
|
|
283
|
-
}
|
|
284
|
-
return result;
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Check if current messages contain multimodal (image) content
|
|
288
|
-
* Used to auto-switch to vision model
|
|
289
|
-
*/
|
|
290
|
-
hasMultimodalContent() {
|
|
291
|
-
return this.messages.some(msg => {
|
|
292
|
-
if (typeof msg.content !== 'string' && Array.isArray(msg.content)) {
|
|
293
|
-
return msg.content.some(part => typeof part === 'object' && 'type' in part && part.type === 'image_url');
|
|
294
|
-
}
|
|
295
|
-
return false;
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Set sampling configuration for this agent session
|
|
300
|
-
* @param config Sampling configuration to apply
|
|
301
|
-
*/
|
|
302
|
-
setSamplingConfig(config) {
|
|
303
|
-
this.samplingConfig = config;
|
|
304
|
-
}
|
|
305
|
-
/**
|
|
306
|
-
* Set thinking/reasoning mode configuration for this agent session
|
|
307
|
-
* @param config Thinking configuration to apply (enabled/disabled)
|
|
308
|
-
* @param isUserExplicit Whether this is an explicit user preference (vs auto-detection)
|
|
309
|
-
*/
|
|
310
|
-
setThinkingConfig(config, isUserExplicit = true) {
|
|
311
|
-
this.thinkingConfig = config;
|
|
312
|
-
// Track user's explicit preference to respect their choice over auto-detection
|
|
313
|
-
if (isUserExplicit) {
|
|
314
|
-
this.userThinkingPreference = config?.type === 'enabled' ? true :
|
|
315
|
-
config?.type === 'disabled' ? false : undefined;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Get current thinking configuration
|
|
320
|
-
*/
|
|
321
|
-
getThinkingConfig() {
|
|
322
|
-
return this.thinkingConfig;
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Check if auto-thinking was enabled for the current message
|
|
326
|
-
* Used by UI to show indicator when thinking was auto-activated
|
|
327
|
-
*/
|
|
328
|
-
isAutoThinkingEnabled() {
|
|
329
|
-
return this.autoThinkingEnabled;
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Get current sampling configuration
|
|
333
|
-
*/
|
|
334
|
-
getSamplingConfig() {
|
|
335
|
-
return this.samplingConfig;
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* Apply auto-thinking mode detection for a user message
|
|
339
|
-
* Only activates if user hasn't explicitly set a preference
|
|
340
|
-
*
|
|
341
|
-
* @param message User's message text
|
|
342
|
-
* @returns true if thinking mode was auto-enabled, false otherwise
|
|
343
|
-
*/
|
|
344
|
-
applyAutoThinking(message) {
|
|
345
|
-
// Reset auto-thinking state
|
|
346
|
-
this.autoThinkingEnabled = false;
|
|
347
|
-
// If user has explicit preference, respect it
|
|
348
|
-
if (this.userThinkingPreference !== undefined) {
|
|
349
|
-
return false;
|
|
350
|
-
}
|
|
351
|
-
// Check if model supports thinking mode
|
|
352
|
-
const model = this.llmClient.getCurrentModel();
|
|
353
|
-
const modelLower = model.toLowerCase();
|
|
354
|
-
const supportsThinking = modelLower.includes('glm') || modelLower.includes('4.6');
|
|
355
|
-
if (!supportsThinking) {
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
// Check if message would benefit from thinking mode
|
|
359
|
-
if (shouldUseThinkingMode(message)) {
|
|
360
|
-
const complexity = getComplexityScore(message);
|
|
361
|
-
// Only auto-enable for moderately complex or higher tasks
|
|
362
|
-
if (complexity >= 25) {
|
|
363
|
-
this.thinkingConfig = { type: 'enabled' };
|
|
364
|
-
this.autoThinkingEnabled = true;
|
|
365
|
-
// Emit event for UI to show indicator
|
|
366
|
-
this.emit('auto_thinking_enabled', {
|
|
367
|
-
complexity,
|
|
368
|
-
message: message.substring(0, 100)
|
|
369
|
-
});
|
|
370
|
-
return true;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
return false;
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* Enable or disable tool approval requirement
|
|
377
|
-
* When enabled, text_editor operations will emit 'tool:approval_required' events
|
|
378
|
-
* and wait for approval before executing
|
|
379
|
-
*
|
|
380
|
-
* This is used by VSCode extension to show diff previews
|
|
381
|
-
*
|
|
382
|
-
* @param enabled - Whether to require approval for text_editor operations
|
|
383
|
-
*/
|
|
384
|
-
setRequireToolApproval(enabled) {
|
|
385
|
-
this.requireToolApproval = enabled;
|
|
386
|
-
}
|
|
387
|
-
/**
|
|
388
|
-
* Approve or reject a pending tool call
|
|
389
|
-
* Called by external integrations (e.g., VSCode extension) in response to
|
|
390
|
-
* 'tool:approval_required' events
|
|
391
|
-
*
|
|
392
|
-
* @param toolCallId - The ID of the tool call to approve/reject
|
|
393
|
-
* @param approved - true to execute the tool, false to reject it
|
|
394
|
-
*/
|
|
395
|
-
approveToolCall(toolCallId, approved) {
|
|
396
|
-
// BUG FIX: Check if already resolved to prevent race condition with timeout
|
|
397
|
-
if (this.toolApprovalResolved.get(toolCallId)) {
|
|
398
|
-
return; // Already resolved by timeout or previous call
|
|
399
|
-
}
|
|
400
|
-
const callback = this.toolApprovalCallbacks.get(toolCallId);
|
|
401
|
-
if (callback) {
|
|
402
|
-
// BUG FIX: Mark as resolved BEFORE calling callback to prevent races
|
|
403
|
-
this.toolApprovalResolved.set(toolCallId, true);
|
|
404
|
-
// BUG FIX: Clear the timeout when approval is received (prevents memory leak)
|
|
405
|
-
const timeout = this.toolApprovalTimeouts.get(toolCallId);
|
|
406
|
-
if (timeout) {
|
|
407
|
-
clearTimeout(timeout);
|
|
408
|
-
this.toolApprovalTimeouts.delete(toolCallId);
|
|
409
|
-
}
|
|
410
|
-
callback(approved);
|
|
411
|
-
this.toolApprovalCallbacks.delete(toolCallId);
|
|
412
|
-
// Clean up resolved tracking after a short delay
|
|
413
|
-
// BUG FIX: Track cleanup timeout so it can be cleared on dispose
|
|
414
|
-
const cleanupTimeout = setTimeout(() => {
|
|
415
|
-
if (this.disposed)
|
|
416
|
-
return; // Don't modify state after disposal
|
|
417
|
-
this.toolApprovalResolved.delete(toolCallId);
|
|
418
|
-
this.toolApprovalCleanupTimeouts.delete(cleanupTimeout);
|
|
419
|
-
}, 1000);
|
|
420
|
-
this.toolApprovalCleanupTimeouts.add(cleanupTimeout);
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* Wait for external approval of a tool call
|
|
425
|
-
* Emits 'tool:approval_required' event and waits for approveToolCall() to be called
|
|
426
|
-
*
|
|
427
|
-
* @param toolCall - The tool call awaiting approval
|
|
428
|
-
* @returns Promise<boolean> - true if approved, false if rejected or timeout
|
|
429
|
-
*/
|
|
430
|
-
waitForToolApproval(toolCall) {
|
|
431
|
-
// If agent is already disposed, immediately reject approval wait to avoid dangling promises
|
|
432
|
-
if (this.disposed) {
|
|
433
|
-
return Promise.resolve(false);
|
|
434
|
-
}
|
|
435
|
-
return new Promise((resolve) => {
|
|
436
|
-
// Emit event so external integrations can show diff preview
|
|
437
|
-
this.emit('tool:approval_required', toolCall);
|
|
438
|
-
// Store callback
|
|
439
|
-
this.toolApprovalCallbacks.set(toolCall.id, resolve);
|
|
440
|
-
// BUG FIX: Initialize resolved state
|
|
441
|
-
this.toolApprovalResolved.set(toolCall.id, false);
|
|
442
|
-
// BUG FIX: Track the timeout so it can be cleared on approval/disposal
|
|
443
|
-
// This prevents memory leaks from dangling timers
|
|
444
|
-
const timeoutId = setTimeout(() => {
|
|
445
|
-
// BUG FIX: Check resolved state to prevent race condition with approveToolCall
|
|
446
|
-
if (this.toolApprovalResolved.get(toolCall.id)) {
|
|
447
|
-
return; // Already resolved by approveToolCall
|
|
448
|
-
}
|
|
449
|
-
// Mark as resolved BEFORE resolving to prevent races
|
|
450
|
-
this.toolApprovalResolved.set(toolCall.id, true);
|
|
451
|
-
// Clean up both the callback and timeout tracking
|
|
452
|
-
this.toolApprovalTimeouts.delete(toolCall.id);
|
|
453
|
-
this.toolApprovalCallbacks.delete(toolCall.id);
|
|
454
|
-
resolve(false); // Auto-reject on timeout
|
|
455
|
-
// Clean up resolved tracking after a short delay
|
|
456
|
-
// BUG FIX: Track cleanup timeout so it can be cleared on dispose
|
|
457
|
-
const cleanupTimeout = setTimeout(() => {
|
|
458
|
-
if (this.disposed)
|
|
459
|
-
return; // Don't modify state after disposal
|
|
460
|
-
this.toolApprovalResolved.delete(toolCall.id);
|
|
461
|
-
this.toolApprovalCleanupTimeouts.delete(cleanupTimeout);
|
|
462
|
-
}, 1000);
|
|
463
|
-
this.toolApprovalCleanupTimeouts.add(cleanupTimeout);
|
|
464
|
-
}, TIMEOUT_CONFIG.TOOL_APPROVAL);
|
|
465
|
-
// Track the timeout for cleanup
|
|
466
|
-
this.toolApprovalTimeouts.set(toolCall.id, timeoutId);
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* Handle context overflow by generating a summary
|
|
471
|
-
* Called when context manager is about to prune messages
|
|
472
|
-
*/
|
|
473
|
-
async handleContextOverflow(data) {
|
|
474
|
-
try {
|
|
475
|
-
const reporter = getStatusReporter();
|
|
476
|
-
const summary = await reporter.generateContextSummary(data.messages, this.chatHistory, 'context_overflow', data.tokenCount);
|
|
477
|
-
// Log for debugging
|
|
478
|
-
if (process.env.DEBUG) {
|
|
479
|
-
console.log(`[Context Overflow] Summary generated: ${summary.path}`);
|
|
480
|
-
}
|
|
481
|
-
// Add a chat entry to inform user (non-blocking)
|
|
482
|
-
const summaryEntry = {
|
|
483
|
-
type: 'assistant',
|
|
484
|
-
content: `⚠️ Context window approaching limit (${data.tokenCount.toLocaleString()} tokens). Summary saved to:\n\`${summary.path}\``,
|
|
485
|
-
timestamp: new Date(),
|
|
486
|
-
};
|
|
487
|
-
this.chatHistory.push(summaryEntry);
|
|
488
|
-
// Emit event for UI/logging
|
|
489
|
-
this.emit('context:summary', summary);
|
|
490
|
-
}
|
|
491
|
-
catch (error) {
|
|
492
|
-
// Summary generation failure should not block execution
|
|
493
|
-
const errorMsg = extractErrorMessage(error);
|
|
494
|
-
console.warn('Failed to generate context summary:', errorMsg);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
/**
|
|
498
|
-
* Apply context pruning to both messages and chatHistory
|
|
499
|
-
* BUGFIX: Prevents chatHistory from growing unbounded
|
|
500
|
-
*/
|
|
501
|
-
applyContextPruning() {
|
|
502
|
-
// Prune LLM messages if needed
|
|
503
|
-
if (this.contextManager.shouldPrune(this.messages, this.tokenCounter)) {
|
|
504
|
-
this.messages = this.contextManager.pruneMessages(this.messages, this.tokenCounter);
|
|
505
|
-
}
|
|
506
|
-
// CRITICAL FIX: Always check and prune chatHistory to prevent unbounded growth
|
|
507
|
-
// This must happen UNCONDITIONALLY, even if context pruning is disabled
|
|
508
|
-
// Keep last 200 entries which is more than enough for UI display
|
|
509
|
-
const MAX_CHAT_HISTORY_ENTRIES = 200;
|
|
510
|
-
if (this.chatHistory.length > MAX_CHAT_HISTORY_ENTRIES) {
|
|
511
|
-
const entriesToRemove = this.chatHistory.length - MAX_CHAT_HISTORY_ENTRIES;
|
|
512
|
-
this.chatHistory = this.chatHistory.slice(entriesToRemove);
|
|
513
|
-
// Update tool call index map after pruning
|
|
514
|
-
// Clear and rebuild only for remaining entries
|
|
515
|
-
this.toolCallIndexMap.clear();
|
|
516
|
-
this.chatHistory.forEach((entry, index) => {
|
|
517
|
-
if (entry.type === "tool_call" && entry.toolCall?.id) {
|
|
518
|
-
this.toolCallIndexMap.set(entry.toolCall.id, index);
|
|
519
|
-
}
|
|
520
|
-
else if (entry.type === "tool_result" && entry.toolCall?.id) {
|
|
521
|
-
this.toolCallIndexMap.set(entry.toolCall.id, index);
|
|
522
|
-
}
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
// CRITICAL FIX: Add hard limit for messages array as safety backstop
|
|
526
|
-
// In case contextManager.shouldPrune() always returns false
|
|
527
|
-
if (this.messages.length > AGENT_CONFIG.MAX_MESSAGES) {
|
|
528
|
-
// Keep system message (if exists) + last N messages
|
|
529
|
-
const systemMessages = this.messages.filter(m => m.role === 'system');
|
|
530
|
-
const nonSystemMessages = this.messages.filter(m => m.role !== 'system');
|
|
531
|
-
const keepMessages = Math.min(nonSystemMessages.length, AGENT_CONFIG.MAX_MESSAGES - systemMessages.length);
|
|
532
|
-
this.messages = [
|
|
533
|
-
...systemMessages,
|
|
534
|
-
...nonSystemMessages.slice(-keepMessages)
|
|
535
|
-
];
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
/**
|
|
539
|
-
* Check if agent is running in deterministic mode
|
|
540
|
-
*/
|
|
541
|
-
isDeterministicMode() {
|
|
542
|
-
return this.samplingConfig?.doSample === false;
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* Parse tool call arguments with caching for loop detection
|
|
546
|
-
* Returns cached result if available, otherwise parses and caches
|
|
547
|
-
* Used specifically for isRepetitiveToolCall to avoid redundant parsing
|
|
548
|
-
*/
|
|
549
|
-
parseToolArgumentsCached(toolCall) {
|
|
550
|
-
const cached = this.toolCallArgsCache.get(toolCall.id);
|
|
551
|
-
if (cached) {
|
|
552
|
-
return cached;
|
|
553
|
-
}
|
|
554
|
-
try {
|
|
555
|
-
const args = JSON.parse(toolCall.function.arguments || '{}');
|
|
556
|
-
this.toolCallArgsCache.set(toolCall.id, args);
|
|
557
|
-
// CRITICAL FIX: Prevent unbounded memory growth with proper cache eviction
|
|
558
|
-
// When cache exceeds limit, reduce to 80% capacity (not just remove fixed entries)
|
|
559
|
-
if (this.toolCallArgsCache.size > CACHE_CONFIG.TOOL_ARGS_CACHE_MAX_SIZE) {
|
|
560
|
-
const targetSize = Math.floor(CACHE_CONFIG.TOOL_ARGS_CACHE_MAX_SIZE * 0.8);
|
|
561
|
-
const toRemove = this.toolCallArgsCache.size - targetSize;
|
|
562
|
-
// BUG FIX: Don't modify Map while iterating - create array of keys first
|
|
563
|
-
const keysToDelete = Array.from(this.toolCallArgsCache.keys()).slice(0, toRemove);
|
|
564
|
-
for (const key of keysToDelete) {
|
|
565
|
-
this.toolCallArgsCache.delete(key);
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
return args;
|
|
569
|
-
}
|
|
570
|
-
catch {
|
|
571
|
-
// Return empty object on parse error (don't cache failures)
|
|
572
|
-
return {};
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
/**
|
|
576
|
-
* Detect if a tool call is repetitive (likely causing a loop)
|
|
577
|
-
* Uses the intelligent LoopDetector which provides:
|
|
578
|
-
* - Tool-specific thresholds (file ops get higher limits)
|
|
579
|
-
* - Progress-based detection (tracks success/failure)
|
|
580
|
-
* - Cycle pattern detection (A→B→A→B loops)
|
|
581
|
-
*/
|
|
582
|
-
isRepetitiveToolCall(toolCall) {
|
|
583
|
-
// Check if loop detection is disabled globally
|
|
584
|
-
if (!AGENT_CONFIG.ENABLE_LOOP_DETECTION) {
|
|
585
|
-
return false;
|
|
586
|
-
}
|
|
587
|
-
// Use the new intelligent loop detector
|
|
588
|
-
const detector = getLoopDetector();
|
|
589
|
-
const result = detector.checkForLoop(toolCall);
|
|
590
|
-
// Debug logging
|
|
591
|
-
debugLoop(`Tool: ${toolCall.function.name}`);
|
|
592
|
-
debugLoop(`Count: ${result.count}, Threshold: ${result.threshold}, Is Loop: ${result.isLoop}`);
|
|
593
|
-
if (result.reason)
|
|
594
|
-
debugLoop(`Reason: ${result.reason}`);
|
|
595
|
-
if (DEBUG_LOOP)
|
|
596
|
-
debugLoop(`Stats: ${JSON.stringify(detector.getStats())}`);
|
|
597
|
-
if (result.isLoop) {
|
|
598
|
-
// Store the result for generating better error message
|
|
599
|
-
this.lastLoopResult = result;
|
|
600
|
-
debugLoop(`⚠️ LOOP DETECTED! Reason: ${result.reason}, Suggestion: ${result.suggestion}`);
|
|
601
|
-
return true;
|
|
602
|
-
}
|
|
603
|
-
// Note: We don't record here - recording happens AFTER execution
|
|
604
|
-
// in executeToolCalls() with the actual success/failure status
|
|
605
|
-
debugLoop(`✅ Allowed, count: ${result.count}/${result.threshold}`);
|
|
606
|
-
return false;
|
|
607
|
-
}
|
|
608
|
-
/** Last loop detection result for error messages */
|
|
609
|
-
lastLoopResult;
|
|
610
|
-
/**
|
|
611
|
-
* Reset the tool call tracking (called at start of new user message)
|
|
612
|
-
*/
|
|
613
|
-
resetToolCallTracking() {
|
|
614
|
-
if (DEBUG_LOOP) {
|
|
615
|
-
const stats = getLoopDetector().getStats();
|
|
616
|
-
debugLoop(`🔄 Resetting tool call tracking (had ${stats.uniqueSignatures} signatures)`);
|
|
617
|
-
}
|
|
618
|
-
// Reset the intelligent loop detector
|
|
619
|
-
resetLoopDetector();
|
|
620
|
-
// Clear the args cache to prevent memory leak
|
|
621
|
-
this.toolCallArgsCache.clear();
|
|
622
|
-
// Clear last loop result
|
|
623
|
-
this.lastLoopResult = undefined;
|
|
624
|
-
}
|
|
625
|
-
/**
|
|
626
|
-
* Generate a helpful warning message when a loop is detected
|
|
627
|
-
* Uses the lastLoopResult for context-aware suggestions
|
|
628
|
-
*
|
|
629
|
-
* Note: Messages are designed to be professional and actionable,
|
|
630
|
-
* avoiding alarming language like "infinite loop" which can confuse users.
|
|
631
|
-
*/
|
|
632
|
-
getLoopWarningMessage() {
|
|
633
|
-
const base = "\n\nI noticed I'm repeating similar operations without making progress.";
|
|
634
|
-
if (this.lastLoopResult) {
|
|
635
|
-
const parts = [base];
|
|
636
|
-
if (this.lastLoopResult.suggestion) {
|
|
637
|
-
parts.push(` ${this.lastLoopResult.suggestion}`);
|
|
638
|
-
}
|
|
639
|
-
parts.push("\n\nLet me try a different approach or provide what I've accomplished so far.");
|
|
640
|
-
return parts.join('');
|
|
641
|
-
}
|
|
642
|
-
return base + " Let me step back and try a different approach.";
|
|
643
|
-
}
|
|
644
|
-
// ============================================================================
|
|
645
|
-
// Multi-Phase Planning Integration
|
|
646
|
-
// ============================================================================
|
|
647
|
-
/**
|
|
648
|
-
* Check if a request should trigger multi-phase planning
|
|
649
|
-
*/
|
|
650
|
-
shouldCreatePlan(message) {
|
|
651
|
-
if (!this.planningEnabled)
|
|
652
|
-
return false;
|
|
653
|
-
return isComplexRequest(message);
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* Get the current plan if any
|
|
657
|
-
*/
|
|
658
|
-
getCurrentPlan() {
|
|
659
|
-
return this.currentPlan;
|
|
660
|
-
}
|
|
661
|
-
/**
|
|
662
|
-
* Generate and execute a plan for a complex request
|
|
663
|
-
* Uses TodoWrite for Claude Code-style seamless progress display
|
|
664
|
-
*/
|
|
665
|
-
async *processWithPlanning(message) {
|
|
666
|
-
// Add user message to history
|
|
667
|
-
const userEntry = {
|
|
668
|
-
type: "user",
|
|
669
|
-
content: message,
|
|
670
|
-
timestamp: new Date(),
|
|
671
|
-
};
|
|
672
|
-
this.chatHistory.push(userEntry);
|
|
673
|
-
this.messages.push({ role: "user", content: message });
|
|
674
|
-
// Silent mode: no explicit banner, just start working
|
|
675
|
-
if (!PLANNER_CONFIG.SILENT_MODE) {
|
|
676
|
-
yield {
|
|
677
|
-
type: "content",
|
|
678
|
-
content: "📋 **Analyzing request and creating execution plan...**\n\n",
|
|
679
|
-
};
|
|
680
|
-
}
|
|
681
|
-
try {
|
|
682
|
-
// Generate plan using LLM
|
|
683
|
-
const plan = await this.taskPlanner.generatePlan(message, async (systemPrompt, userPrompt) => {
|
|
684
|
-
const planMessages = [
|
|
685
|
-
{ role: "system", content: systemPrompt },
|
|
686
|
-
{ role: "user", content: userPrompt },
|
|
687
|
-
];
|
|
688
|
-
const response = await this.llmClient.chat(planMessages, [], this.buildChatOptions());
|
|
689
|
-
return response.choices[0]?.message?.content || "";
|
|
690
|
-
}, {
|
|
691
|
-
projectType: "typescript", // Could be detected
|
|
692
|
-
});
|
|
693
|
-
if (!plan) {
|
|
694
|
-
yield {
|
|
695
|
-
type: "content",
|
|
696
|
-
content: "Could not generate a plan. Processing as single request...\n\n",
|
|
697
|
-
};
|
|
698
|
-
// Fall back to normal processing - disable planning and retry
|
|
699
|
-
this.planningEnabled = false;
|
|
700
|
-
yield* this.processUserMessageStreamInternal(message);
|
|
701
|
-
this.planningEnabled = true;
|
|
702
|
-
return;
|
|
703
|
-
}
|
|
704
|
-
this.currentPlan = plan;
|
|
705
|
-
// Emit plan created event
|
|
706
|
-
this.emit("plan:created", { plan });
|
|
707
|
-
// Create TodoWrite items for phases (Claude Code-style progress)
|
|
708
|
-
if (PLANNER_CONFIG.SILENT_MODE) {
|
|
709
|
-
// Use TodoWrite to show phases as natural todo items
|
|
710
|
-
const todoItems = plan.phases.map((phase, index) => ({
|
|
711
|
-
id: `phase-${index}`,
|
|
712
|
-
content: phase.name,
|
|
713
|
-
status: index === 0 ? "in_progress" : "pending",
|
|
714
|
-
priority: phase.riskLevel === "high" ? "high" :
|
|
715
|
-
phase.riskLevel === "low" ? "low" : "medium",
|
|
716
|
-
}));
|
|
717
|
-
try {
|
|
718
|
-
await this.toolExecutor.getTodoTool().createTodoList(todoItems);
|
|
719
|
-
}
|
|
720
|
-
catch (todoError) {
|
|
721
|
-
// TodoWrite failure is non-critical, continue execution
|
|
722
|
-
console.warn("TodoWrite create failed:", extractErrorMessage(todoError));
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
else {
|
|
726
|
-
// Display explicit plan summary
|
|
727
|
-
yield {
|
|
728
|
-
type: "content",
|
|
729
|
-
content: this.planExecutor.formatPlanSummary(plan),
|
|
730
|
-
};
|
|
731
|
-
}
|
|
732
|
-
// Execute phases one by one with progress updates
|
|
733
|
-
const phaseResults = [];
|
|
734
|
-
let totalTokensUsed = 0;
|
|
735
|
-
const planStartTime = Date.now();
|
|
736
|
-
for (let i = 0; i < plan.phases.length; i++) {
|
|
737
|
-
const phase = plan.phases[i];
|
|
738
|
-
plan.currentPhaseIndex = i;
|
|
739
|
-
if (PLANNER_CONFIG.SILENT_MODE) {
|
|
740
|
-
// Update TodoWrite: mark current phase as in_progress
|
|
741
|
-
try {
|
|
742
|
-
await this.toolExecutor.getTodoTool().updateTodoList([{
|
|
743
|
-
id: `phase-${i}`,
|
|
744
|
-
status: "in_progress",
|
|
745
|
-
}]);
|
|
746
|
-
}
|
|
747
|
-
catch { /* TodoWrite update is non-critical */ }
|
|
748
|
-
}
|
|
749
|
-
else {
|
|
750
|
-
// Show explicit phase starting banner
|
|
751
|
-
yield {
|
|
752
|
-
type: "content",
|
|
753
|
-
content: `\n**⏳ Phase ${i + 1}/${plan.phases.length}: ${phase.name}**\n`,
|
|
754
|
-
};
|
|
755
|
-
}
|
|
756
|
-
// Execute the phase (delegated to PlanExecutor - Phase 2 refactoring)
|
|
757
|
-
const context = {
|
|
758
|
-
planId: plan.id,
|
|
759
|
-
originalRequest: message,
|
|
760
|
-
completedPhases: phaseResults.filter(r => r.success).map(r => r.phaseId),
|
|
761
|
-
};
|
|
762
|
-
const { result, messages: updatedMessages } = await this.planExecutor.executePhase(phase, context, this.messages, this.chatHistory);
|
|
763
|
-
this.messages = updatedMessages; // Update messages with phase execution results
|
|
764
|
-
phaseResults.push(result);
|
|
765
|
-
totalTokensUsed += result.tokensUsed;
|
|
766
|
-
// Report phase result
|
|
767
|
-
if (result.success) {
|
|
768
|
-
if (PLANNER_CONFIG.SILENT_MODE) {
|
|
769
|
-
// Update TodoWrite: mark phase as completed
|
|
770
|
-
try {
|
|
771
|
-
await this.toolExecutor.getTodoTool().updateTodoList([{
|
|
772
|
-
id: `phase-${i}`,
|
|
773
|
-
status: "completed",
|
|
774
|
-
}]);
|
|
775
|
-
}
|
|
776
|
-
catch { /* TodoWrite update is non-critical */ }
|
|
777
|
-
}
|
|
778
|
-
else {
|
|
779
|
-
yield {
|
|
780
|
-
type: "content",
|
|
781
|
-
content: `✓ Phase ${i + 1} completed (${Math.ceil(result.duration / 1000)}s)\n`,
|
|
782
|
-
};
|
|
783
|
-
if (result.filesModified.length > 0) {
|
|
784
|
-
yield {
|
|
785
|
-
type: "content",
|
|
786
|
-
content: ` Files modified: ${result.filesModified.join(", ")}\n`,
|
|
787
|
-
};
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
else {
|
|
792
|
-
if (PLANNER_CONFIG.SILENT_MODE) {
|
|
793
|
-
// Update TodoWrite: mark phase as failed (update content to show failure)
|
|
794
|
-
try {
|
|
795
|
-
await this.toolExecutor.getTodoTool().updateTodoList([{
|
|
796
|
-
id: `phase-${i}`,
|
|
797
|
-
status: "completed", // Mark as done even if failed
|
|
798
|
-
content: `${phase.name} (failed)`,
|
|
799
|
-
}]);
|
|
800
|
-
}
|
|
801
|
-
catch { /* TodoWrite update is non-critical */ }
|
|
802
|
-
}
|
|
803
|
-
else {
|
|
804
|
-
yield {
|
|
805
|
-
type: "content",
|
|
806
|
-
content: `✕ Phase ${i + 1} failed: ${result.error}\n`,
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
// Continue with next phase unless abort strategy
|
|
810
|
-
if (phase.fallbackStrategy === "abort") {
|
|
811
|
-
if (!PLANNER_CONFIG.SILENT_MODE) {
|
|
812
|
-
yield {
|
|
813
|
-
type: "content",
|
|
814
|
-
content: `\n⚠️ Plan aborted due to phase failure.\n`,
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
break;
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
const totalDuration = Date.now() - planStartTime;
|
|
822
|
-
// Build final result
|
|
823
|
-
const successfulPhases = phaseResults.filter(r => r.success);
|
|
824
|
-
const failedPhases = phaseResults.filter(r => !r.success);
|
|
825
|
-
const allFilesModified = [...new Set(phaseResults.flatMap(r => r.filesModified))];
|
|
826
|
-
const summary = successfulPhases.length === phaseResults.length
|
|
827
|
-
? `All ${phaseResults.length} phases completed successfully. ${allFilesModified.length} files modified.`
|
|
828
|
-
: `${successfulPhases.length}/${phaseResults.length} phases completed. ${failedPhases.length} failed.`;
|
|
829
|
-
const warnings = [];
|
|
830
|
-
for (const result of failedPhases) {
|
|
831
|
-
warnings.push(`Phase ${result.phaseId} failed: ${result.error || "Unknown error"}`);
|
|
832
|
-
}
|
|
833
|
-
const planResult = {
|
|
834
|
-
planId: plan.id,
|
|
835
|
-
success: phaseResults.every(r => r.success),
|
|
836
|
-
phaseResults,
|
|
837
|
-
totalDuration,
|
|
838
|
-
totalTokensUsed,
|
|
839
|
-
summary,
|
|
840
|
-
warnings,
|
|
841
|
-
};
|
|
842
|
-
// Report final results (silent mode shows minimal output)
|
|
843
|
-
if (!PLANNER_CONFIG.SILENT_MODE) {
|
|
844
|
-
yield {
|
|
845
|
-
type: "content",
|
|
846
|
-
content: this.planExecutor.formatPlanResult(planResult),
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
else {
|
|
850
|
-
// Brief completion message in silent mode
|
|
851
|
-
const successCount = phaseResults.filter(r => r.success).length;
|
|
852
|
-
if (successCount === phaseResults.length) {
|
|
853
|
-
yield {
|
|
854
|
-
type: "content",
|
|
855
|
-
content: `\n✓ All ${phaseResults.length} tasks completed successfully.\n`,
|
|
856
|
-
};
|
|
857
|
-
}
|
|
858
|
-
else {
|
|
859
|
-
yield {
|
|
860
|
-
type: "content",
|
|
861
|
-
content: `\n⚠️ ${successCount}/${phaseResults.length} tasks completed. Check todo list for details.\n`,
|
|
862
|
-
};
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
// Emit plan completed event
|
|
866
|
-
this.emit("plan:completed", { plan, result: planResult });
|
|
867
|
-
// Generate status report on plan completion
|
|
868
|
-
try {
|
|
869
|
-
const reporter = getStatusReporter();
|
|
870
|
-
const tokenCount = this.tokenCounter.countMessageTokens(this.messages);
|
|
871
|
-
const statusReport = await reporter.generateStatusReport({
|
|
872
|
-
messages: this.messages,
|
|
873
|
-
chatHistory: this.chatHistory,
|
|
874
|
-
tokenCount,
|
|
875
|
-
plan,
|
|
876
|
-
});
|
|
877
|
-
// Notify user of status report
|
|
878
|
-
yield {
|
|
879
|
-
type: "content",
|
|
880
|
-
content: `\n📊 Status report saved to: \`${statusReport.path}\`\n`,
|
|
881
|
-
};
|
|
882
|
-
// Emit event for UI/logging
|
|
883
|
-
this.emit("plan:report", statusReport);
|
|
884
|
-
}
|
|
885
|
-
catch (error) {
|
|
886
|
-
// Status report generation failure should not block execution
|
|
887
|
-
const errorMsg = extractErrorMessage(error);
|
|
888
|
-
console.warn("Failed to generate status report:", errorMsg);
|
|
889
|
-
}
|
|
890
|
-
this.currentPlan = null;
|
|
891
|
-
}
|
|
892
|
-
catch (error) {
|
|
893
|
-
// Defensive error extraction to prevent nested failures
|
|
894
|
-
let errorMsg;
|
|
895
|
-
try {
|
|
896
|
-
errorMsg = extractErrorMessage(error);
|
|
897
|
-
}
|
|
898
|
-
catch {
|
|
899
|
-
errorMsg = String(error) || "Unknown error";
|
|
900
|
-
}
|
|
901
|
-
yield {
|
|
902
|
-
type: "content",
|
|
903
|
-
content: `\n⚠️ Plan execution error: ${errorMsg}\n`,
|
|
904
|
-
};
|
|
905
|
-
this.emit("plan:failed", { error: errorMsg });
|
|
906
|
-
this.currentPlan = null;
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
/**
|
|
910
|
-
* Internal streaming processor (used when planning falls back)
|
|
911
|
-
* Executes the core message processing loop without planning
|
|
912
|
-
*/
|
|
913
|
-
async *processUserMessageStreamInternal(message) {
|
|
914
|
-
// Reset tool call tracking for new message
|
|
915
|
-
this.resetToolCallTracking();
|
|
916
|
-
// Prepare user message and get input tokens
|
|
917
|
-
const inputTokensRef = { value: this.prepareUserMessageForStreaming(message) };
|
|
918
|
-
yield {
|
|
919
|
-
type: "token_count",
|
|
920
|
-
tokenCount: inputTokensRef.value,
|
|
921
|
-
};
|
|
922
|
-
// Yield context warnings if needed
|
|
923
|
-
yield* this.yieldContextWarnings();
|
|
924
|
-
const maxToolRounds = this.maxToolRounds;
|
|
925
|
-
let toolRounds = 0;
|
|
926
|
-
const totalOutputTokensRef = { value: 0 };
|
|
927
|
-
const lastTokenUpdateRef = { value: 0 };
|
|
928
|
-
try {
|
|
929
|
-
// Agent loop - continue until no more tool calls or max rounds reached
|
|
930
|
-
while (toolRounds < maxToolRounds) {
|
|
931
|
-
// Check if operation was cancelled
|
|
932
|
-
if (this.isCancelled()) {
|
|
933
|
-
yield* this.yieldCancellation();
|
|
934
|
-
return;
|
|
935
|
-
}
|
|
936
|
-
// Load tools safely
|
|
937
|
-
const tools = await this.loadToolsSafely();
|
|
938
|
-
// Create chat stream
|
|
939
|
-
const stream = this.llmClient.chatStream(this.messages, tools, this.buildChatOptions({
|
|
940
|
-
searchOptions: { search_parameters: { mode: "off" } }
|
|
941
|
-
}));
|
|
942
|
-
// Process streaming chunks (delegated to StreamHandler - Phase 2 refactoring)
|
|
943
|
-
const chunkGen = this.streamHandler.processChunks(stream, {
|
|
944
|
-
inputTokens: inputTokensRef.value,
|
|
945
|
-
lastTokenUpdate: lastTokenUpdateRef,
|
|
946
|
-
totalOutputTokens: totalOutputTokensRef,
|
|
947
|
-
});
|
|
948
|
-
let streamResult;
|
|
949
|
-
for await (const chunk of chunkGen) {
|
|
950
|
-
if ('accumulated' in chunk) {
|
|
951
|
-
streamResult = chunk;
|
|
952
|
-
}
|
|
953
|
-
else {
|
|
954
|
-
yield chunk;
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
if (!streamResult) {
|
|
958
|
-
continue;
|
|
959
|
-
}
|
|
960
|
-
// Add assistant message to history
|
|
961
|
-
this.addAssistantMessage(streamResult.accumulated);
|
|
962
|
-
// Handle tool calls if present
|
|
963
|
-
if (streamResult.accumulated.tool_calls && streamResult.accumulated.tool_calls.length > 0) {
|
|
964
|
-
toolRounds++;
|
|
965
|
-
// Check for repetitive tool calls (loop detection)
|
|
966
|
-
const hasRepetitiveCall = streamResult.accumulated.tool_calls.some((tc) => this.isRepetitiveToolCall(tc));
|
|
967
|
-
if (hasRepetitiveCall) {
|
|
968
|
-
const loopMsg = this.getLoopWarningMessage();
|
|
969
|
-
yield {
|
|
970
|
-
type: "content",
|
|
971
|
-
content: loopMsg,
|
|
972
|
-
};
|
|
973
|
-
break;
|
|
974
|
-
}
|
|
975
|
-
yield* this.executeToolCalls(streamResult.accumulated.tool_calls, streamResult.yielded, inputTokensRef, totalOutputTokensRef);
|
|
976
|
-
// Continue loop to get next response
|
|
977
|
-
}
|
|
978
|
-
else {
|
|
979
|
-
// No tool calls, we're done
|
|
980
|
-
break;
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
catch (error) {
|
|
985
|
-
const errorMsg = extractErrorMessage(error);
|
|
986
|
-
yield {
|
|
987
|
-
type: "content",
|
|
988
|
-
content: `\n⚠️ Error processing message: ${errorMsg}\n`,
|
|
989
|
-
};
|
|
990
|
-
}
|
|
991
|
-
// Final token count
|
|
992
|
-
yield {
|
|
993
|
-
type: "token_count",
|
|
994
|
-
tokenCount: inputTokensRef.value + totalOutputTokensRef.value,
|
|
995
|
-
};
|
|
996
|
-
yield { type: "done" };
|
|
997
|
-
}
|
|
998
|
-
async processUserMessage(message) {
|
|
999
|
-
// Check if agent has been disposed
|
|
1000
|
-
this.checkDisposed();
|
|
1001
|
-
// GLM-4.6 OPTIMIZATION: Auto-enable thinking mode for complex tasks
|
|
1002
|
-
this.applyAutoThinking(message);
|
|
1003
|
-
// Reset tool call tracking for new message
|
|
1004
|
-
this.resetToolCallTracking();
|
|
1005
|
-
// Resolve MCP resource references (Phase 4)
|
|
1006
|
-
let resolvedMessage = message;
|
|
1007
|
-
const mcpReferences = extractMCPReferences(message);
|
|
1008
|
-
if (mcpReferences.length > 0) {
|
|
1009
|
-
try {
|
|
1010
|
-
const mcpManager = getMCPManager();
|
|
1011
|
-
resolvedMessage = await resolveMCPReferences(message, mcpManager);
|
|
1012
|
-
}
|
|
1013
|
-
catch (error) {
|
|
1014
|
-
// If resolution fails, continue with original message
|
|
1015
|
-
console.warn('Failed to resolve MCP references:', error);
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
// Add user message to conversation
|
|
1019
|
-
const userEntry = {
|
|
1020
|
-
type: "user",
|
|
1021
|
-
content: resolvedMessage,
|
|
1022
|
-
timestamp: new Date(),
|
|
1023
|
-
};
|
|
1024
|
-
this.chatHistory.push(userEntry);
|
|
1025
|
-
this.messages.push({ role: "user", content: resolvedMessage });
|
|
1026
|
-
const newEntries = [userEntry];
|
|
1027
|
-
const maxToolRounds = this.maxToolRounds; // Prevent infinite loops
|
|
1028
|
-
let toolRounds = 0;
|
|
1029
|
-
try {
|
|
1030
|
-
const tools = await getAllTools();
|
|
1031
|
-
let currentResponse = await this.llmClient.chat(this.messages, tools, this.buildChatOptions({
|
|
1032
|
-
searchOptions: { search_parameters: { mode: "off" } }
|
|
1033
|
-
}));
|
|
1034
|
-
// Agent loop - continue until no more tool calls or max rounds reached
|
|
1035
|
-
while (toolRounds < maxToolRounds) {
|
|
1036
|
-
const assistantMessage = currentResponse.choices[0]?.message;
|
|
1037
|
-
if (!assistantMessage) {
|
|
1038
|
-
throw new Error("No response from AI");
|
|
1039
|
-
}
|
|
1040
|
-
// Handle tool calls
|
|
1041
|
-
if (assistantMessage.tool_calls &&
|
|
1042
|
-
assistantMessage.tool_calls.length > 0) {
|
|
1043
|
-
toolRounds++;
|
|
1044
|
-
// Check for repetitive tool calls (loop detection)
|
|
1045
|
-
debugLoop(`Checking ${assistantMessage.tool_calls.length} tool calls...`);
|
|
1046
|
-
const hasRepetitiveCall = assistantMessage.tool_calls.some((tc) => this.isRepetitiveToolCall(tc));
|
|
1047
|
-
debugLoop(`hasRepetitiveCall: ${hasRepetitiveCall}`);
|
|
1048
|
-
if (hasRepetitiveCall) {
|
|
1049
|
-
debugLoop(`🛑 Breaking loop!`);
|
|
1050
|
-
const loopMsg = this.getLoopWarningMessage();
|
|
1051
|
-
const warningEntry = {
|
|
1052
|
-
type: "assistant",
|
|
1053
|
-
content: loopMsg,
|
|
1054
|
-
timestamp: new Date(),
|
|
1055
|
-
};
|
|
1056
|
-
this.chatHistory.push(warningEntry);
|
|
1057
|
-
newEntries.push(warningEntry);
|
|
1058
|
-
break;
|
|
1059
|
-
}
|
|
1060
|
-
// Add assistant message with tool calls
|
|
1061
|
-
const assistantEntry = {
|
|
1062
|
-
type: "assistant",
|
|
1063
|
-
content: assistantMessage.content || "Using tools to help you...",
|
|
1064
|
-
timestamp: new Date(),
|
|
1065
|
-
toolCalls: assistantMessage.tool_calls,
|
|
1066
|
-
};
|
|
1067
|
-
this.chatHistory.push(assistantEntry);
|
|
1068
|
-
newEntries.push(assistantEntry);
|
|
1069
|
-
// Add assistant message to conversation
|
|
1070
|
-
this.messages.push({
|
|
1071
|
-
role: "assistant",
|
|
1072
|
-
content: assistantMessage.content || "",
|
|
1073
|
-
tool_calls: assistantMessage.tool_calls,
|
|
1074
|
-
});
|
|
1075
|
-
// Create initial tool call entries to show tools are being executed
|
|
1076
|
-
assistantMessage.tool_calls.forEach((toolCall) => {
|
|
1077
|
-
const toolCallEntry = {
|
|
1078
|
-
type: "tool_call",
|
|
1079
|
-
content: "Executing...",
|
|
1080
|
-
timestamp: new Date(),
|
|
1081
|
-
toolCall: toolCall,
|
|
1082
|
-
};
|
|
1083
|
-
const index = this.chatHistory.length;
|
|
1084
|
-
this.chatHistory.push(toolCallEntry);
|
|
1085
|
-
this.toolCallIndexMap.set(toolCall.id, index); // O(1) lookup for later updates
|
|
1086
|
-
newEntries.push(toolCallEntry);
|
|
1087
|
-
});
|
|
1088
|
-
// Execute tool calls and update the entries
|
|
1089
|
-
// Partition into parallel-safe and sequential tools
|
|
1090
|
-
const { parallel, sequential } = partitionToolCalls(assistantMessage.tool_calls);
|
|
1091
|
-
// Helper to process a tool result
|
|
1092
|
-
const processResult = (toolCall, result) => {
|
|
1093
|
-
// Update the existing tool_call entry with the result (O(1) lookup)
|
|
1094
|
-
const entryIndex = this.toolCallIndexMap.get(toolCall.id);
|
|
1095
|
-
// Validate entryIndex is still valid after potential context pruning
|
|
1096
|
-
if (entryIndex !== undefined &&
|
|
1097
|
-
entryIndex < this.chatHistory.length &&
|
|
1098
|
-
this.chatHistory[entryIndex]?.toolCall?.id === toolCall.id) {
|
|
1099
|
-
const updatedEntry = {
|
|
1100
|
-
...this.chatHistory[entryIndex],
|
|
1101
|
-
type: "tool_result",
|
|
1102
|
-
content: this.formatToolResultContent(result),
|
|
1103
|
-
toolResult: result,
|
|
1104
|
-
};
|
|
1105
|
-
this.chatHistory[entryIndex] = updatedEntry;
|
|
1106
|
-
// Also update in newEntries for return value
|
|
1107
|
-
const newEntryIndex = newEntries.findIndex((entry) => entry.type === "tool_call" &&
|
|
1108
|
-
entry.toolCall?.id === toolCall.id);
|
|
1109
|
-
if (newEntryIndex !== -1) {
|
|
1110
|
-
newEntries[newEntryIndex] = updatedEntry;
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
// Add tool result to messages with proper format (needed for AI context)
|
|
1114
|
-
this.messages.push({
|
|
1115
|
-
role: "tool",
|
|
1116
|
-
content: this.formatToolResultContent(result, "Success", "Error"),
|
|
1117
|
-
tool_call_id: toolCall.id,
|
|
1118
|
-
});
|
|
1119
|
-
};
|
|
1120
|
-
// Execute parallel-safe tools concurrently
|
|
1121
|
-
if (parallel.length > 0) {
|
|
1122
|
-
const parallelPromises = parallel.map(async (toolCall) => {
|
|
1123
|
-
const result = await this.executeTool(toolCall);
|
|
1124
|
-
return { toolCall, result };
|
|
1125
|
-
});
|
|
1126
|
-
const parallelResults = await Promise.allSettled(parallelPromises);
|
|
1127
|
-
for (const settledResult of parallelResults) {
|
|
1128
|
-
if (settledResult.status === "fulfilled") {
|
|
1129
|
-
const { toolCall, result } = settledResult.value;
|
|
1130
|
-
processResult(toolCall, result);
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
// Execute sequential tools one at a time
|
|
1135
|
-
for (const toolCall of sequential) {
|
|
1136
|
-
const result = await this.executeTool(toolCall);
|
|
1137
|
-
processResult(toolCall, result);
|
|
1138
|
-
}
|
|
1139
|
-
// Apply context pruning after adding tool results to prevent overflow
|
|
1140
|
-
// Tool results can be very large (file reads, grep output, etc.)
|
|
1141
|
-
this.applyContextPruning();
|
|
1142
|
-
// Get next response - this might contain more tool calls
|
|
1143
|
-
currentResponse = await this.llmClient.chat(this.messages, tools, this.buildChatOptions({
|
|
1144
|
-
searchOptions: { search_parameters: { mode: "off" } }
|
|
1145
|
-
}));
|
|
1146
|
-
}
|
|
1147
|
-
else {
|
|
1148
|
-
// No more tool calls, add final response
|
|
1149
|
-
const finalEntry = {
|
|
1150
|
-
type: "assistant",
|
|
1151
|
-
content: assistantMessage.content ||
|
|
1152
|
-
"I understand, but I don't have a specific response.",
|
|
1153
|
-
timestamp: new Date(),
|
|
1154
|
-
};
|
|
1155
|
-
this.chatHistory.push(finalEntry);
|
|
1156
|
-
this.messages.push({
|
|
1157
|
-
role: "assistant",
|
|
1158
|
-
content: assistantMessage.content || "",
|
|
1159
|
-
});
|
|
1160
|
-
newEntries.push(finalEntry);
|
|
1161
|
-
break; // Exit the loop
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
if (toolRounds >= maxToolRounds) {
|
|
1165
|
-
const warningEntry = {
|
|
1166
|
-
type: "assistant",
|
|
1167
|
-
content: "Maximum tool execution rounds reached. Let me provide what I've accomplished.",
|
|
1168
|
-
timestamp: new Date(),
|
|
1169
|
-
};
|
|
1170
|
-
this.chatHistory.push(warningEntry);
|
|
1171
|
-
newEntries.push(warningEntry);
|
|
1172
|
-
}
|
|
1173
|
-
return newEntries;
|
|
1174
|
-
}
|
|
1175
|
-
catch (error) {
|
|
1176
|
-
const errorMsg = extractErrorMessage(error);
|
|
1177
|
-
const errorEntry = {
|
|
1178
|
-
type: "assistant",
|
|
1179
|
-
content: `Sorry, I encountered an error: ${errorMsg}`,
|
|
1180
|
-
timestamp: new Date(),
|
|
1181
|
-
};
|
|
1182
|
-
this.chatHistory.push(errorEntry);
|
|
1183
|
-
return [userEntry, errorEntry];
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
/**
|
|
1187
|
-
* Prepare user message and apply context management
|
|
1188
|
-
* Returns the calculated input tokens
|
|
1189
|
-
*
|
|
1190
|
-
* Supports both text-only and multimodal (with images) messages.
|
|
1191
|
-
*/
|
|
1192
|
-
prepareUserMessageForStreaming(message) {
|
|
1193
|
-
// Determine display content for chat history
|
|
1194
|
-
const displayContent = typeof message === 'string'
|
|
1195
|
-
? message
|
|
1196
|
-
: this.extractDisplayContent(message);
|
|
1197
|
-
// Add user message to conversation (display format)
|
|
1198
|
-
const userEntry = {
|
|
1199
|
-
type: "user",
|
|
1200
|
-
content: displayContent,
|
|
1201
|
-
timestamp: new Date(),
|
|
1202
|
-
};
|
|
1203
|
-
this.chatHistory.push(userEntry);
|
|
1204
|
-
// Add to LLM messages (full format including images)
|
|
1205
|
-
this.messages.push({ role: "user", content: message });
|
|
1206
|
-
// Apply context management before sending to API
|
|
1207
|
-
this.applyContextPruning();
|
|
1208
|
-
// Calculate input tokens
|
|
1209
|
-
return this.tokenCounter.countMessageTokens(this.messages);
|
|
1210
|
-
}
|
|
1211
|
-
/**
|
|
1212
|
-
* Extract display text from multimodal message content
|
|
1213
|
-
* Used for chat history display (excludes binary image data)
|
|
1214
|
-
*/
|
|
1215
|
-
extractDisplayContent(content) {
|
|
1216
|
-
const parts = [];
|
|
1217
|
-
for (const part of content) {
|
|
1218
|
-
if (part.type === 'text') {
|
|
1219
|
-
parts.push(part.text);
|
|
1220
|
-
}
|
|
1221
|
-
else if (part.type === 'image_url') {
|
|
1222
|
-
parts.push('[Image attached]');
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
return parts.join('\n');
|
|
1226
|
-
}
|
|
1227
|
-
/**
|
|
1228
|
-
* Extract text content from message for analysis purposes
|
|
1229
|
-
* Used by planning and complexity detection
|
|
1230
|
-
*/
|
|
1231
|
-
getTextContentFromMessage(message) {
|
|
1232
|
-
if (typeof message === 'string') {
|
|
1233
|
-
return message;
|
|
1234
|
-
}
|
|
1235
|
-
// Extract only text parts from multimodal content
|
|
1236
|
-
return message
|
|
1237
|
-
.filter((part) => part.type === 'text')
|
|
1238
|
-
.map(part => part.text)
|
|
1239
|
-
.join('\n');
|
|
1240
|
-
}
|
|
1241
|
-
/**
|
|
1242
|
-
* Yield context warnings if needed
|
|
1243
|
-
*/
|
|
1244
|
-
async *yieldContextWarnings() {
|
|
1245
|
-
const stats = this.contextManager.getStats(this.messages, this.tokenCounter);
|
|
1246
|
-
if (stats.shouldPrune || stats.isNearLimit) {
|
|
1247
|
-
const warning = this.contextManager.createWarningMessage(stats);
|
|
1248
|
-
yield {
|
|
1249
|
-
type: "content",
|
|
1250
|
-
content: `\n${warning}\n\n`,
|
|
1251
|
-
};
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
/**
|
|
1255
|
-
* Check if operation was cancelled
|
|
1256
|
-
*/
|
|
1257
|
-
isCancelled() {
|
|
1258
|
-
return this.abortController?.signal.aborted ?? false;
|
|
1259
|
-
}
|
|
1260
|
-
/**
|
|
1261
|
-
* Yield cancellation message
|
|
1262
|
-
*/
|
|
1263
|
-
async *yieldCancellation() {
|
|
1264
|
-
yield {
|
|
1265
|
-
type: "content",
|
|
1266
|
-
content: "\n\n[Operation cancelled by user]",
|
|
1267
|
-
};
|
|
1268
|
-
yield { type: "done" };
|
|
1269
|
-
}
|
|
1270
|
-
/**
|
|
1271
|
-
* Load tools with error handling
|
|
1272
|
-
* Returns tools array, logs error if loading fails
|
|
1273
|
-
*/
|
|
1274
|
-
async loadToolsSafely() {
|
|
1275
|
-
try {
|
|
1276
|
-
return await getAllTools();
|
|
1277
|
-
}
|
|
1278
|
-
catch (error) {
|
|
1279
|
-
// Log error but don't throw - continue with empty tools
|
|
1280
|
-
const errorMsg = extractErrorMessage(error);
|
|
1281
|
-
console.warn(`⚠️ Error loading tools: ${errorMsg}`);
|
|
1282
|
-
return [];
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
/**
|
|
1286
|
-
* Format tool result content for display or message
|
|
1287
|
-
* Centralizes the common pattern of formatting success/error output
|
|
1288
|
-
*
|
|
1289
|
-
* @param result - Tool execution result
|
|
1290
|
-
* @param defaultSuccess - Default message if success but no output (default: "Success")
|
|
1291
|
-
* @param defaultError - Default message if error but no error message (default: "Error occurred")
|
|
1292
|
-
* @returns Formatted content string
|
|
1293
|
-
*/
|
|
1294
|
-
formatToolResultContent(result, defaultSuccess = "Success", defaultError = "Error occurred") {
|
|
1295
|
-
return result.success
|
|
1296
|
-
? result.output || defaultSuccess
|
|
1297
|
-
: result.error || defaultError;
|
|
1298
|
-
}
|
|
1299
|
-
/**
|
|
1300
|
-
* Add assistant message to history and conversation
|
|
1301
|
-
*/
|
|
1302
|
-
addAssistantMessage(accumulatedMessage) {
|
|
1303
|
-
// Safely extract tool_calls with proper validation
|
|
1304
|
-
const toolCalls = Array.isArray(accumulatedMessage.tool_calls)
|
|
1305
|
-
? accumulatedMessage.tool_calls
|
|
1306
|
-
: undefined;
|
|
1307
|
-
const assistantEntry = {
|
|
1308
|
-
type: "assistant",
|
|
1309
|
-
content: accumulatedMessage.content || "Using tools to help you...",
|
|
1310
|
-
timestamp: new Date(),
|
|
1311
|
-
toolCalls,
|
|
1312
|
-
};
|
|
1313
|
-
this.chatHistory.push(assistantEntry);
|
|
1314
|
-
this.messages.push({
|
|
1315
|
-
role: "assistant",
|
|
1316
|
-
content: accumulatedMessage.content || "",
|
|
1317
|
-
tool_calls: toolCalls,
|
|
1318
|
-
});
|
|
1319
|
-
// Apply context pruning after adding message to prevent overflow
|
|
1320
|
-
// Critical for long assistant responses and tool results
|
|
1321
|
-
this.applyContextPruning();
|
|
1322
|
-
}
|
|
1323
|
-
/**
|
|
1324
|
-
* Execute tool calls and yield results
|
|
1325
|
-
*/
|
|
1326
|
-
async *executeToolCalls(toolCalls, toolCallsYielded, inputTokens, totalOutputTokens) {
|
|
1327
|
-
// Only yield tool_calls if we haven't already yielded them during streaming
|
|
1328
|
-
if (!toolCallsYielded) {
|
|
1329
|
-
yield {
|
|
1330
|
-
type: "tool_calls",
|
|
1331
|
-
toolCalls,
|
|
1332
|
-
};
|
|
1333
|
-
}
|
|
1334
|
-
// Partition tools into parallel-safe and sequential
|
|
1335
|
-
const { parallel, sequential } = partitionToolCalls(toolCalls);
|
|
1336
|
-
// Helper to process a single tool result
|
|
1337
|
-
const processToolResult = (toolCall, result, executionDurationMs) => {
|
|
1338
|
-
// Record tool call with actual success/failure status for intelligent loop detection
|
|
1339
|
-
const detector = getLoopDetector();
|
|
1340
|
-
detector.recordToolCall(toolCall, result.success);
|
|
1341
|
-
debugLoop(`📝 Recorded: ${toolCall.function.name}, success=${result.success}`);
|
|
1342
|
-
const toolResultEntry = {
|
|
1343
|
-
type: "tool_result",
|
|
1344
|
-
content: this.formatToolResultContent(result),
|
|
1345
|
-
timestamp: new Date(),
|
|
1346
|
-
toolCall: toolCall,
|
|
1347
|
-
toolResult: result,
|
|
1348
|
-
executionDurationMs,
|
|
1349
|
-
};
|
|
1350
|
-
this.chatHistory.push(toolResultEntry);
|
|
1351
|
-
// Add tool result with proper format (needed for AI context)
|
|
1352
|
-
this.messages.push({
|
|
1353
|
-
role: "tool",
|
|
1354
|
-
content: this.formatToolResultContent(result, "Success", "Error"),
|
|
1355
|
-
tool_call_id: toolCall.id,
|
|
1356
|
-
});
|
|
1357
|
-
return toolResultEntry;
|
|
1358
|
-
};
|
|
1359
|
-
// Execute parallel-safe tools concurrently
|
|
1360
|
-
if (parallel.length > 0) {
|
|
1361
|
-
// Check for cancellation before parallel batch
|
|
1362
|
-
if (this.isCancelled()) {
|
|
1363
|
-
yield* this.yieldCancellation();
|
|
1364
|
-
return;
|
|
1365
|
-
}
|
|
1366
|
-
debugLoop(`🚀 Executing ${parallel.length} tools in parallel: ${parallel.map(t => t.function.name).join(", ")}`);
|
|
1367
|
-
// Execute all parallel tools concurrently with Promise.allSettled
|
|
1368
|
-
const parallelPromises = parallel.map(async (toolCall) => {
|
|
1369
|
-
const executionStartTime = Date.now();
|
|
1370
|
-
const result = await this.executeTool(toolCall);
|
|
1371
|
-
const executionDurationMs = Date.now() - executionStartTime;
|
|
1372
|
-
return { toolCall, result, executionDurationMs };
|
|
1373
|
-
});
|
|
1374
|
-
const parallelResults = await Promise.allSettled(parallelPromises);
|
|
1375
|
-
// Process and yield results in order
|
|
1376
|
-
for (const settledResult of parallelResults) {
|
|
1377
|
-
if (settledResult.status === "fulfilled") {
|
|
1378
|
-
const { toolCall, result, executionDurationMs } = settledResult.value;
|
|
1379
|
-
processToolResult(toolCall, result, executionDurationMs);
|
|
1380
|
-
yield {
|
|
1381
|
-
type: "tool_result",
|
|
1382
|
-
toolCall,
|
|
1383
|
-
toolResult: result,
|
|
1384
|
-
executionDurationMs,
|
|
1385
|
-
};
|
|
1386
|
-
}
|
|
1387
|
-
else {
|
|
1388
|
-
// Handle rejected promise (shouldn't happen normally, but be safe)
|
|
1389
|
-
const error = settledResult.reason;
|
|
1390
|
-
debugLoop(`❌ Parallel tool execution failed: ${error}`);
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
// Execute sequential tools one at a time
|
|
1395
|
-
for (const toolCall of sequential) {
|
|
1396
|
-
// Check for cancellation before executing each tool
|
|
1397
|
-
if (this.isCancelled()) {
|
|
1398
|
-
yield* this.yieldCancellation();
|
|
1399
|
-
return;
|
|
1400
|
-
}
|
|
1401
|
-
// Track execution timing
|
|
1402
|
-
const executionStartTime = Date.now();
|
|
1403
|
-
const result = await this.executeTool(toolCall);
|
|
1404
|
-
const executionDurationMs = Date.now() - executionStartTime;
|
|
1405
|
-
processToolResult(toolCall, result, executionDurationMs);
|
|
1406
|
-
yield {
|
|
1407
|
-
type: "tool_result",
|
|
1408
|
-
toolCall,
|
|
1409
|
-
toolResult: result,
|
|
1410
|
-
executionDurationMs,
|
|
1411
|
-
};
|
|
1412
|
-
}
|
|
1413
|
-
// Apply context pruning after adding tool results to prevent overflow
|
|
1414
|
-
// Tool results can be very large (file reads, grep output, etc.)
|
|
1415
|
-
this.applyContextPruning();
|
|
1416
|
-
// Update token count after processing all tool calls
|
|
1417
|
-
inputTokens.value = this.tokenCounter.countMessageTokens(this.messages);
|
|
1418
|
-
yield {
|
|
1419
|
-
type: "token_count",
|
|
1420
|
-
tokenCount: inputTokens.value + totalOutputTokens.value,
|
|
1421
|
-
};
|
|
1422
|
-
}
|
|
1423
|
-
async *processUserMessageStream(message) {
|
|
1424
|
-
// Create new abort controller for this request
|
|
1425
|
-
this.abortController = new AbortController();
|
|
1426
|
-
// Extract text content for analysis (planning, complexity detection)
|
|
1427
|
-
const textContent = this.getTextContentFromMessage(message);
|
|
1428
|
-
// GLM-4.6 OPTIMIZATION: Auto-enable thinking mode for complex tasks
|
|
1429
|
-
// This detects requests that would benefit from extended reasoning
|
|
1430
|
-
const autoThinkingApplied = this.applyAutoThinking(textContent);
|
|
1431
|
-
if (autoThinkingApplied) {
|
|
1432
|
-
// Notify UI that thinking mode was auto-enabled
|
|
1433
|
-
yield {
|
|
1434
|
-
type: "content",
|
|
1435
|
-
content: "🧠 *Auto-thinking enabled for complex task*\n\n"
|
|
1436
|
-
};
|
|
1437
|
-
}
|
|
1438
|
-
// Check if this is a complex request that should use multi-phase planning
|
|
1439
|
-
// Note: Planning currently only works with text-only messages
|
|
1440
|
-
if (typeof message === 'string' && this.shouldCreatePlan(textContent)) {
|
|
1441
|
-
// Delegate to planning processor
|
|
1442
|
-
yield* this.processWithPlanning(textContent);
|
|
1443
|
-
yield { type: "done" };
|
|
1444
|
-
return;
|
|
1445
|
-
}
|
|
1446
|
-
// Reset tool call tracking for new message
|
|
1447
|
-
this.resetToolCallTracking();
|
|
1448
|
-
// Prepare user message and get input tokens
|
|
1449
|
-
const inputTokensRef = { value: this.prepareUserMessageForStreaming(message) };
|
|
1450
|
-
yield {
|
|
1451
|
-
type: "token_count",
|
|
1452
|
-
tokenCount: inputTokensRef.value,
|
|
1453
|
-
};
|
|
1454
|
-
// Yield context warnings if needed
|
|
1455
|
-
yield* this.yieldContextWarnings();
|
|
1456
|
-
const maxToolRounds = this.maxToolRounds;
|
|
1457
|
-
let toolRounds = 0;
|
|
1458
|
-
const totalOutputTokensRef = { value: 0 };
|
|
1459
|
-
const lastTokenUpdateRef = { value: 0 };
|
|
1460
|
-
try {
|
|
1461
|
-
// Agent loop - continue until no more tool calls or max rounds reached
|
|
1462
|
-
while (toolRounds < maxToolRounds) {
|
|
1463
|
-
debugLoop(`Agent loop iteration, toolRounds: ${toolRounds}`);
|
|
1464
|
-
// Check if operation was cancelled
|
|
1465
|
-
if (this.isCancelled()) {
|
|
1466
|
-
yield* this.yieldCancellation();
|
|
1467
|
-
return;
|
|
1468
|
-
}
|
|
1469
|
-
// Load tools safely
|
|
1470
|
-
const tools = await this.loadToolsSafely();
|
|
1471
|
-
// Yield warning if no tools available
|
|
1472
|
-
if (tools.length === 0) {
|
|
1473
|
-
yield {
|
|
1474
|
-
type: "content",
|
|
1475
|
-
content: "\n⚠️ No tools available, continuing with limited functionality...\n\n"
|
|
1476
|
-
};
|
|
1477
|
-
}
|
|
1478
|
-
// Create chat stream
|
|
1479
|
-
const stream = this.llmClient.chatStream(this.messages, tools, this.buildChatOptions({
|
|
1480
|
-
searchOptions: { search_parameters: { mode: "off" } }
|
|
1481
|
-
}));
|
|
1482
|
-
// Process streaming chunks (delegated to StreamHandler - Phase 2 refactoring)
|
|
1483
|
-
const chunkGen = this.streamHandler.processChunks(stream, {
|
|
1484
|
-
inputTokens: inputTokensRef.value,
|
|
1485
|
-
lastTokenUpdate: lastTokenUpdateRef,
|
|
1486
|
-
totalOutputTokens: totalOutputTokensRef,
|
|
1487
|
-
});
|
|
1488
|
-
let streamResult;
|
|
1489
|
-
for await (const chunk of chunkGen) {
|
|
1490
|
-
if ('accumulated' in chunk) {
|
|
1491
|
-
streamResult = chunk;
|
|
1492
|
-
}
|
|
1493
|
-
else {
|
|
1494
|
-
yield chunk;
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
if (!streamResult) {
|
|
1498
|
-
continue;
|
|
1499
|
-
}
|
|
1500
|
-
// Add assistant message to history
|
|
1501
|
-
this.addAssistantMessage(streamResult.accumulated);
|
|
1502
|
-
// Handle tool calls if present
|
|
1503
|
-
if (streamResult.accumulated.tool_calls && streamResult.accumulated.tool_calls.length > 0) {
|
|
1504
|
-
toolRounds++;
|
|
1505
|
-
// Check for repetitive tool calls (loop detection)
|
|
1506
|
-
const hasRepetitiveCall = streamResult.accumulated.tool_calls.some((tc) => this.isRepetitiveToolCall(tc));
|
|
1507
|
-
if (hasRepetitiveCall) {
|
|
1508
|
-
const loopMsg = this.getLoopWarningMessage();
|
|
1509
|
-
yield {
|
|
1510
|
-
type: "content",
|
|
1511
|
-
content: loopMsg,
|
|
1512
|
-
};
|
|
1513
|
-
break;
|
|
1514
|
-
}
|
|
1515
|
-
yield* this.executeToolCalls(streamResult.accumulated.tool_calls, streamResult.yielded, inputTokensRef, totalOutputTokensRef);
|
|
1516
|
-
// Continue loop to get next response
|
|
1517
|
-
}
|
|
1518
|
-
else {
|
|
1519
|
-
// No tool calls, we're done
|
|
1520
|
-
break;
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
// Check if max rounds reached
|
|
1524
|
-
if (toolRounds >= maxToolRounds) {
|
|
1525
|
-
yield {
|
|
1526
|
-
type: "content",
|
|
1527
|
-
content: "\n\nMaximum tool execution rounds reached. Let me provide what I've accomplished.",
|
|
1528
|
-
};
|
|
1529
|
-
}
|
|
1530
|
-
yield { type: "done" };
|
|
1531
|
-
}
|
|
1532
|
-
catch (error) {
|
|
1533
|
-
// Check if this was a cancellation
|
|
1534
|
-
if (this.isCancelled()) {
|
|
1535
|
-
yield* this.yieldCancellation();
|
|
1536
|
-
return;
|
|
1537
|
-
}
|
|
1538
|
-
const errorMsg = extractErrorMessage(error);
|
|
1539
|
-
const errorEntry = {
|
|
1540
|
-
type: "assistant",
|
|
1541
|
-
content: `Sorry, I encountered an error: ${errorMsg}`,
|
|
1542
|
-
timestamp: new Date(),
|
|
1543
|
-
};
|
|
1544
|
-
this.chatHistory.push(errorEntry);
|
|
1545
|
-
yield {
|
|
1546
|
-
type: "content",
|
|
1547
|
-
content: errorEntry.content,
|
|
1548
|
-
};
|
|
1549
|
-
yield { type: "done" };
|
|
1550
|
-
}
|
|
1551
|
-
finally {
|
|
1552
|
-
// Clean up abort controller
|
|
1553
|
-
this.abortController = null;
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
/**
|
|
1557
|
-
* Execute a tool call using the ToolExecutor
|
|
1558
|
-
* Handles tool approval for VSCode integration before delegation
|
|
1559
|
-
*/
|
|
1560
|
-
async executeTool(toolCall) {
|
|
1561
|
-
// Check if tool approval is required (for VSCode integration)
|
|
1562
|
-
if (this.requireToolApproval) {
|
|
1563
|
-
// Only require approval for file modification operations
|
|
1564
|
-
const needsApproval = toolCall.function.name === "create_file" ||
|
|
1565
|
-
toolCall.function.name === "str_replace_editor" ||
|
|
1566
|
-
toolCall.function.name === "insert_text";
|
|
1567
|
-
if (needsApproval) {
|
|
1568
|
-
// Emit event and wait for approval
|
|
1569
|
-
const approved = await this.waitForToolApproval(toolCall);
|
|
1570
|
-
if (!approved) {
|
|
1571
|
-
// User rejected the change
|
|
1572
|
-
this.emit('tool:rejected', toolCall);
|
|
1573
|
-
return {
|
|
1574
|
-
success: false,
|
|
1575
|
-
error: 'Change rejected by user'
|
|
1576
|
-
};
|
|
1577
|
-
}
|
|
1578
|
-
// User approved
|
|
1579
|
-
this.emit('tool:approved', toolCall);
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
// Delegate to ToolExecutor (Phase 2 refactoring)
|
|
1583
|
-
return await this.toolExecutor.execute(toolCall);
|
|
1584
|
-
}
|
|
1585
|
-
getChatHistory() {
|
|
1586
|
-
this.checkDisposed();
|
|
1587
|
-
return [...this.chatHistory];
|
|
1588
|
-
}
|
|
1589
|
-
getCurrentDirectory() {
|
|
1590
|
-
return this.toolExecutor.getBashTool().getCurrentDirectory();
|
|
1591
|
-
}
|
|
1592
|
-
async executeBashCommand(command) {
|
|
1593
|
-
return await this.toolExecutor.getBashTool().execute(command);
|
|
1594
|
-
}
|
|
1595
|
-
/**
|
|
1596
|
-
* Check if a bash command is currently executing
|
|
1597
|
-
*/
|
|
1598
|
-
isBashExecuting() {
|
|
1599
|
-
return this.toolExecutor.getBashTool().isExecuting();
|
|
1600
|
-
}
|
|
1601
|
-
/**
|
|
1602
|
-
* Move currently running bash command to background
|
|
1603
|
-
* Returns task ID if successful, null otherwise
|
|
1604
|
-
*/
|
|
1605
|
-
moveBashToBackground() {
|
|
1606
|
-
return this.toolExecutor.getBashTool().moveToBackground();
|
|
1607
|
-
}
|
|
1608
|
-
getCurrentModel() {
|
|
1609
|
-
return this.llmClient.getCurrentModel();
|
|
1610
|
-
}
|
|
1611
|
-
setModel(model) {
|
|
1612
|
-
this.llmClient.setModel(model);
|
|
1613
|
-
// Update token counter for new model (use singleton)
|
|
1614
|
-
this.tokenCounter = getTokenCounter(model);
|
|
1615
|
-
// Update stream handler model for usage tracking
|
|
1616
|
-
this.streamHandler.setModel(model);
|
|
1617
|
-
}
|
|
1618
|
-
abortCurrentOperation() {
|
|
1619
|
-
if (this.abortController) {
|
|
1620
|
-
this.abortController.abort();
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
/**
|
|
1624
|
-
* Get current context window usage percentage
|
|
1625
|
-
* Returns a number between 0-100
|
|
1626
|
-
*/
|
|
1627
|
-
getContextPercentage() {
|
|
1628
|
-
const stats = this.contextManager.getStats(this.messages, this.tokenCounter);
|
|
1629
|
-
return Math.round(stats.percentage * 100) / 100;
|
|
1630
|
-
}
|
|
1631
|
-
/**
|
|
1632
|
-
* Create a checkpoint of current state
|
|
1633
|
-
*/
|
|
1634
|
-
async createCheckpoint(description) {
|
|
1635
|
-
const files = [];
|
|
1636
|
-
// For now, we don't capture file state automatically
|
|
1637
|
-
// This can be enhanced to capture modified files from tool calls
|
|
1638
|
-
const checkpoint = await this.checkpointManager.createCheckpoint({
|
|
1639
|
-
files,
|
|
1640
|
-
conversationState: this.chatHistory,
|
|
1641
|
-
description: description || 'Manual checkpoint',
|
|
1642
|
-
metadata: {
|
|
1643
|
-
model: this.llmClient.getCurrentModel(),
|
|
1644
|
-
triggeredBy: 'user',
|
|
1645
|
-
},
|
|
1646
|
-
});
|
|
1647
|
-
return checkpoint.id;
|
|
1648
|
-
}
|
|
1649
|
-
/**
|
|
1650
|
-
* Rewind conversation to a checkpoint
|
|
1651
|
-
*/
|
|
1652
|
-
async rewindConversation(checkpointId) {
|
|
1653
|
-
try {
|
|
1654
|
-
const conversationState = await this.checkpointManager.getConversationState(checkpointId);
|
|
1655
|
-
if (!conversationState) {
|
|
1656
|
-
return {
|
|
1657
|
-
success: false,
|
|
1658
|
-
error: `Checkpoint ${checkpointId} not found`,
|
|
1659
|
-
};
|
|
1660
|
-
}
|
|
1661
|
-
// Restore conversation state
|
|
1662
|
-
this.chatHistory = [...conversationState];
|
|
1663
|
-
// Rebuild messages array from chat history
|
|
1664
|
-
// Safely preserve system message if it exists
|
|
1665
|
-
const systemMessage = this.messages.length > 0 ? this.messages[0] : null;
|
|
1666
|
-
this.messages = systemMessage ? [systemMessage] : [];
|
|
1667
|
-
// CRITICAL FIX: Track tool calls to validate tool results
|
|
1668
|
-
// Prevents API errors from orphaned tool results without corresponding tool calls
|
|
1669
|
-
const toolCallIds = new Set();
|
|
1670
|
-
for (const entry of conversationState) {
|
|
1671
|
-
if (entry.type === 'user') {
|
|
1672
|
-
this.messages.push({
|
|
1673
|
-
role: 'user',
|
|
1674
|
-
content: entry.content,
|
|
1675
|
-
});
|
|
1676
|
-
}
|
|
1677
|
-
else if (entry.type === 'assistant') {
|
|
1678
|
-
// Track tool call IDs from assistant messages
|
|
1679
|
-
if (entry.toolCalls && Array.isArray(entry.toolCalls)) {
|
|
1680
|
-
for (const toolCall of entry.toolCalls) {
|
|
1681
|
-
if (toolCall?.id) {
|
|
1682
|
-
toolCallIds.add(toolCall.id);
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
|
-
this.messages.push({
|
|
1687
|
-
role: 'assistant',
|
|
1688
|
-
content: entry.content,
|
|
1689
|
-
tool_calls: entry.toolCalls,
|
|
1690
|
-
});
|
|
1691
|
-
}
|
|
1692
|
-
else if (entry.type === 'tool_result' && entry.toolCall) {
|
|
1693
|
-
// CRITICAL FIX: Only add tool result if corresponding tool call exists
|
|
1694
|
-
// This prevents "tool message without corresponding tool call" API errors
|
|
1695
|
-
if (toolCallIds.has(entry.toolCall.id)) {
|
|
1696
|
-
this.messages.push({
|
|
1697
|
-
role: 'tool',
|
|
1698
|
-
content: entry.content,
|
|
1699
|
-
tool_call_id: entry.toolCall.id,
|
|
1700
|
-
});
|
|
1701
|
-
}
|
|
1702
|
-
else {
|
|
1703
|
-
console.warn(`Skipping orphaned tool result for tool_call_id: ${entry.toolCall.id}`);
|
|
1704
|
-
}
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
this.emit('system', `Conversation rewound to checkpoint ${checkpointId}`);
|
|
1708
|
-
return { success: true };
|
|
1709
|
-
}
|
|
1710
|
-
catch (error) {
|
|
1711
|
-
const errorMsg = extractErrorMessage(error);
|
|
1712
|
-
return {
|
|
1713
|
-
success: false,
|
|
1714
|
-
error: `Failed to rewind: ${errorMsg}`,
|
|
1715
|
-
};
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
/**
|
|
1719
|
-
* Get checkpoint manager instance
|
|
1720
|
-
*/
|
|
1721
|
-
getCheckpointManager() {
|
|
1722
|
-
return this.checkpointManager;
|
|
1723
|
-
}
|
|
1724
|
-
/**
|
|
1725
|
-
* Get subagent orchestrator instance
|
|
1726
|
-
*/
|
|
1727
|
-
getSubagentOrchestrator() {
|
|
1728
|
-
return this.subagentOrchestrator;
|
|
1729
|
-
}
|
|
1730
|
-
/**
|
|
1731
|
-
* Spawn a specialized subagent for a specific task
|
|
1732
|
-
* This is a user-facing method that simplifies subagent usage
|
|
1733
|
-
*
|
|
1734
|
-
* @param role - The role/specialization of the subagent
|
|
1735
|
-
* @param description - Task description
|
|
1736
|
-
* @param context - Optional additional context
|
|
1737
|
-
* @returns The result of the subagent execution
|
|
1738
|
-
*/
|
|
1739
|
-
async spawnSubagent(role, description, context) {
|
|
1740
|
-
try {
|
|
1741
|
-
// Import parseSubagentRole helper to convert string to enum
|
|
1742
|
-
const { parseSubagentRole } = await import('./subagent-types.js');
|
|
1743
|
-
const subagentRole = parseSubagentRole(role);
|
|
1744
|
-
// Spawn the subagent
|
|
1745
|
-
const subagent = await this.subagentOrchestrator.spawnSubagent(subagentRole);
|
|
1746
|
-
// Execute the task
|
|
1747
|
-
const result = await subagent.executeTask({
|
|
1748
|
-
id: `task-${Date.now()}`,
|
|
1749
|
-
description,
|
|
1750
|
-
role: subagentRole,
|
|
1751
|
-
priority: 1,
|
|
1752
|
-
context: {
|
|
1753
|
-
files: context?.files || [],
|
|
1754
|
-
conversationHistory: this.chatHistory.slice(-10), // Last 10 messages
|
|
1755
|
-
metadata: {
|
|
1756
|
-
workingDirectory: process.cwd(),
|
|
1757
|
-
additionalContext: context?.additionalContext,
|
|
1758
|
-
},
|
|
1759
|
-
},
|
|
1760
|
-
});
|
|
1761
|
-
return {
|
|
1762
|
-
success: result.success,
|
|
1763
|
-
output: result.output,
|
|
1764
|
-
filesModified: result.filesModified,
|
|
1765
|
-
filesCreated: result.filesCreated,
|
|
1766
|
-
error: result.error,
|
|
1767
|
-
};
|
|
1768
|
-
}
|
|
1769
|
-
catch (error) {
|
|
1770
|
-
const errorMsg = extractErrorMessage(error);
|
|
1771
|
-
return {
|
|
1772
|
-
success: false,
|
|
1773
|
-
output: '',
|
|
1774
|
-
error: `Failed to spawn subagent: ${errorMsg}`,
|
|
1775
|
-
};
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
/**
|
|
1779
|
-
* Execute multiple tasks in parallel using subagents
|
|
1780
|
-
* This automatically handles dependency resolution and parallel execution
|
|
1781
|
-
*
|
|
1782
|
-
* @param tasks - Array of tasks with role and description
|
|
1783
|
-
* @returns Array of results from all tasks
|
|
1784
|
-
*/
|
|
1785
|
-
async executeParallelTasks(tasks) {
|
|
1786
|
-
try {
|
|
1787
|
-
// Import parseSubagentRole helper to convert string to enum
|
|
1788
|
-
const { parseSubagentRole } = await import('./subagent-types.js');
|
|
1789
|
-
// Convert tasks to SubagentTask format
|
|
1790
|
-
const subagentTasks = tasks.map((task, index) => ({
|
|
1791
|
-
id: task.id || `task-${index}-${Date.now()}`,
|
|
1792
|
-
description: task.description,
|
|
1793
|
-
role: parseSubagentRole(task.role),
|
|
1794
|
-
priority: 1,
|
|
1795
|
-
context: {
|
|
1796
|
-
files: [],
|
|
1797
|
-
conversationHistory: this.chatHistory.slice(-10),
|
|
1798
|
-
metadata: {
|
|
1799
|
-
workingDirectory: process.cwd(),
|
|
1800
|
-
},
|
|
1801
|
-
},
|
|
1802
|
-
dependencies: task.dependencies || [],
|
|
1803
|
-
}));
|
|
1804
|
-
// Execute all tasks in parallel with dependency resolution
|
|
1805
|
-
const results = await this.subagentOrchestrator.executeParallel(subagentTasks);
|
|
1806
|
-
// Convert results to simpler format
|
|
1807
|
-
return results.map(result => ({
|
|
1808
|
-
taskId: result.taskId,
|
|
1809
|
-
success: result.success,
|
|
1810
|
-
output: result.output,
|
|
1811
|
-
filesModified: result.filesModified,
|
|
1812
|
-
filesCreated: result.filesCreated,
|
|
1813
|
-
error: result.error,
|
|
1814
|
-
}));
|
|
1815
|
-
}
|
|
1816
|
-
catch (error) {
|
|
1817
|
-
const errorMsg = extractErrorMessage(error);
|
|
1818
|
-
return [{
|
|
1819
|
-
taskId: 'error',
|
|
1820
|
-
success: false,
|
|
1821
|
-
output: '',
|
|
1822
|
-
error: `Failed to execute parallel tasks: ${errorMsg}`,
|
|
1823
|
-
}];
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
/**
|
|
1827
|
-
* Check if agent has been disposed
|
|
1828
|
-
* @internal
|
|
1829
|
-
*/
|
|
1830
|
-
checkDisposed() {
|
|
1831
|
-
if (this.disposed) {
|
|
1832
|
-
throw new SDKError(SDKErrorCode.AGENT_DISPOSED, 'Agent has been disposed and cannot be used. Create a new agent instance.');
|
|
1833
|
-
}
|
|
1834
|
-
}
|
|
1835
|
-
/**
|
|
1836
|
-
* Dispose of resources and remove event listeners
|
|
1837
|
-
*
|
|
1838
|
-
* This method should be called when the agent is no longer needed to prevent
|
|
1839
|
-
* memory leaks and properly close all connections.
|
|
1840
|
-
*
|
|
1841
|
-
* After calling dispose(), the agent cannot be used anymore. Any method calls
|
|
1842
|
-
* will throw an AGENT_DISPOSED error.
|
|
1843
|
-
*
|
|
1844
|
-
* Cleans up:
|
|
1845
|
-
* - Event listeners
|
|
1846
|
-
* - In-memory caches (tool calls, arguments)
|
|
1847
|
-
* - Token counter and context manager
|
|
1848
|
-
* - Aborts in-flight requests
|
|
1849
|
-
* - Terminates subagents
|
|
1850
|
-
* - Clears conversation history
|
|
1851
|
-
*
|
|
1852
|
-
* @example
|
|
1853
|
-
* ```typescript
|
|
1854
|
-
* const agent = await createAgent();
|
|
1855
|
-
* try {
|
|
1856
|
-
* await agent.processUserMessage('task');
|
|
1857
|
-
* } finally {
|
|
1858
|
-
* agent.dispose(); // Always cleanup
|
|
1859
|
-
* }
|
|
1860
|
-
* ```
|
|
1861
|
-
*/
|
|
1862
|
-
dispose() {
|
|
1863
|
-
if (this.disposed)
|
|
1864
|
-
return; // Already disposed, safe to call multiple times
|
|
1865
|
-
this.disposed = true;
|
|
1866
|
-
// Remove all event listeners to prevent memory leaks
|
|
1867
|
-
this.removeAllListeners();
|
|
1868
|
-
// CRITICAL FIX: Remove event listener from contextManager to prevent memory leak
|
|
1869
|
-
// Only remove the specific listener we registered, not all listeners for this event
|
|
1870
|
-
if (this.contextOverflowListener) {
|
|
1871
|
-
this.contextManager.removeListener('before_prune', this.contextOverflowListener);
|
|
1872
|
-
this.contextOverflowListener = undefined;
|
|
1873
|
-
}
|
|
1874
|
-
// Dispose tool executor (includes all tools with cleanup methods)
|
|
1875
|
-
this.toolExecutor.dispose();
|
|
1876
|
-
// Clear in-memory caches
|
|
1877
|
-
this.toolCallIndexMap.clear();
|
|
1878
|
-
this.toolCallArgsCache.clear();
|
|
1879
|
-
// BUG FIX: Clear all pending tool approval timeouts to prevent memory leaks
|
|
1880
|
-
// These timers would otherwise keep running for up to 5 minutes after dispose
|
|
1881
|
-
for (const timeout of this.toolApprovalTimeouts.values()) {
|
|
1882
|
-
clearTimeout(timeout);
|
|
1883
|
-
}
|
|
1884
|
-
this.toolApprovalTimeouts.clear();
|
|
1885
|
-
// BUG FIX: Clear all cleanup timeouts to prevent post-disposal timer callbacks
|
|
1886
|
-
for (const timeout of this.toolApprovalCleanupTimeouts) {
|
|
1887
|
-
clearTimeout(timeout);
|
|
1888
|
-
}
|
|
1889
|
-
this.toolApprovalCleanupTimeouts.clear();
|
|
1890
|
-
// Resolve any pending approval callbacks so awaiting promises don't hang forever
|
|
1891
|
-
for (const [, callback] of this.toolApprovalCallbacks) {
|
|
1892
|
-
try {
|
|
1893
|
-
callback(false);
|
|
1894
|
-
}
|
|
1895
|
-
catch {
|
|
1896
|
-
// Ignore callback errors during teardown
|
|
1897
|
-
}
|
|
1898
|
-
}
|
|
1899
|
-
this.toolApprovalCallbacks.clear();
|
|
1900
|
-
this.toolApprovalResolved.clear();
|
|
1901
|
-
// Clear conversation history to free memory
|
|
1902
|
-
this.chatHistory = [];
|
|
1903
|
-
this.messages = [];
|
|
1904
|
-
// Dispose context manager (tokenCounter is a singleton, don't dispose)
|
|
1905
|
-
// CRITICAL FIX: tokenCounter is obtained via getTokenCounter() which returns
|
|
1906
|
-
// a shared singleton instance. Disposing it would break other agent instances
|
|
1907
|
-
// using the same model. The singleton manages its own lifecycle.
|
|
1908
|
-
this.contextManager.dispose();
|
|
1909
|
-
// Abort any in-flight requests
|
|
1910
|
-
if (this.abortController) {
|
|
1911
|
-
this.abortController.abort();
|
|
1912
|
-
this.abortController = null;
|
|
1913
|
-
}
|
|
1914
|
-
// Terminate all subagents
|
|
1915
|
-
this.subagentOrchestrator.terminateAll().catch((error) => {
|
|
1916
|
-
console.warn('Error terminating subagents:', error);
|
|
1917
|
-
});
|
|
1918
|
-
// Note: We don't disconnect MCP servers here because they might be shared
|
|
1919
|
-
// across multiple agent instances. MCP connections are managed globally
|
|
1920
|
-
// by the MCPManager singleton and will be cleaned up on process exit.
|
|
1921
|
-
}
|
|
1922
|
-
/**
|
|
1923
|
-
* Clean up resources and remove all event listeners.
|
|
1924
|
-
* @deprecated Use dispose() instead for complete cleanup.
|
|
1925
|
-
* This method now delegates to dispose() for backwards compatibility.
|
|
1926
|
-
*/
|
|
1927
|
-
destroy() {
|
|
1928
|
-
this.dispose();
|
|
1929
|
-
}
|
|
1930
|
-
}
|
|
1931
|
-
//# sourceMappingURL=llm-agent.js.map
|