@falai/agent 0.8.1 → 0.9.0-alpha-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +332 -147
- package/dist/{adapters → cjs/src/adapters}/MemoryAdapter.d.ts +4 -4
- package/dist/cjs/src/adapters/MemoryAdapter.d.ts.map +1 -0
- package/dist/cjs/{adapters → src/adapters}/MemoryAdapter.js +41 -21
- package/dist/cjs/src/adapters/MemoryAdapter.js.map +1 -0
- package/dist/{adapters → cjs/src/adapters}/MongoAdapter.d.ts +3 -3
- package/dist/cjs/src/adapters/MongoAdapter.d.ts.map +1 -0
- package/dist/cjs/{adapters → src/adapters}/MongoAdapter.js +2 -1
- package/dist/cjs/src/adapters/MongoAdapter.js.map +1 -0
- package/dist/cjs/{adapters → src/adapters}/OpenSearchAdapter.d.ts +3 -3
- package/dist/cjs/src/adapters/OpenSearchAdapter.d.ts.map +1 -0
- package/dist/cjs/{adapters → src/adapters}/OpenSearchAdapter.js +10 -13
- package/dist/cjs/src/adapters/OpenSearchAdapter.js.map +1 -0
- package/dist/cjs/{adapters → src/adapters}/PostgreSQLAdapter.d.ts +3 -3
- package/dist/cjs/src/adapters/PostgreSQLAdapter.d.ts.map +1 -0
- package/dist/cjs/{adapters → src/adapters}/PostgreSQLAdapter.js +1 -1
- package/dist/cjs/src/adapters/PostgreSQLAdapter.js.map +1 -0
- package/dist/cjs/{adapters → src/adapters}/PrismaAdapter.d.ts +3 -3
- package/dist/cjs/src/adapters/PrismaAdapter.d.ts.map +1 -0
- package/dist/cjs/{adapters → src/adapters}/PrismaAdapter.js +35 -5
- package/dist/cjs/src/adapters/PrismaAdapter.js.map +1 -0
- package/dist/cjs/{adapters → src/adapters}/RedisAdapter.d.ts +3 -3
- package/dist/cjs/src/adapters/RedisAdapter.d.ts.map +1 -0
- package/dist/cjs/{adapters → src/adapters}/RedisAdapter.js +3 -2
- package/dist/cjs/src/adapters/RedisAdapter.js.map +1 -0
- package/dist/{adapters → cjs/src/adapters}/SQLiteAdapter.d.ts +3 -3
- package/dist/cjs/src/adapters/SQLiteAdapter.d.ts.map +1 -0
- package/dist/cjs/{adapters → src/adapters}/SQLiteAdapter.js +2 -1
- package/dist/cjs/src/adapters/SQLiteAdapter.js.map +1 -0
- package/dist/cjs/src/adapters/index.d.ts.map +1 -0
- package/dist/cjs/src/adapters/index.js.map +1 -0
- package/dist/cjs/src/constants/index.d.ts.map +1 -0
- package/dist/cjs/src/constants/index.js.map +1 -0
- package/dist/cjs/src/core/Agent.d.ts +223 -0
- package/dist/cjs/src/core/Agent.d.ts.map +1 -0
- package/dist/cjs/src/core/Agent.js +1660 -0
- package/dist/cjs/src/core/Agent.js.map +1 -0
- package/dist/cjs/src/core/Events.d.ts +26 -0
- package/dist/cjs/src/core/Events.d.ts.map +1 -0
- package/dist/cjs/src/core/Events.js +144 -0
- package/dist/cjs/src/core/Events.js.map +1 -0
- package/dist/{core → cjs/src/core}/PersistenceManager.d.ts +21 -19
- package/dist/cjs/src/core/PersistenceManager.d.ts.map +1 -0
- package/dist/cjs/{core → src/core}/PersistenceManager.js +73 -20
- package/dist/cjs/src/core/PersistenceManager.js.map +1 -0
- package/dist/cjs/src/core/PromptComposer.d.ts +27 -0
- package/dist/cjs/src/core/PromptComposer.d.ts.map +1 -0
- package/dist/cjs/src/core/PromptComposer.js +157 -0
- package/dist/cjs/src/core/PromptComposer.js.map +1 -0
- package/dist/cjs/src/core/ResponseEngine.d.ts +32 -0
- package/dist/cjs/src/core/ResponseEngine.d.ts.map +1 -0
- package/dist/cjs/src/core/ResponseEngine.js +84 -0
- package/dist/cjs/src/core/ResponseEngine.js.map +1 -0
- package/dist/cjs/src/core/ResponsePipeline.d.ts +171 -0
- package/dist/cjs/src/core/ResponsePipeline.d.ts.map +1 -0
- package/dist/cjs/src/core/ResponsePipeline.js +514 -0
- package/dist/cjs/src/core/ResponsePipeline.js.map +1 -0
- package/dist/cjs/src/core/Route.d.ts +145 -0
- package/dist/cjs/src/core/Route.d.ts.map +1 -0
- package/dist/cjs/src/core/Route.js +343 -0
- package/dist/cjs/src/core/Route.js.map +1 -0
- package/dist/cjs/src/core/RoutingEngine.d.ts +129 -0
- package/dist/cjs/src/core/RoutingEngine.d.ts.map +1 -0
- package/dist/cjs/{core → src/core}/RoutingEngine.js +215 -117
- package/dist/cjs/src/core/RoutingEngine.js.map +1 -0
- package/dist/cjs/src/core/SessionManager.d.ts +86 -0
- package/dist/cjs/src/core/SessionManager.d.ts.map +1 -0
- package/dist/cjs/src/core/SessionManager.js +217 -0
- package/dist/cjs/src/core/SessionManager.js.map +1 -0
- package/dist/cjs/src/core/Step.d.ts +96 -0
- package/dist/cjs/src/core/Step.d.ts.map +1 -0
- package/dist/cjs/src/core/Step.js +206 -0
- package/dist/cjs/src/core/Step.js.map +1 -0
- package/dist/cjs/src/core/ToolExecutor.d.ts +45 -0
- package/dist/cjs/src/core/ToolExecutor.d.ts.map +1 -0
- package/dist/cjs/{core → src/core}/ToolExecutor.js +30 -19
- package/dist/cjs/src/core/ToolExecutor.js.map +1 -0
- package/dist/{index.d.ts → cjs/src/index.d.ts} +7 -15
- package/dist/cjs/src/index.d.ts.map +1 -0
- package/dist/cjs/{index.js → src/index.js} +21 -19
- package/dist/cjs/src/index.js.map +1 -0
- package/dist/cjs/{providers → src/providers}/AnthropicProvider.d.ts +1 -1
- package/dist/cjs/src/providers/AnthropicProvider.d.ts.map +1 -0
- package/dist/cjs/{providers → src/providers}/AnthropicProvider.js +54 -2
- package/dist/cjs/src/providers/AnthropicProvider.js.map +1 -0
- package/dist/{providers → cjs/src/providers}/GeminiProvider.d.ts +1 -1
- package/dist/cjs/src/providers/GeminiProvider.d.ts.map +1 -0
- package/dist/cjs/{providers → src/providers}/GeminiProvider.js +65 -0
- package/dist/cjs/src/providers/GeminiProvider.js.map +1 -0
- package/dist/cjs/{providers → src/providers}/OpenAIProvider.d.ts +1 -1
- package/dist/cjs/src/providers/OpenAIProvider.d.ts.map +1 -0
- package/dist/cjs/{providers → src/providers}/OpenAIProvider.js +70 -1
- package/dist/cjs/src/providers/OpenAIProvider.js.map +1 -0
- package/dist/{providers → cjs/src/providers}/OpenRouterProvider.d.ts +1 -1
- package/dist/cjs/src/providers/OpenRouterProvider.d.ts.map +1 -0
- package/dist/cjs/{providers → src/providers}/OpenRouterProvider.js +76 -0
- package/dist/cjs/src/providers/OpenRouterProvider.js.map +1 -0
- package/dist/cjs/src/providers/index.d.ts.map +1 -0
- package/dist/cjs/src/providers/index.js.map +1 -0
- package/dist/cjs/{types → src/types}/agent.d.ts +80 -41
- package/dist/cjs/src/types/agent.d.ts.map +1 -0
- package/dist/cjs/src/types/agent.js.map +1 -0
- package/dist/cjs/{types → src/types}/ai.d.ts +7 -0
- package/dist/cjs/src/types/ai.d.ts.map +1 -0
- package/dist/cjs/src/types/ai.js.map +1 -0
- package/dist/{types → cjs/src/types}/history.d.ts +76 -18
- package/dist/cjs/src/types/history.d.ts.map +1 -0
- package/dist/cjs/src/types/history.js +33 -0
- package/dist/cjs/src/types/history.js.map +1 -0
- package/dist/cjs/src/types/index.d.ts +20 -0
- package/dist/cjs/src/types/index.d.ts.map +1 -0
- package/dist/cjs/src/types/index.js +30 -0
- package/dist/cjs/src/types/index.js.map +1 -0
- package/dist/{types → cjs/src/types}/persistence.d.ts +38 -23
- package/dist/cjs/src/types/persistence.d.ts.map +1 -0
- package/dist/cjs/src/types/persistence.js.map +1 -0
- package/dist/cjs/src/types/route.d.ts +235 -0
- package/dist/cjs/src/types/route.d.ts.map +1 -0
- package/dist/cjs/{types → src/types}/route.js.map +1 -1
- package/dist/cjs/src/types/routing.d.ts.map +1 -0
- package/dist/{types → cjs/src/types}/routing.js.map +1 -1
- package/dist/cjs/src/types/schema.d.ts.map +1 -0
- package/dist/{types → cjs/src/types}/schema.js.map +1 -1
- package/dist/cjs/src/types/session.d.ts +65 -0
- package/dist/cjs/src/types/session.d.ts.map +1 -0
- package/dist/cjs/src/types/session.js +6 -0
- package/dist/cjs/src/types/session.js.map +1 -0
- package/dist/cjs/src/types/template.d.ts +30 -0
- package/dist/cjs/src/types/template.d.ts.map +1 -0
- package/dist/cjs/src/types/template.js +3 -0
- package/dist/cjs/src/types/template.js.map +1 -0
- package/dist/{types → cjs/src/types}/tool.d.ts +17 -13
- package/dist/cjs/src/types/tool.d.ts.map +1 -0
- package/dist/cjs/{types → src/types}/tool.js.map +1 -1
- package/dist/cjs/src/utils/clone.d.ts +8 -0
- package/dist/cjs/src/utils/clone.d.ts.map +1 -0
- package/dist/cjs/src/utils/clone.js +36 -0
- package/dist/cjs/src/utils/clone.js.map +1 -0
- package/dist/{utils → cjs/src/utils}/event.d.ts +1 -1
- package/dist/cjs/src/utils/event.d.ts.map +1 -0
- package/dist/cjs/{utils → src/utils}/event.js +2 -2
- package/dist/cjs/src/utils/event.js.map +1 -0
- package/dist/cjs/src/utils/history.d.ts +31 -0
- package/dist/cjs/src/utils/history.d.ts.map +1 -0
- package/dist/cjs/src/utils/history.js +128 -0
- package/dist/cjs/src/utils/history.js.map +1 -0
- package/dist/cjs/src/utils/id.d.ts.map +1 -0
- package/dist/cjs/src/utils/id.js.map +1 -0
- package/dist/cjs/src/utils/index.d.ts +13 -0
- package/dist/cjs/src/utils/index.d.ts.map +1 -0
- package/dist/cjs/src/utils/index.js +49 -0
- package/dist/cjs/src/utils/index.js.map +1 -0
- package/dist/cjs/src/utils/logger.d.ts.map +1 -0
- package/dist/cjs/src/utils/logger.js.map +1 -0
- package/dist/cjs/src/utils/retry.d.ts.map +1 -0
- package/dist/cjs/src/utils/retry.js.map +1 -0
- package/dist/cjs/src/utils/session.d.ts +51 -0
- package/dist/cjs/src/utils/session.d.ts.map +1 -0
- package/dist/cjs/{types → src/utils}/session.js +35 -32
- package/dist/cjs/src/utils/session.js.map +1 -0
- package/dist/cjs/src/utils/template.d.ts +107 -0
- package/dist/cjs/src/utils/template.d.ts.map +1 -0
- package/dist/cjs/src/utils/template.js +283 -0
- package/dist/cjs/src/utils/template.js.map +1 -0
- package/dist/{cjs → src}/adapters/MemoryAdapter.d.ts +4 -4
- package/dist/src/adapters/MemoryAdapter.d.ts.map +1 -0
- package/dist/{adapters → src/adapters}/MemoryAdapter.js +41 -21
- package/dist/src/adapters/MemoryAdapter.js.map +1 -0
- package/dist/{cjs → src}/adapters/MongoAdapter.d.ts +3 -3
- package/dist/src/adapters/MongoAdapter.d.ts.map +1 -0
- package/dist/{adapters → src/adapters}/MongoAdapter.js +2 -1
- package/dist/src/adapters/MongoAdapter.js.map +1 -0
- package/dist/{adapters → src/adapters}/OpenSearchAdapter.d.ts +3 -3
- package/dist/src/adapters/OpenSearchAdapter.d.ts.map +1 -0
- package/dist/{adapters → src/adapters}/OpenSearchAdapter.js +10 -13
- package/dist/src/adapters/OpenSearchAdapter.js.map +1 -0
- package/dist/{adapters → src/adapters}/PostgreSQLAdapter.d.ts +3 -3
- package/dist/src/adapters/PostgreSQLAdapter.d.ts.map +1 -0
- package/dist/{adapters → src/adapters}/PostgreSQLAdapter.js +1 -1
- package/dist/src/adapters/PostgreSQLAdapter.js.map +1 -0
- package/dist/{adapters → src/adapters}/PrismaAdapter.d.ts +3 -3
- package/dist/src/adapters/PrismaAdapter.d.ts.map +1 -0
- package/dist/{adapters → src/adapters}/PrismaAdapter.js +35 -5
- package/dist/src/adapters/PrismaAdapter.js.map +1 -0
- package/dist/{adapters → src/adapters}/RedisAdapter.d.ts +3 -3
- package/dist/src/adapters/RedisAdapter.d.ts.map +1 -0
- package/dist/{adapters → src/adapters}/RedisAdapter.js +3 -2
- package/dist/src/adapters/RedisAdapter.js.map +1 -0
- package/dist/{cjs → src}/adapters/SQLiteAdapter.d.ts +3 -3
- package/dist/src/adapters/SQLiteAdapter.d.ts.map +1 -0
- package/dist/{adapters → src/adapters}/SQLiteAdapter.js +2 -1
- package/dist/src/adapters/SQLiteAdapter.js.map +1 -0
- package/dist/src/adapters/index.js.map +1 -0
- package/dist/src/constants/index.js.map +1 -0
- package/dist/src/core/Agent.d.ts +223 -0
- package/dist/src/core/Agent.d.ts.map +1 -0
- package/dist/src/core/Agent.js +1656 -0
- package/dist/src/core/Agent.js.map +1 -0
- package/dist/src/core/Events.d.ts +26 -0
- package/dist/src/core/Events.d.ts.map +1 -0
- package/dist/src/core/Events.js +137 -0
- package/dist/src/core/Events.js.map +1 -0
- package/dist/{cjs → src}/core/PersistenceManager.d.ts +21 -19
- package/dist/src/core/PersistenceManager.d.ts.map +1 -0
- package/dist/{core → src/core}/PersistenceManager.js +71 -18
- package/dist/src/core/PersistenceManager.js.map +1 -0
- package/dist/src/core/PromptComposer.d.ts +27 -0
- package/dist/src/core/PromptComposer.d.ts.map +1 -0
- package/dist/src/core/PromptComposer.js +153 -0
- package/dist/src/core/PromptComposer.js.map +1 -0
- package/dist/src/core/ResponseEngine.d.ts +32 -0
- package/dist/src/core/ResponseEngine.d.ts.map +1 -0
- package/dist/src/core/ResponseEngine.js +80 -0
- package/dist/src/core/ResponseEngine.js.map +1 -0
- package/dist/src/core/ResponsePipeline.d.ts +171 -0
- package/dist/src/core/ResponsePipeline.d.ts.map +1 -0
- package/dist/src/core/ResponsePipeline.js +510 -0
- package/dist/src/core/ResponsePipeline.js.map +1 -0
- package/dist/src/core/Route.d.ts +145 -0
- package/dist/src/core/Route.d.ts.map +1 -0
- package/dist/src/core/Route.js +339 -0
- package/dist/src/core/Route.js.map +1 -0
- package/dist/src/core/RoutingEngine.d.ts +129 -0
- package/dist/src/core/RoutingEngine.d.ts.map +1 -0
- package/dist/{core → src/core}/RoutingEngine.js +211 -113
- package/dist/src/core/RoutingEngine.js.map +1 -0
- package/dist/src/core/SessionManager.d.ts +86 -0
- package/dist/src/core/SessionManager.d.ts.map +1 -0
- package/dist/src/core/SessionManager.js +213 -0
- package/dist/src/core/SessionManager.js.map +1 -0
- package/dist/src/core/Step.d.ts +96 -0
- package/dist/src/core/Step.d.ts.map +1 -0
- package/dist/src/core/Step.js +202 -0
- package/dist/src/core/Step.js.map +1 -0
- package/dist/src/core/ToolExecutor.d.ts +45 -0
- package/dist/src/core/ToolExecutor.d.ts.map +1 -0
- package/dist/src/core/ToolExecutor.js +80 -0
- package/dist/src/core/ToolExecutor.js.map +1 -0
- package/dist/{cjs → src}/index.d.ts +7 -15
- package/dist/src/index.d.ts.map +1 -0
- package/dist/{index.js → src/index.js} +6 -7
- package/dist/src/index.js.map +1 -0
- package/dist/{providers → src/providers}/AnthropicProvider.d.ts +1 -1
- package/dist/src/providers/AnthropicProvider.d.ts.map +1 -0
- package/dist/{providers → src/providers}/AnthropicProvider.js +54 -2
- package/dist/src/providers/AnthropicProvider.js.map +1 -0
- package/dist/{cjs → src}/providers/GeminiProvider.d.ts +1 -1
- package/dist/{cjs → src}/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/{providers → src/providers}/GeminiProvider.js +65 -0
- package/dist/src/providers/GeminiProvider.js.map +1 -0
- package/dist/{providers → src/providers}/OpenAIProvider.d.ts +1 -1
- package/dist/{cjs → src}/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/{providers → src/providers}/OpenAIProvider.js +70 -1
- package/dist/src/providers/OpenAIProvider.js.map +1 -0
- package/dist/{cjs → src}/providers/OpenRouterProvider.d.ts +1 -1
- package/dist/{cjs → src}/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/{providers → src/providers}/OpenRouterProvider.js +76 -0
- package/dist/src/providers/OpenRouterProvider.js.map +1 -0
- package/dist/src/providers/index.js.map +1 -0
- package/dist/{types → src/types}/agent.d.ts +80 -41
- package/dist/src/types/agent.d.ts.map +1 -0
- package/dist/src/types/agent.js.map +1 -0
- package/dist/{types → src/types}/ai.d.ts +7 -0
- package/dist/src/types/ai.d.ts.map +1 -0
- package/dist/{cjs → src}/types/ai.js.map +1 -1
- package/dist/{cjs → src}/types/history.d.ts +76 -18
- package/dist/src/types/history.d.ts.map +1 -0
- package/dist/src/types/history.js +30 -0
- package/dist/src/types/history.js.map +1 -0
- package/dist/src/types/index.d.ts +20 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +10 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/{cjs → src}/types/persistence.d.ts +38 -23
- package/dist/src/types/persistence.d.ts.map +1 -0
- package/dist/src/types/persistence.js.map +1 -0
- package/dist/src/types/route.d.ts +235 -0
- package/dist/src/types/route.d.ts.map +1 -0
- package/dist/{types → src/types}/route.js.map +1 -1
- package/dist/src/types/session.d.ts +65 -0
- package/dist/src/types/session.d.ts.map +1 -0
- package/dist/src/types/session.js +5 -0
- package/dist/src/types/session.js.map +1 -0
- package/dist/src/types/template.d.ts +30 -0
- package/dist/src/types/template.d.ts.map +1 -0
- package/dist/src/types/template.js +2 -0
- package/dist/src/types/template.js.map +1 -0
- package/dist/{cjs → src}/types/tool.d.ts +17 -13
- package/dist/src/types/tool.d.ts.map +1 -0
- package/dist/{types → src/types}/tool.js.map +1 -1
- package/dist/src/utils/clone.d.ts +8 -0
- package/dist/src/utils/clone.d.ts.map +1 -0
- package/dist/src/utils/clone.js +33 -0
- package/dist/src/utils/clone.js.map +1 -0
- package/dist/{cjs → src}/utils/event.d.ts +1 -1
- package/dist/{cjs → src}/utils/event.d.ts.map +1 -1
- package/dist/{utils → src/utils}/event.js +1 -1
- package/dist/src/utils/event.js.map +1 -0
- package/dist/src/utils/history.d.ts +31 -0
- package/dist/src/utils/history.d.ts.map +1 -0
- package/dist/src/utils/history.js +121 -0
- package/dist/src/utils/history.js.map +1 -0
- package/dist/src/utils/id.js.map +1 -0
- package/dist/src/utils/index.d.ts +13 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +19 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/retry.js.map +1 -0
- package/dist/src/utils/session.d.ts +51 -0
- package/dist/src/utils/session.d.ts.map +1 -0
- package/dist/{types → src/utils}/session.js +33 -32
- package/dist/src/utils/session.js.map +1 -0
- package/dist/src/utils/template.d.ts +107 -0
- package/dist/src/utils/template.d.ts.map +1 -0
- package/dist/src/utils/template.js +276 -0
- package/dist/src/utils/template.js.map +1 -0
- package/docs/README.md +174 -68
- package/docs/{API_REFERENCE.md → api/README.md} +925 -255
- package/docs/api/overview.md +952 -0
- package/docs/core/agent/README.md +787 -0
- package/docs/{CONTEXT_MANAGEMENT.md → core/agent/context-management.md} +175 -102
- package/docs/{ARCHITECTURE.md → core/agent/session-management.md} +117 -69
- package/docs/core/ai-integration/prompt-composition.md +220 -0
- package/docs/core/ai-integration/providers.md +515 -0
- package/docs/core/ai-integration/response-processing.md +176 -0
- package/docs/core/conversation-flows/data-collection.md +623 -0
- package/docs/core/conversation-flows/route-dsl.md +502 -0
- package/docs/core/conversation-flows/routes.md +117 -0
- package/docs/core/conversation-flows/step-transitions.md +595 -0
- package/docs/core/conversation-flows/steps.md +154 -0
- package/docs/{ADAPTERS.md → core/persistence/adapters.md} +1 -1
- package/docs/core/persistence/session-storage.md +644 -0
- package/docs/core/routing/intelligent-routing.md +348 -0
- package/docs/core/tools/tool-definition.md +346 -0
- package/docs/core/tools/tool-execution.md +815 -0
- package/docs/core/tools/tool-scoping.md +628 -0
- package/docs/guides/getting-started/README.md +406 -0
- package/examples/{company-qna-agent.ts → advanced-patterns/knowledge-based-agent.ts} +139 -95
- package/examples/{persistent-onboarding.ts → advanced-patterns/persistent-onboarding.ts} +244 -137
- package/examples/{rules-prohibitions.ts → advanced-patterns/route-lifecycle-hooks.ts} +130 -84
- package/examples/{streaming-agent.ts → advanced-patterns/streaming-responses.ts} +116 -90
- package/examples/ai-providers/anthropic-integration.ts +384 -0
- package/examples/{openai-agent.ts → ai-providers/openai-integration.ts} +57 -63
- package/examples/conversation-flows/completion-transitions.ts +277 -0
- package/examples/core-concepts/basic-agent.ts +443 -0
- package/examples/core-concepts/schema-driven-extraction.ts +305 -0
- package/examples/core-concepts/session-management.ts +406 -0
- package/examples/integrations/database-integration.ts +630 -0
- package/examples/integrations/healthcare-integration.ts +609 -0
- package/examples/{opensearch-persistence.ts → integrations/search-integration.ts} +199 -171
- package/examples/integrations/server-session-management.ts +307 -0
- package/examples/persistence/custom-adapter.ts +529 -0
- package/examples/{prisma-persistence.ts → persistence/database-persistence.ts} +215 -272
- package/examples/persistence/memory-sessions.ts +495 -0
- package/examples/{prisma-schema.example.prisma → persistence/prisma-schema.example.prisma} +1 -1
- package/examples/persistence/redis-persistence.ts +490 -0
- package/examples/tools/basic-tools.ts +561 -0
- package/examples/{extracted-data-modification.ts → tools/data-enrichment-tools.ts} +128 -117
- package/package.json +14 -10
- package/src/adapters/MemoryAdapter.ts +74 -46
- package/src/adapters/MongoAdapter.ts +33 -24
- package/src/adapters/OpenSearchAdapter.ts +41 -37
- package/src/adapters/PostgreSQLAdapter.ts +35 -24
- package/src/adapters/PrismaAdapter.ts +69 -27
- package/src/adapters/RedisAdapter.ts +38 -26
- package/src/adapters/SQLiteAdapter.ts +32 -22
- package/src/core/Agent.ts +1431 -526
- package/src/core/Events.ts +100 -112
- package/src/core/PersistenceManager.ts +103 -49
- package/src/core/PromptComposer.ts +158 -85
- package/src/core/ResponseEngine.ts +128 -46
- package/src/core/ResponsePipeline.ts +830 -0
- package/src/core/Route.ts +222 -53
- package/src/core/RoutingEngine.ts +345 -229
- package/src/core/SessionManager.ts +265 -0
- package/src/core/Step.ts +157 -67
- package/src/core/ToolExecutor.ts +52 -43
- package/src/index.ts +31 -37
- package/src/providers/AnthropicProvider.ts +71 -5
- package/src/providers/GeminiProvider.ts +83 -2
- package/src/providers/OpenAIProvider.ts +95 -3
- package/src/providers/OpenRouterProvider.ts +102 -2
- package/src/types/agent.ts +81 -46
- package/src/types/ai.ts +7 -0
- package/src/types/history.ts +91 -18
- package/src/types/index.ts +45 -7
- package/src/types/persistence.ts +45 -28
- package/src/types/route.ts +122 -57
- package/src/types/session.ts +20 -220
- package/src/types/template.ts +36 -0
- package/src/types/tool.ts +23 -19
- package/src/utils/clone.ts +36 -0
- package/src/utils/event.ts +1 -1
- package/src/utils/history.ts +143 -0
- package/src/utils/index.ts +53 -0
- package/src/utils/session.ts +204 -0
- package/src/utils/template.ts +335 -0
- package/dist/adapters/MemoryAdapter.d.ts.map +0 -1
- package/dist/adapters/MemoryAdapter.js.map +0 -1
- package/dist/adapters/MongoAdapter.d.ts.map +0 -1
- package/dist/adapters/MongoAdapter.js.map +0 -1
- package/dist/adapters/OpenSearchAdapter.d.ts.map +0 -1
- package/dist/adapters/OpenSearchAdapter.js.map +0 -1
- package/dist/adapters/PostgreSQLAdapter.d.ts.map +0 -1
- package/dist/adapters/PostgreSQLAdapter.js.map +0 -1
- package/dist/adapters/PrismaAdapter.d.ts.map +0 -1
- package/dist/adapters/PrismaAdapter.js.map +0 -1
- package/dist/adapters/RedisAdapter.d.ts.map +0 -1
- package/dist/adapters/RedisAdapter.js.map +0 -1
- package/dist/adapters/SQLiteAdapter.d.ts.map +0 -1
- package/dist/adapters/SQLiteAdapter.js.map +0 -1
- package/dist/adapters/index.d.ts.map +0 -1
- package/dist/adapters/index.js.map +0 -1
- package/dist/cjs/adapters/MemoryAdapter.d.ts.map +0 -1
- package/dist/cjs/adapters/MemoryAdapter.js.map +0 -1
- package/dist/cjs/adapters/MongoAdapter.d.ts.map +0 -1
- package/dist/cjs/adapters/MongoAdapter.js.map +0 -1
- package/dist/cjs/adapters/OpenSearchAdapter.d.ts.map +0 -1
- package/dist/cjs/adapters/OpenSearchAdapter.js.map +0 -1
- package/dist/cjs/adapters/PostgreSQLAdapter.d.ts.map +0 -1
- package/dist/cjs/adapters/PostgreSQLAdapter.js.map +0 -1
- package/dist/cjs/adapters/PrismaAdapter.d.ts.map +0 -1
- package/dist/cjs/adapters/PrismaAdapter.js.map +0 -1
- package/dist/cjs/adapters/RedisAdapter.d.ts.map +0 -1
- package/dist/cjs/adapters/RedisAdapter.js.map +0 -1
- package/dist/cjs/adapters/SQLiteAdapter.d.ts.map +0 -1
- package/dist/cjs/adapters/SQLiteAdapter.js.map +0 -1
- package/dist/cjs/adapters/index.js.map +0 -1
- package/dist/cjs/constants/index.js.map +0 -1
- package/dist/cjs/core/Agent.d.ts +0 -197
- package/dist/cjs/core/Agent.d.ts.map +0 -1
- package/dist/cjs/core/Agent.js +0 -966
- package/dist/cjs/core/Agent.js.map +0 -1
- package/dist/cjs/core/DomainRegistry.d.ts +0 -36
- package/dist/cjs/core/DomainRegistry.d.ts.map +0 -1
- package/dist/cjs/core/DomainRegistry.js +0 -72
- package/dist/cjs/core/DomainRegistry.js.map +0 -1
- package/dist/cjs/core/Events.d.ts +0 -41
- package/dist/cjs/core/Events.d.ts.map +0 -1
- package/dist/cjs/core/Events.js +0 -99
- package/dist/cjs/core/Events.js.map +0 -1
- package/dist/cjs/core/PersistenceManager.d.ts.map +0 -1
- package/dist/cjs/core/PersistenceManager.js.map +0 -1
- package/dist/cjs/core/PromptComposer.d.ts +0 -24
- package/dist/cjs/core/PromptComposer.d.ts.map +0 -1
- package/dist/cjs/core/PromptComposer.js +0 -127
- package/dist/cjs/core/PromptComposer.js.map +0 -1
- package/dist/cjs/core/ResponseEngine.d.ts +0 -14
- package/dist/cjs/core/ResponseEngine.d.ts.map +0 -1
- package/dist/cjs/core/ResponseEngine.js +0 -56
- package/dist/cjs/core/ResponseEngine.js.map +0 -1
- package/dist/cjs/core/Route.d.ts +0 -90
- package/dist/cjs/core/Route.d.ts.map +0 -1
- package/dist/cjs/core/Route.js +0 -203
- package/dist/cjs/core/Route.js.map +0 -1
- package/dist/cjs/core/RoutingEngine.d.ts +0 -109
- package/dist/cjs/core/RoutingEngine.d.ts.map +0 -1
- package/dist/cjs/core/RoutingEngine.js.map +0 -1
- package/dist/cjs/core/Step.d.ts +0 -72
- package/dist/cjs/core/Step.d.ts.map +0 -1
- package/dist/cjs/core/Step.js +0 -150
- package/dist/cjs/core/Step.js.map +0 -1
- package/dist/cjs/core/Tool.d.ts +0 -39
- package/dist/cjs/core/Tool.d.ts.map +0 -1
- package/dist/cjs/core/Tool.js +0 -34
- package/dist/cjs/core/Tool.js.map +0 -1
- package/dist/cjs/core/ToolExecutor.d.ts +0 -29
- package/dist/cjs/core/ToolExecutor.d.ts.map +0 -1
- package/dist/cjs/core/ToolExecutor.js.map +0 -1
- package/dist/cjs/core/Transition.d.ts +0 -32
- package/dist/cjs/core/Transition.d.ts.map +0 -1
- package/dist/cjs/core/Transition.js +0 -89
- package/dist/cjs/core/Transition.js.map +0 -1
- package/dist/cjs/index.d.ts.map +0 -1
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/providers/AnthropicProvider.d.ts.map +0 -1
- package/dist/cjs/providers/AnthropicProvider.js.map +0 -1
- package/dist/cjs/providers/GeminiProvider.js.map +0 -1
- package/dist/cjs/providers/OpenAIProvider.js.map +0 -1
- package/dist/cjs/providers/OpenRouterProvider.js.map +0 -1
- package/dist/cjs/providers/index.js.map +0 -1
- package/dist/cjs/types/agent.d.ts.map +0 -1
- package/dist/cjs/types/agent.js.map +0 -1
- package/dist/cjs/types/ai.d.ts.map +0 -1
- package/dist/cjs/types/history.d.ts.map +0 -1
- package/dist/cjs/types/history.js +0 -37
- package/dist/cjs/types/history.js.map +0 -1
- package/dist/cjs/types/index.d.ts +0 -12
- package/dist/cjs/types/index.d.ts.map +0 -1
- package/dist/cjs/types/index.js +0 -12
- package/dist/cjs/types/index.js.map +0 -1
- package/dist/cjs/types/persistence.d.ts.map +0 -1
- package/dist/cjs/types/persistence.js.map +0 -1
- package/dist/cjs/types/route.d.ts +0 -175
- package/dist/cjs/types/route.d.ts.map +0 -1
- package/dist/cjs/types/session.d.ts +0 -104
- package/dist/cjs/types/session.d.ts.map +0 -1
- package/dist/cjs/types/session.js.map +0 -1
- package/dist/cjs/types/tool.d.ts.map +0 -1
- package/dist/cjs/utils/event.js.map +0 -1
- package/dist/cjs/utils/id.js.map +0 -1
- package/dist/cjs/utils/logger.js.map +0 -1
- package/dist/cjs/utils/retry.js.map +0 -1
- package/dist/constants/index.d.ts.map +0 -1
- package/dist/constants/index.js.map +0 -1
- package/dist/core/Agent.d.ts +0 -197
- package/dist/core/Agent.d.ts.map +0 -1
- package/dist/core/Agent.js +0 -962
- package/dist/core/Agent.js.map +0 -1
- package/dist/core/DomainRegistry.d.ts +0 -36
- package/dist/core/DomainRegistry.d.ts.map +0 -1
- package/dist/core/DomainRegistry.js +0 -68
- package/dist/core/DomainRegistry.js.map +0 -1
- package/dist/core/Events.d.ts +0 -41
- package/dist/core/Events.d.ts.map +0 -1
- package/dist/core/Events.js +0 -94
- package/dist/core/Events.js.map +0 -1
- package/dist/core/PersistenceManager.d.ts.map +0 -1
- package/dist/core/PersistenceManager.js.map +0 -1
- package/dist/core/PromptComposer.d.ts +0 -24
- package/dist/core/PromptComposer.d.ts.map +0 -1
- package/dist/core/PromptComposer.js +0 -123
- package/dist/core/PromptComposer.js.map +0 -1
- package/dist/core/ResponseEngine.d.ts +0 -14
- package/dist/core/ResponseEngine.d.ts.map +0 -1
- package/dist/core/ResponseEngine.js +0 -52
- package/dist/core/ResponseEngine.js.map +0 -1
- package/dist/core/Route.d.ts +0 -90
- package/dist/core/Route.d.ts.map +0 -1
- package/dist/core/Route.js +0 -199
- package/dist/core/Route.js.map +0 -1
- package/dist/core/RoutingEngine.d.ts +0 -109
- package/dist/core/RoutingEngine.d.ts.map +0 -1
- package/dist/core/RoutingEngine.js.map +0 -1
- package/dist/core/Step.d.ts +0 -72
- package/dist/core/Step.d.ts.map +0 -1
- package/dist/core/Step.js +0 -146
- package/dist/core/Step.js.map +0 -1
- package/dist/core/Tool.d.ts +0 -39
- package/dist/core/Tool.d.ts.map +0 -1
- package/dist/core/Tool.js +0 -31
- package/dist/core/Tool.js.map +0 -1
- package/dist/core/ToolExecutor.d.ts +0 -29
- package/dist/core/ToolExecutor.d.ts.map +0 -1
- package/dist/core/ToolExecutor.js +0 -69
- package/dist/core/ToolExecutor.js.map +0 -1
- package/dist/core/Transition.d.ts +0 -32
- package/dist/core/Transition.d.ts.map +0 -1
- package/dist/core/Transition.js +0 -85
- package/dist/core/Transition.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/providers/AnthropicProvider.d.ts.map +0 -1
- package/dist/providers/AnthropicProvider.js.map +0 -1
- package/dist/providers/GeminiProvider.d.ts.map +0 -1
- package/dist/providers/GeminiProvider.js.map +0 -1
- package/dist/providers/OpenAIProvider.d.ts.map +0 -1
- package/dist/providers/OpenAIProvider.js.map +0 -1
- package/dist/providers/OpenRouterProvider.d.ts.map +0 -1
- package/dist/providers/OpenRouterProvider.js.map +0 -1
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/index.js.map +0 -1
- package/dist/types/agent.d.ts.map +0 -1
- package/dist/types/agent.js.map +0 -1
- package/dist/types/ai.d.ts.map +0 -1
- package/dist/types/ai.js.map +0 -1
- package/dist/types/history.d.ts.map +0 -1
- package/dist/types/history.js +0 -34
- package/dist/types/history.js.map +0 -1
- package/dist/types/index.d.ts +0 -12
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -6
- package/dist/types/index.js.map +0 -1
- package/dist/types/persistence.d.ts.map +0 -1
- package/dist/types/persistence.js.map +0 -1
- package/dist/types/route.d.ts +0 -175
- package/dist/types/route.d.ts.map +0 -1
- package/dist/types/routing.d.ts.map +0 -1
- package/dist/types/schema.d.ts.map +0 -1
- package/dist/types/session.d.ts +0 -104
- package/dist/types/session.d.ts.map +0 -1
- package/dist/types/session.js.map +0 -1
- package/dist/types/tool.d.ts.map +0 -1
- package/dist/utils/event.d.ts.map +0 -1
- package/dist/utils/event.js.map +0 -1
- package/dist/utils/id.d.ts.map +0 -1
- package/dist/utils/id.js.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/retry.d.ts.map +0 -1
- package/dist/utils/retry.js.map +0 -1
- package/docs/AGENT.md +0 -535
- package/docs/DOCS.md +0 -263
- package/docs/DOMAINS.md +0 -735
- package/docs/EXAMPLES.md +0 -467
- package/docs/GETTING_STARTED.md +0 -424
- package/docs/PERSISTENCE.md +0 -815
- package/docs/PROVIDERS.md +0 -612
- package/docs/ROUTES.md +0 -1085
- package/docs/STEPS.md +0 -883
- package/examples/business-onboarding.ts +0 -791
- package/examples/custom-database-persistence.ts +0 -574
- package/examples/declarative-agent.ts +0 -401
- package/examples/domain-scoping.ts +0 -366
- package/examples/healthcare-agent.ts +0 -511
- package/examples/redis-persistence.ts +0 -525
- package/examples/route-transitions.ts +0 -266
- package/examples/travel-agent.ts +0 -584
- package/src/core/DomainRegistry.ts +0 -80
- package/src/core/Tool.ts +0 -112
- package/src/core/Transition.ts +0 -115
- /package/dist/{adapters → cjs/src/adapters}/index.d.ts +0 -0
- /package/dist/cjs/{adapters → src/adapters}/index.js +0 -0
- /package/dist/cjs/{constants → src/constants}/index.d.ts +0 -0
- /package/dist/cjs/{constants → src/constants}/index.js +0 -0
- /package/dist/cjs/{providers → src/providers}/index.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/index.js +0 -0
- /package/dist/cjs/{types → src/types}/agent.js +0 -0
- /package/dist/cjs/{types → src/types}/ai.js +0 -0
- /package/dist/cjs/{types → src/types}/persistence.js +0 -0
- /package/dist/cjs/{types → src/types}/route.js +0 -0
- /package/dist/cjs/{types → src/types}/routing.d.ts +0 -0
- /package/dist/cjs/{types → src/types}/routing.js +0 -0
- /package/dist/cjs/{types → src/types}/schema.d.ts +0 -0
- /package/dist/cjs/{types → src/types}/schema.js +0 -0
- /package/dist/cjs/{types → src/types}/tool.js +0 -0
- /package/dist/cjs/{utils → src/utils}/id.d.ts +0 -0
- /package/dist/cjs/{utils → src/utils}/id.js +0 -0
- /package/dist/cjs/{utils → src/utils}/logger.d.ts +0 -0
- /package/dist/cjs/{utils → src/utils}/logger.js +0 -0
- /package/dist/cjs/{utils → src/utils}/retry.d.ts +0 -0
- /package/dist/cjs/{utils → src/utils}/retry.js +0 -0
- /package/dist/{cjs → src}/adapters/index.d.ts +0 -0
- /package/dist/{cjs → src}/adapters/index.d.ts.map +0 -0
- /package/dist/{adapters → src/adapters}/index.js +0 -0
- /package/dist/{constants → src/constants}/index.d.ts +0 -0
- /package/dist/{cjs → src}/constants/index.d.ts.map +0 -0
- /package/dist/{constants → src/constants}/index.js +0 -0
- /package/dist/{providers → src/providers}/index.d.ts +0 -0
- /package/dist/{cjs → src}/providers/index.d.ts.map +0 -0
- /package/dist/{providers → src/providers}/index.js +0 -0
- /package/dist/{types → src/types}/agent.js +0 -0
- /package/dist/{types → src/types}/ai.js +0 -0
- /package/dist/{types → src/types}/persistence.js +0 -0
- /package/dist/{types → src/types}/route.js +0 -0
- /package/dist/{types → src/types}/routing.d.ts +0 -0
- /package/dist/{cjs → src}/types/routing.d.ts.map +0 -0
- /package/dist/{types → src/types}/routing.js +0 -0
- /package/dist/{cjs → src}/types/routing.js.map +0 -0
- /package/dist/{types → src/types}/schema.d.ts +0 -0
- /package/dist/{cjs → src}/types/schema.d.ts.map +0 -0
- /package/dist/{types → src/types}/schema.js +0 -0
- /package/dist/{cjs → src}/types/schema.js.map +0 -0
- /package/dist/{types → src/types}/tool.js +0 -0
- /package/dist/{utils → src/utils}/id.d.ts +0 -0
- /package/dist/{cjs → src}/utils/id.d.ts.map +0 -0
- /package/dist/{utils → src/utils}/id.js +0 -0
- /package/dist/{utils → src/utils}/logger.d.ts +0 -0
- /package/dist/{cjs → src}/utils/logger.d.ts.map +0 -0
- /package/dist/{utils → src/utils}/logger.js +0 -0
- /package/dist/{utils → src/utils}/retry.d.ts +0 -0
- /package/dist/{cjs → src}/utils/retry.d.ts.map +0 -0
- /package/dist/{utils → src/utils}/retry.js +0 -0
- /package/docs/{PUBLISHING.md → guides/advanced-patterns/publishing.md} +0 -0
package/src/core/Agent.ts
CHANGED
|
@@ -2,54 +2,90 @@
|
|
|
2
2
|
* Core Agent implementation
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
import type {
|
|
6
|
+
AgentOptions,
|
|
7
|
+
Term,
|
|
8
|
+
Guideline,
|
|
9
|
+
Tool,
|
|
10
|
+
Event,
|
|
11
|
+
RouteOptions,
|
|
12
|
+
SessionState,
|
|
13
|
+
AgentStructuredResponse,
|
|
14
|
+
Template,
|
|
15
|
+
StepRef,
|
|
16
|
+
History,
|
|
17
|
+
AgentResponseStreamChunk,
|
|
18
|
+
AgentResponse,
|
|
19
|
+
StructuredSchema,
|
|
20
|
+
ValidationError,
|
|
21
|
+
ValidationResult,
|
|
22
|
+
} from "../types";
|
|
23
|
+
import { EventKind, MessageRole } from "../types/history";
|
|
11
24
|
import {
|
|
12
|
-
createSession,
|
|
13
25
|
enterRoute,
|
|
14
26
|
enterStep,
|
|
15
27
|
mergeCollected,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
28
|
+
logger,
|
|
29
|
+
LoggerLevel,
|
|
30
|
+
render,
|
|
31
|
+
getLastMessageFromHistory,
|
|
32
|
+
normalizeHistory,
|
|
33
|
+
cloneDeep,
|
|
34
|
+
} from "../utils";
|
|
19
35
|
|
|
20
36
|
import { Route } from "./Route";
|
|
21
37
|
import { Step } from "./Step";
|
|
22
|
-
import { DomainRegistry } from "./DomainRegistry";
|
|
23
38
|
import { PersistenceManager } from "./PersistenceManager";
|
|
39
|
+
import { SessionManager } from "./SessionManager";
|
|
24
40
|
import { RoutingEngine } from "./RoutingEngine";
|
|
25
41
|
import { ResponseEngine } from "./ResponseEngine";
|
|
26
42
|
import { ToolExecutor } from "./ToolExecutor";
|
|
27
|
-
import {
|
|
43
|
+
import { ResponsePipeline } from "./ResponsePipeline";
|
|
28
44
|
import { END_ROUTE_ID } from "../constants";
|
|
29
|
-
import { ToolRef } from "../types";
|
|
30
45
|
|
|
31
46
|
/**
|
|
32
|
-
*
|
|
47
|
+
* Error thrown when data validation fails
|
|
33
48
|
*/
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
private domainRegistry = new DomainRegistry();
|
|
41
|
-
private context: TContext | undefined;
|
|
42
|
-
private persistenceManager: PersistenceManager | undefined;
|
|
43
|
-
private routingEngine: RoutingEngine<TContext>;
|
|
44
|
-
private responseEngine: ResponseEngine<TContext>;
|
|
45
|
-
private currentSession?: SessionState;
|
|
49
|
+
class DataValidationError extends Error {
|
|
50
|
+
constructor(public errors: ValidationError[], message?: string) {
|
|
51
|
+
super(message || "Data validation failed");
|
|
52
|
+
this.name = "DataValidationError";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
46
55
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Error thrown when route configuration is invalid
|
|
58
|
+
*/
|
|
59
|
+
class RouteConfigurationError extends Error {
|
|
60
|
+
constructor(public routeTitle: string, public invalidFields: string[], message?: string) {
|
|
61
|
+
super(message || `Route configuration error in '${routeTitle}'`);
|
|
62
|
+
this.name = "RouteConfigurationError";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
51
65
|
|
|
52
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Main Agent class with generic context and data support
|
|
68
|
+
*/
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
|
+
export class Agent<TContext = any, TData = any> {
|
|
71
|
+
private terms: Term<TContext, TData>[] = [];
|
|
72
|
+
private guidelines: Guideline<TContext, TData>[] = [];
|
|
73
|
+
private tools: Tool<TContext, TData, unknown[], unknown>[] = [];
|
|
74
|
+
private routes: Route<TContext, TData>[] = [];
|
|
75
|
+
private context: TContext | undefined;
|
|
76
|
+
private persistenceManager: PersistenceManager<TData> | undefined;
|
|
77
|
+
private routingEngine: RoutingEngine<TContext, TData>;
|
|
78
|
+
private responseEngine: ResponseEngine<TContext, TData>;
|
|
79
|
+
private responsePipeline: ResponsePipeline<TContext, TData>;
|
|
80
|
+
private currentSession?: SessionState<TData>;
|
|
81
|
+
private knowledgeBase: Record<string, unknown> = {};
|
|
82
|
+
private schema?: StructuredSchema;
|
|
83
|
+
private collectedData: Partial<TData> = {};
|
|
84
|
+
|
|
85
|
+
/** Public session manager for easy session management */
|
|
86
|
+
public session: SessionManager<TData>;
|
|
87
|
+
|
|
88
|
+
constructor(private readonly options: AgentOptions<TContext, TData>) {
|
|
53
89
|
// Set log level based on debug option
|
|
54
90
|
if (options.debug) {
|
|
55
91
|
logger.setLevel(LoggerLevel.DEBUG);
|
|
@@ -62,38 +98,89 @@ export class Agent<TContext = unknown> {
|
|
|
62
98
|
);
|
|
63
99
|
}
|
|
64
100
|
|
|
101
|
+
// Initialize and validate agent-level schema if provided
|
|
102
|
+
if (options.schema) {
|
|
103
|
+
this.schema = options.schema;
|
|
104
|
+
this.validateSchema(this.schema);
|
|
105
|
+
logger.debug("[Agent] Agent-level schema initialized and validated");
|
|
106
|
+
}
|
|
107
|
+
|
|
65
108
|
// Initialize context if provided
|
|
66
109
|
this.context = options.context;
|
|
67
110
|
|
|
111
|
+
// Initialize collected data with initial data if provided
|
|
112
|
+
if (options.initialData) {
|
|
113
|
+
if (this.schema) {
|
|
114
|
+
const validation = this.validateData(options.initialData);
|
|
115
|
+
if (!validation.valid) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Initial data validation failed: ${validation.errors.map(e => e.message).join(', ')}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
this.collectedData = { ...options.initialData };
|
|
122
|
+
logger.debug("[Agent] Initial data set:", this.collectedData);
|
|
123
|
+
}
|
|
124
|
+
|
|
68
125
|
// Initialize current session if provided
|
|
69
126
|
this.currentSession = options.session;
|
|
70
127
|
|
|
71
128
|
// Initialize routing and response engines
|
|
72
|
-
this.routingEngine = new RoutingEngine<TContext>({
|
|
129
|
+
this.routingEngine = new RoutingEngine<TContext, TData>({
|
|
73
130
|
maxCandidates: 5,
|
|
74
131
|
allowRouteSwitch: true,
|
|
75
132
|
switchThreshold: 70,
|
|
76
133
|
});
|
|
77
|
-
this.responseEngine = new ResponseEngine<TContext>();
|
|
134
|
+
this.responseEngine = new ResponseEngine<TContext, TData>();
|
|
135
|
+
this.responsePipeline = new ResponsePipeline<TContext, TData>(
|
|
136
|
+
options,
|
|
137
|
+
this.routes,
|
|
138
|
+
this.tools,
|
|
139
|
+
this.routingEngine,
|
|
140
|
+
this.updateContext.bind(this),
|
|
141
|
+
this.updateData.bind(this),
|
|
142
|
+
this.updateCollectedData.bind(this)
|
|
143
|
+
);
|
|
78
144
|
|
|
79
145
|
// Initialize persistence if configured
|
|
80
146
|
if (options.persistence) {
|
|
81
|
-
|
|
147
|
+
try {
|
|
148
|
+
// Validate persistence configuration
|
|
149
|
+
if (!options.persistence.adapter) {
|
|
150
|
+
throw new Error("Persistence adapter is required when persistence is configured");
|
|
151
|
+
}
|
|
82
152
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
153
|
+
if (!options.persistence.adapter.sessionRepository) {
|
|
154
|
+
throw new Error("Persistence adapter must provide a sessionRepository");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (!options.persistence.adapter.messageRepository) {
|
|
158
|
+
throw new Error("Persistence adapter must provide a messageRepository");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
this.persistenceManager = new PersistenceManager<TData>(options.persistence);
|
|
162
|
+
|
|
163
|
+
// Initialize the adapter if it has an initialize method
|
|
164
|
+
if (options.persistence.adapter.initialize) {
|
|
165
|
+
options.persistence.adapter.initialize().catch((error) => {
|
|
166
|
+
logger.error(
|
|
167
|
+
"[Agent] Persistence adapter initialization failed:",
|
|
168
|
+
error instanceof Error ? error.message : String(error)
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
} catch (error) {
|
|
173
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
174
|
+
logger.error("[Agent] Failed to initialize persistence:", errorMessage);
|
|
175
|
+
throw new Error(`Failed to initialize persistence: ${errorMessage}`);
|
|
91
176
|
}
|
|
92
177
|
}
|
|
93
178
|
|
|
94
|
-
// Initialize from options
|
|
179
|
+
// Initialize from options - use create methods for consistency
|
|
95
180
|
if (options.terms) {
|
|
96
|
-
|
|
181
|
+
options.terms.forEach((term) => {
|
|
182
|
+
this.createTerm(term);
|
|
183
|
+
});
|
|
97
184
|
}
|
|
98
185
|
|
|
99
186
|
if (options.guidelines) {
|
|
@@ -102,19 +189,172 @@ export class Agent<TContext = unknown> {
|
|
|
102
189
|
});
|
|
103
190
|
}
|
|
104
191
|
|
|
105
|
-
if (options.
|
|
106
|
-
options.
|
|
107
|
-
this.
|
|
192
|
+
if (options.tools) {
|
|
193
|
+
options.tools.forEach((tool) => {
|
|
194
|
+
this.createTool(tool);
|
|
108
195
|
});
|
|
109
196
|
}
|
|
110
197
|
|
|
111
198
|
if (options.routes) {
|
|
112
199
|
options.routes.forEach((routeOptions) => {
|
|
113
|
-
this.createRoute
|
|
200
|
+
this.createRoute(routeOptions);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Initialize knowledge base
|
|
205
|
+
if (options.knowledgeBase) {
|
|
206
|
+
this.knowledgeBase = { ...options.knowledgeBase };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Initialize session manager
|
|
210
|
+
this.session = new SessionManager<TData>(this.persistenceManager);
|
|
211
|
+
|
|
212
|
+
// Store sessionId for later use in getOrCreate calls
|
|
213
|
+
if (options.sessionId) {
|
|
214
|
+
// The session will be loaded on first getOrCreate call
|
|
215
|
+
this.session.getOrCreate(options.sessionId).catch((err) => {
|
|
216
|
+
logger.error("Failed to start session", err);
|
|
114
217
|
});
|
|
115
218
|
}
|
|
116
219
|
}
|
|
117
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Validate the agent-level schema structure
|
|
223
|
+
* @private
|
|
224
|
+
*/
|
|
225
|
+
private validateSchema(schema: StructuredSchema): void {
|
|
226
|
+
if (!schema || typeof schema !== 'object') {
|
|
227
|
+
throw new Error(
|
|
228
|
+
"Agent schema must be a valid JSON Schema object. " +
|
|
229
|
+
"Provide a schema with 'type': 'object' and 'properties' to define the data structure."
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (schema.type !== 'object') {
|
|
234
|
+
throw new Error(
|
|
235
|
+
`Agent schema must be of type 'object', but received '${String(schema.type)}'. ` +
|
|
236
|
+
"Agent-level schemas must define object structures for data collection."
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (!schema.properties || typeof schema.properties !== 'object') {
|
|
241
|
+
throw new Error(
|
|
242
|
+
"Agent schema must have a 'properties' field defining the data fields. " +
|
|
243
|
+
"Example: { type: 'object', properties: { name: { type: 'string' }, email: { type: 'string' } } }"
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
logger.debug("[Agent] Schema validation passed");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Validate data against the agent-level schema
|
|
252
|
+
*/
|
|
253
|
+
validateData(data: Partial<TData>): ValidationResult {
|
|
254
|
+
if (!this.schema) {
|
|
255
|
+
// No schema defined, consider all data valid
|
|
256
|
+
return { valid: true, errors: [], warnings: [] };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const errors: ValidationError[] = [];
|
|
260
|
+
const warnings: ValidationError[] = [];
|
|
261
|
+
|
|
262
|
+
// Basic validation - check if provided fields exist in schema
|
|
263
|
+
if (this.schema.properties) {
|
|
264
|
+
for (const [key, value] of Object.entries(data)) {
|
|
265
|
+
if (!(key in this.schema.properties)) {
|
|
266
|
+
errors.push({
|
|
267
|
+
field: key,
|
|
268
|
+
value,
|
|
269
|
+
message: `Field '${key}' is not defined in agent schema`,
|
|
270
|
+
schemaPath: `properties.${key}`
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Check required fields if specified
|
|
277
|
+
if (this.schema.required && Array.isArray(this.schema.required)) {
|
|
278
|
+
for (const requiredField of this.schema.required) {
|
|
279
|
+
if (!(requiredField in data) || data[requiredField as keyof TData] === undefined) {
|
|
280
|
+
warnings.push({
|
|
281
|
+
field: requiredField,
|
|
282
|
+
value: undefined,
|
|
283
|
+
message: `Required field '${requiredField}' is missing`,
|
|
284
|
+
schemaPath: `required`
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
valid: errors.length === 0,
|
|
292
|
+
errors,
|
|
293
|
+
warnings
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Check if a field is valid according to the agent schema
|
|
299
|
+
* @param field - The field key to validate
|
|
300
|
+
* @returns true if field exists in schema or no schema is defined, false otherwise
|
|
301
|
+
*/
|
|
302
|
+
isValidSchemaField(field: keyof TData): boolean {
|
|
303
|
+
if (!this.schema || !this.schema.properties) {
|
|
304
|
+
// No schema defined, consider all fields valid
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return field as string in this.schema.properties;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Get the current collected data
|
|
313
|
+
*/
|
|
314
|
+
getCollectedData(): Partial<TData> {
|
|
315
|
+
return { ...this.collectedData };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Update collected data with validation
|
|
320
|
+
*/
|
|
321
|
+
async updateCollectedData(updates: Partial<TData>): Promise<void> {
|
|
322
|
+
// Validate the updates
|
|
323
|
+
const validation = this.validateData(updates);
|
|
324
|
+
if (!validation.valid) {
|
|
325
|
+
const errorMessages = validation.errors.map(e => e.message).join(', ');
|
|
326
|
+
throw new DataValidationError(validation.errors, `Data validation failed: ${errorMessages}`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Log warnings if any
|
|
330
|
+
if (validation.warnings.length > 0) {
|
|
331
|
+
const warningMessages = validation.warnings.map(w => w.message).join(', ');
|
|
332
|
+
logger.warn(`[Agent] Data validation warnings: ${warningMessages}`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Merge updates with current data
|
|
336
|
+
const previousData = { ...this.collectedData };
|
|
337
|
+
this.collectedData = {
|
|
338
|
+
...this.collectedData,
|
|
339
|
+
...updates
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// Trigger agent-level lifecycle hook if configured
|
|
343
|
+
if (this.options.hooks?.onDataUpdate) {
|
|
344
|
+
this.collectedData = await this.options.hooks.onDataUpdate(
|
|
345
|
+
this.collectedData,
|
|
346
|
+
previousData
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Update current session if it exists to keep it in sync
|
|
351
|
+
if (this.currentSession) {
|
|
352
|
+
this.currentSession = mergeCollected(this.currentSession, this.collectedData);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
logger.debug("[Agent] Collected data updated:", updates);
|
|
356
|
+
}
|
|
357
|
+
|
|
118
358
|
/**
|
|
119
359
|
* Get agent name
|
|
120
360
|
*/
|
|
@@ -137,12 +377,48 @@ export class Agent<TContext = unknown> {
|
|
|
137
377
|
}
|
|
138
378
|
|
|
139
379
|
/**
|
|
140
|
-
*
|
|
141
|
-
* @template TData - Type of data collected throughout the route
|
|
380
|
+
* Get agent identity
|
|
142
381
|
*/
|
|
143
|
-
|
|
382
|
+
get identity(): Template<TContext> | undefined {
|
|
383
|
+
return this.options.identity;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Create a new route (journey) using agent-level data type
|
|
388
|
+
*/
|
|
389
|
+
createRoute(
|
|
144
390
|
options: RouteOptions<TContext, TData>
|
|
145
391
|
): Route<TContext, TData> {
|
|
392
|
+
// Validate that requiredFields exist in agent schema
|
|
393
|
+
if (options.requiredFields && this.schema?.properties) {
|
|
394
|
+
const invalidRequiredFields = options.requiredFields.filter(
|
|
395
|
+
field => !(String(field) in this.schema!.properties!)
|
|
396
|
+
);
|
|
397
|
+
if (invalidRequiredFields.length > 0) {
|
|
398
|
+
throw new RouteConfigurationError(
|
|
399
|
+
options.title,
|
|
400
|
+
invalidRequiredFields.map(f => String(f)),
|
|
401
|
+
`Invalid required fields in route '${options.title}': ${invalidRequiredFields.join(', ')}. ` +
|
|
402
|
+
`Must be valid keys from agent schema. Available fields: ${Object.keys(this.schema.properties).join(', ')}.`
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Validate that optionalFields exist in agent schema
|
|
408
|
+
if (options.optionalFields && this.schema?.properties) {
|
|
409
|
+
const invalidOptionalFields = options.optionalFields.filter(
|
|
410
|
+
field => !(String(field) in this.schema!.properties!)
|
|
411
|
+
);
|
|
412
|
+
if (invalidOptionalFields.length > 0) {
|
|
413
|
+
throw new RouteConfigurationError(
|
|
414
|
+
options.title,
|
|
415
|
+
invalidOptionalFields.map(f => String(f)),
|
|
416
|
+
`Invalid optional fields in route '${options.title}': ${invalidOptionalFields.join(', ')}. ` +
|
|
417
|
+
`Must be valid keys from agent schema. Available fields: ${Object.keys(this.schema.properties).join(', ')}.`
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
146
422
|
const route = new Route<TContext, TData>(options);
|
|
147
423
|
this.routes.push(route);
|
|
148
424
|
return route;
|
|
@@ -151,7 +427,7 @@ export class Agent<TContext = unknown> {
|
|
|
151
427
|
/**
|
|
152
428
|
* Create a domain term for the glossary
|
|
153
429
|
*/
|
|
154
|
-
createTerm(term: Term): this {
|
|
430
|
+
createTerm(term: Term<TContext, TData>): this {
|
|
155
431
|
this.terms.push(term);
|
|
156
432
|
return this;
|
|
157
433
|
}
|
|
@@ -159,7 +435,7 @@ export class Agent<TContext = unknown> {
|
|
|
159
435
|
/**
|
|
160
436
|
* Create a behavioral guideline
|
|
161
437
|
*/
|
|
162
|
-
createGuideline(guideline: Guideline): this {
|
|
438
|
+
createGuideline(guideline: Guideline<TContext, TData>): this {
|
|
163
439
|
const guidelineWithId = {
|
|
164
440
|
...guideline,
|
|
165
441
|
id: guideline.id || `guideline_${this.guidelines.length}`,
|
|
@@ -170,50 +446,24 @@ export class Agent<TContext = unknown> {
|
|
|
170
446
|
}
|
|
171
447
|
|
|
172
448
|
/**
|
|
173
|
-
*
|
|
449
|
+
* Register a tool at the agent level
|
|
174
450
|
*/
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
...capability,
|
|
178
|
-
id: capability.id || `capability_${this.capabilities.length}`,
|
|
179
|
-
};
|
|
180
|
-
this.capabilities.push(capabilityWithId);
|
|
451
|
+
createTool(tool: Tool<TContext, TData, unknown[], unknown>): this {
|
|
452
|
+
this.tools.push(tool);
|
|
181
453
|
return this;
|
|
182
454
|
}
|
|
183
455
|
|
|
184
456
|
/**
|
|
185
|
-
*
|
|
186
|
-
* Automatically tags all ToolRef objects with their domain name for security enforcement
|
|
457
|
+
* Register multiple tools at the agent level
|
|
187
458
|
*/
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
): void {
|
|
192
|
-
// Tag all tools in this domain with the domain name for security enforcement
|
|
193
|
-
const taggedDomain = { ...domainObject };
|
|
194
|
-
for (const key in taggedDomain) {
|
|
195
|
-
const value = taggedDomain[key];
|
|
196
|
-
// Check if value is a ToolRef (has handler, id, name properties)
|
|
197
|
-
if (
|
|
198
|
-
value &&
|
|
199
|
-
typeof value === "object" &&
|
|
200
|
-
"handler" in value &&
|
|
201
|
-
"id" in value &&
|
|
202
|
-
"name" in value
|
|
203
|
-
) {
|
|
204
|
-
// Tag the tool with its domain name
|
|
205
|
-
(value as Record<string, unknown>).domainName = name;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
this.domainRegistry.register(name, taggedDomain);
|
|
210
|
-
// Attach to the domain property for easy access
|
|
211
|
-
this.domain[name] = taggedDomain;
|
|
459
|
+
registerTools(tools: Tool<TContext, TData, unknown[], unknown>[]): this {
|
|
460
|
+
tools.forEach((tool) => this.createTool(tool));
|
|
461
|
+
return this;
|
|
212
462
|
}
|
|
213
463
|
|
|
214
464
|
/**
|
|
215
465
|
* Update the agent's context
|
|
216
|
-
* Triggers
|
|
466
|
+
* Triggers both agent-level and route-specific onContextUpdate lifecycle hooks if configured
|
|
217
467
|
*/
|
|
218
468
|
async updateContext(updates: Partial<TContext>): Promise<void> {
|
|
219
469
|
const previousContext = this.context;
|
|
@@ -224,7 +474,20 @@ export class Agent<TContext = unknown> {
|
|
|
224
474
|
...(updates as Record<string, unknown>),
|
|
225
475
|
} as TContext;
|
|
226
476
|
|
|
227
|
-
// Trigger lifecycle hook if configured
|
|
477
|
+
// Trigger route-specific lifecycle hook if configured and session has current route
|
|
478
|
+
if (this.currentSession?.currentRoute) {
|
|
479
|
+
const currentRoute = this.routes.find(
|
|
480
|
+
(r) => r.id === this.currentSession!.currentRoute?.id
|
|
481
|
+
);
|
|
482
|
+
if (
|
|
483
|
+
currentRoute?.hooks?.onContextUpdate &&
|
|
484
|
+
previousContext !== undefined
|
|
485
|
+
) {
|
|
486
|
+
await currentRoute.handleContextUpdate(this.context, previousContext);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Trigger agent-level lifecycle hook if configured
|
|
228
491
|
if (this.options.hooks?.onContextUpdate && previousContext !== undefined) {
|
|
229
492
|
await this.options.hooks.onContextUpdate(this.context, previousContext);
|
|
230
493
|
}
|
|
@@ -232,38 +495,53 @@ export class Agent<TContext = unknown> {
|
|
|
232
495
|
|
|
233
496
|
/**
|
|
234
497
|
* Update collected data in session with lifecycle hook support
|
|
235
|
-
* Triggers
|
|
498
|
+
* Triggers both agent-level and route-specific onDataUpdate lifecycle hooks if configured
|
|
236
499
|
* @internal
|
|
237
500
|
*/
|
|
238
|
-
private async updateData
|
|
501
|
+
private async updateData(
|
|
239
502
|
session: SessionState<TData>,
|
|
240
|
-
|
|
503
|
+
dataUpdate: Partial<TData>
|
|
241
504
|
): Promise<SessionState<TData>> {
|
|
242
505
|
const previousCollected = { ...session.data };
|
|
243
506
|
|
|
244
507
|
// Merge new collected data
|
|
245
508
|
let newCollected = {
|
|
246
509
|
...session.data,
|
|
247
|
-
...
|
|
510
|
+
...dataUpdate,
|
|
248
511
|
};
|
|
249
512
|
|
|
250
|
-
// Trigger lifecycle hook if configured
|
|
513
|
+
// Trigger route-specific lifecycle hook if configured and session has a current route
|
|
514
|
+
if (session.currentRoute) {
|
|
515
|
+
const currentRoute = this.routes.find(
|
|
516
|
+
(r) => r.id === session.currentRoute?.id
|
|
517
|
+
);
|
|
518
|
+
if (currentRoute?.hooks?.onDataUpdate) {
|
|
519
|
+
newCollected = await currentRoute.handleDataUpdate(
|
|
520
|
+
newCollected,
|
|
521
|
+
previousCollected
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Trigger agent-level lifecycle hook if configured
|
|
251
527
|
if (this.options.hooks?.onDataUpdate) {
|
|
252
528
|
newCollected = (await this.options.hooks.onDataUpdate(
|
|
253
529
|
newCollected,
|
|
254
530
|
previousCollected
|
|
255
|
-
))
|
|
531
|
+
));
|
|
256
532
|
}
|
|
257
533
|
|
|
534
|
+
// Update agent's collected data to stay in sync
|
|
535
|
+
this.collectedData = { ...newCollected };
|
|
536
|
+
|
|
258
537
|
// Return updated session
|
|
259
538
|
return mergeCollected(session, newCollected);
|
|
260
539
|
}
|
|
261
540
|
|
|
262
541
|
/**
|
|
263
542
|
* Get current context (fetches from provider if configured)
|
|
264
|
-
* @internal
|
|
265
543
|
*/
|
|
266
|
-
|
|
544
|
+
async getContext(): Promise<TContext | undefined> {
|
|
267
545
|
// If context provider is configured, use it to fetch fresh context
|
|
268
546
|
if (this.options.contextProvider) {
|
|
269
547
|
return await this.options.contextProvider();
|
|
@@ -272,197 +550,91 @@ export class Agent<TContext = unknown> {
|
|
|
272
550
|
// Otherwise return the stored context
|
|
273
551
|
return this.context;
|
|
274
552
|
}
|
|
553
|
+
/**
|
|
554
|
+
* Get current schema
|
|
555
|
+
*/
|
|
556
|
+
getSchema(): StructuredSchema | undefined {
|
|
557
|
+
return this.schema;
|
|
558
|
+
}
|
|
275
559
|
|
|
276
560
|
/**
|
|
277
561
|
* Generate a response based on history and context as a stream
|
|
278
562
|
*/
|
|
279
563
|
async *respondStream(params: {
|
|
280
|
-
history:
|
|
564
|
+
history: History;
|
|
281
565
|
step?: StepRef;
|
|
282
|
-
session?: SessionState
|
|
566
|
+
session?: SessionState<TData>;
|
|
283
567
|
contextOverride?: Partial<TContext>;
|
|
284
568
|
signal?: AbortSignal;
|
|
285
|
-
}): AsyncGenerator<{
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
session
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
569
|
+
}): AsyncGenerator<AgentResponseStreamChunk<TData>> {
|
|
570
|
+
const { history: simpleHistory, signal } = params;
|
|
571
|
+
const history = normalizeHistory(simpleHistory);
|
|
572
|
+
|
|
573
|
+
// Prepare context and session using the response pipeline
|
|
574
|
+
this.responsePipeline.setContext(this.context);
|
|
575
|
+
this.responsePipeline.setCurrentSession(this.currentSession);
|
|
576
|
+
let session: SessionState;
|
|
577
|
+
const responseContext = await this.responsePipeline.prepareResponseContext({
|
|
578
|
+
contextOverride: params.contextOverride,
|
|
579
|
+
session: params.session ? cloneDeep(params.session) : undefined,
|
|
580
|
+
});
|
|
581
|
+
const { effectiveContext } = responseContext;
|
|
582
|
+
session = responseContext.session;
|
|
583
|
+
|
|
584
|
+
// Merge agent's collected data into session (agent data takes precedence)
|
|
585
|
+
if (Object.keys(this.collectedData).length > 0) {
|
|
586
|
+
session = mergeCollected(session, this.collectedData);
|
|
587
|
+
logger.debug("[Agent] Merged agent collected data into session:", this.collectedData);
|
|
303
588
|
}
|
|
589
|
+
|
|
590
|
+
// Update our stored context if it was modified by beforeRespond hook
|
|
591
|
+
this.context = this.responsePipeline.getStoredContext();
|
|
304
592
|
|
|
305
|
-
//
|
|
306
|
-
const effectiveContext = {
|
|
307
|
-
...(currentContext as Record<string, unknown>),
|
|
308
|
-
...(contextOverride as Record<string, unknown>),
|
|
309
|
-
} as TContext;
|
|
310
|
-
|
|
311
|
-
// Initialize or get session (use current session if available)
|
|
312
|
-
let session = params.session || this.currentSession || createSession();
|
|
313
|
-
|
|
314
|
-
// PHASE 1: TOOL EXECUTION - Execute tools if current step has tool
|
|
593
|
+
// PHASE 1: PREPARE - Execute prepare function if current step has one
|
|
315
594
|
if (session.currentRoute && session.currentStep) {
|
|
316
595
|
const currentRoute = this.routes.find(
|
|
317
596
|
(r) => r.id === session.currentRoute?.id
|
|
318
597
|
);
|
|
319
598
|
if (currentRoute) {
|
|
320
599
|
const currentStep = currentRoute.getStep(session.currentStep.id);
|
|
321
|
-
if (currentStep) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
toolTransition.spec.tool as ToolRef<TContext, unknown[], unknown>,
|
|
331
|
-
effectiveContext,
|
|
332
|
-
this.updateContext.bind(this),
|
|
333
|
-
history,
|
|
334
|
-
session.data,
|
|
335
|
-
allowedDomains
|
|
336
|
-
);
|
|
337
|
-
|
|
338
|
-
// Update context with tool results
|
|
339
|
-
if (result.contextUpdate) {
|
|
340
|
-
await this.updateContext(
|
|
341
|
-
result.contextUpdate as Partial<TContext>
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Update collected data with tool results
|
|
346
|
-
if (result.collectedUpdate) {
|
|
347
|
-
session = await this.updateData(session, result.collectedUpdate);
|
|
348
|
-
logger.debug(
|
|
349
|
-
`[Agent] Tool updated collected data:`,
|
|
350
|
-
result.collectedUpdate
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
logger.debug(
|
|
355
|
-
`[Agent] Executed tool: ${result.toolName} (success: ${result.success})`
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// PHASE 2: ROUTING + STEP SELECTION - Determine which route and step to use (combined)
|
|
363
|
-
let selectedRoute: Route<TContext> | undefined;
|
|
364
|
-
let responseDirectives: string[] | undefined;
|
|
365
|
-
let selectedStep: Step<TContext> | undefined;
|
|
366
|
-
let isRouteComplete = false;
|
|
367
|
-
|
|
368
|
-
// Check for pending transition from previous route completion
|
|
369
|
-
if (session.pendingTransition) {
|
|
370
|
-
const targetRoute = this.routes.find(
|
|
371
|
-
(r) => r.id === session.pendingTransition?.targetRouteId
|
|
372
|
-
);
|
|
373
|
-
|
|
374
|
-
if (targetRoute) {
|
|
375
|
-
logger.debug(
|
|
376
|
-
`[Agent] Auto-transitioning from pending transition to route: ${targetRoute.title}`
|
|
377
|
-
);
|
|
378
|
-
// Clear pending transition and enter new route
|
|
379
|
-
session = {
|
|
380
|
-
...session,
|
|
381
|
-
pendingTransition: undefined,
|
|
382
|
-
};
|
|
383
|
-
session = enterRoute(session, targetRoute.id, targetRoute.title);
|
|
384
|
-
|
|
385
|
-
// Merge initial data if available
|
|
386
|
-
if (targetRoute.initialData) {
|
|
387
|
-
session = mergeCollected(session, targetRoute.initialData);
|
|
600
|
+
if (currentStep?.prepare) {
|
|
601
|
+
logger.debug(`[Agent] Executing prepare for step: ${currentStep.id}`);
|
|
602
|
+
await this.executePrepareFinalize(
|
|
603
|
+
currentStep.prepare,
|
|
604
|
+
effectiveContext,
|
|
605
|
+
session.data,
|
|
606
|
+
currentRoute,
|
|
607
|
+
currentStep
|
|
608
|
+
);
|
|
388
609
|
}
|
|
389
|
-
|
|
390
|
-
selectedRoute = targetRoute;
|
|
391
|
-
} else {
|
|
392
|
-
logger.warn(
|
|
393
|
-
`[Agent] Pending transition target route not found: ${session.pendingTransition.targetRouteId}`
|
|
394
|
-
);
|
|
395
|
-
// Clear invalid transition
|
|
396
|
-
session = {
|
|
397
|
-
...session,
|
|
398
|
-
pendingTransition: undefined,
|
|
399
|
-
};
|
|
400
610
|
}
|
|
401
611
|
}
|
|
402
612
|
|
|
403
|
-
//
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
routes: this.routes,
|
|
613
|
+
// PHASE 2: ROUTING + STEP SELECTION - Use response pipeline
|
|
614
|
+
const routingResult =
|
|
615
|
+
await this.responsePipeline.handleRoutingAndStepSelection({
|
|
407
616
|
session,
|
|
408
617
|
history,
|
|
409
|
-
agentMeta: {
|
|
410
|
-
name: this.options.name,
|
|
411
|
-
goal: this.options.goal,
|
|
412
|
-
description: this.options.description,
|
|
413
|
-
personality: this.options.personality,
|
|
414
|
-
},
|
|
415
|
-
provider: this.options.provider,
|
|
416
618
|
context: effectiveContext,
|
|
417
619
|
signal,
|
|
418
620
|
});
|
|
621
|
+
const selectedRoute = routingResult.selectedRoute;
|
|
622
|
+
const selectedStep = routingResult.selectedStep;
|
|
623
|
+
const responseDirectives = routingResult.responseDirectives;
|
|
624
|
+
const isRouteComplete = routingResult.isRouteComplete;
|
|
625
|
+
session = routingResult.session;
|
|
626
|
+
|
|
627
|
+
// PHASE 3: DETERMINE NEXT STEP - Use pipeline method
|
|
628
|
+
const stepResult = this.responsePipeline.determineNextStep({
|
|
629
|
+
selectedRoute,
|
|
630
|
+
selectedStep,
|
|
631
|
+
session,
|
|
632
|
+
isRouteComplete,
|
|
633
|
+
});
|
|
634
|
+
const nextStep = stepResult.nextStep;
|
|
635
|
+
session = stepResult.session;
|
|
419
636
|
|
|
420
|
-
selectedRoute = orchestration.selectedRoute;
|
|
421
|
-
selectedStep = orchestration.selectedStep;
|
|
422
|
-
responseDirectives = orchestration.responseDirectives;
|
|
423
|
-
session = orchestration.session;
|
|
424
|
-
isRouteComplete = orchestration.isRouteComplete || false;
|
|
425
|
-
|
|
426
|
-
// Log if route is complete
|
|
427
|
-
if (isRouteComplete) {
|
|
428
|
-
logger.debug(
|
|
429
|
-
`[Agent] Route complete: all required data collected, END_ROUTE reached`
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// PHASE 3: DETERMINE NEXT STEP - Use step from combined decision or get initial step
|
|
435
637
|
if (selectedRoute && !isRouteComplete) {
|
|
436
|
-
let nextStep: Step<TContext>;
|
|
437
|
-
|
|
438
|
-
// If we have a selected step from the combined routing decision, use it
|
|
439
|
-
if (selectedStep) {
|
|
440
|
-
nextStep = selectedStep;
|
|
441
|
-
} else {
|
|
442
|
-
// New route or no step selected - get initial step or first valid step
|
|
443
|
-
const candidates = this.routingEngine.getCandidateSteps(
|
|
444
|
-
selectedRoute,
|
|
445
|
-
undefined,
|
|
446
|
-
session.data || {}
|
|
447
|
-
);
|
|
448
|
-
if (candidates.length > 0) {
|
|
449
|
-
nextStep = candidates[0].step;
|
|
450
|
-
logger.debug(
|
|
451
|
-
`[Agent] Using first valid step: ${nextStep.id} for new route`
|
|
452
|
-
);
|
|
453
|
-
} else {
|
|
454
|
-
// Fallback to initial step even if it should be skipped
|
|
455
|
-
nextStep = selectedRoute.initialStep;
|
|
456
|
-
logger.warn(
|
|
457
|
-
`[Agent] No valid steps found, using initial step: ${nextStep.id}`
|
|
458
|
-
);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// Update session with next step
|
|
463
|
-
session = enterStep(session, nextStep.id, nextStep.description);
|
|
464
|
-
logger.debug(`[Agent] Entered step: ${nextStep.id}`);
|
|
465
|
-
|
|
466
638
|
// PHASE 4: RESPONSE GENERATION - Stream message using selected route and step
|
|
467
639
|
// Get last user message
|
|
468
640
|
const lastUserMessage = getLastMessageFromHistory(history);
|
|
@@ -470,24 +642,47 @@ export class Agent<TContext = unknown> {
|
|
|
470
642
|
// Build response schema for this route (with collect fields from step)
|
|
471
643
|
const responseSchema = this.responseEngine.responseSchemaForRoute(
|
|
472
644
|
selectedRoute,
|
|
473
|
-
nextStep
|
|
645
|
+
nextStep,
|
|
646
|
+
this.schema
|
|
474
647
|
);
|
|
475
648
|
|
|
649
|
+
// Check if selected route and next step are defined
|
|
650
|
+
if (!selectedRoute || !nextStep) {
|
|
651
|
+
logger.error("[Agent] Selected route or next step is not defined", {
|
|
652
|
+
selectedRoute,
|
|
653
|
+
nextStep,
|
|
654
|
+
});
|
|
655
|
+
throw new Error("Selected route or next step is not defined");
|
|
656
|
+
}
|
|
657
|
+
|
|
476
658
|
// Build response prompt
|
|
477
|
-
const responsePrompt = this.responseEngine.buildResponsePrompt(
|
|
478
|
-
selectedRoute,
|
|
479
|
-
nextStep,
|
|
480
|
-
selectedRoute.getRules(),
|
|
481
|
-
selectedRoute.getProhibitions(),
|
|
482
|
-
responseDirectives,
|
|
659
|
+
const responsePrompt = await this.responseEngine.buildResponsePrompt({
|
|
660
|
+
route: selectedRoute,
|
|
661
|
+
currentStep: nextStep,
|
|
662
|
+
rules: selectedRoute.getRules(),
|
|
663
|
+
prohibitions: selectedRoute.getProhibitions(),
|
|
664
|
+
directives: responseDirectives,
|
|
483
665
|
history,
|
|
484
|
-
lastUserMessage,
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
666
|
+
lastMessage: lastUserMessage,
|
|
667
|
+
agentOptions: this.options,
|
|
668
|
+
// Combine agent and route properties according to the specified logic
|
|
669
|
+
combinedGuidelines: [
|
|
670
|
+
...this.getGuidelines(),
|
|
671
|
+
...selectedRoute.getGuidelines(),
|
|
672
|
+
],
|
|
673
|
+
combinedTerms: this.mergeTerms(
|
|
674
|
+
this.getTerms(),
|
|
675
|
+
selectedRoute.getTerms()
|
|
676
|
+
),
|
|
677
|
+
context: effectiveContext,
|
|
678
|
+
session,
|
|
679
|
+
agentSchema: this.schema,
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
// Collect available tools for AI
|
|
683
|
+
const availableTools = this.collectAvailableTools(
|
|
684
|
+
selectedRoute,
|
|
685
|
+
nextStep
|
|
491
686
|
);
|
|
492
687
|
|
|
493
688
|
// Generate message stream using AI provider
|
|
@@ -495,6 +690,7 @@ export class Agent<TContext = unknown> {
|
|
|
495
690
|
prompt: responsePrompt,
|
|
496
691
|
history,
|
|
497
692
|
context: effectiveContext,
|
|
693
|
+
tools: availableTools,
|
|
498
694
|
signal,
|
|
499
695
|
parameters: {
|
|
500
696
|
jsonSchema: responseSchema,
|
|
@@ -504,26 +700,220 @@ export class Agent<TContext = unknown> {
|
|
|
504
700
|
|
|
505
701
|
// Stream chunks to caller
|
|
506
702
|
for await (const chunk of stream) {
|
|
507
|
-
|
|
703
|
+
let toolCalls:
|
|
508
704
|
| Array<{ toolName: string; arguments: Record<string, unknown> }>
|
|
509
705
|
| undefined = undefined;
|
|
510
706
|
|
|
707
|
+
// Extract tool calls from AI response on final chunk
|
|
708
|
+
if (chunk.done && chunk.structured?.toolCalls) {
|
|
709
|
+
toolCalls = chunk.structured.toolCalls;
|
|
710
|
+
|
|
711
|
+
// Execute dynamic tool calls
|
|
712
|
+
if (toolCalls.length > 0) {
|
|
713
|
+
logger.debug(
|
|
714
|
+
`[Agent] Executing ${toolCalls.length} dynamic tool calls`
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
for (const toolCall of toolCalls) {
|
|
718
|
+
const tool = this.findAvailableTool(
|
|
719
|
+
toolCall.toolName,
|
|
720
|
+
selectedRoute
|
|
721
|
+
);
|
|
722
|
+
if (!tool) {
|
|
723
|
+
logger.warn(`[Agent] Tool not found: ${toolCall.toolName}`);
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
const toolExecutor = new ToolExecutor<TContext, TData>();
|
|
728
|
+
const result = await toolExecutor.executeTool({
|
|
729
|
+
tool: tool,
|
|
730
|
+
context: effectiveContext,
|
|
731
|
+
updateContext: this.updateContext.bind(this),
|
|
732
|
+
updateData: this.updateCollectedData.bind(this),
|
|
733
|
+
history,
|
|
734
|
+
data: session.data,
|
|
735
|
+
toolArguments: toolCall.arguments,
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
// Update context with tool results
|
|
739
|
+
if (result.contextUpdate) {
|
|
740
|
+
await this.updateContext(
|
|
741
|
+
result.contextUpdate as Partial<TContext>
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Update collected data with tool results
|
|
746
|
+
if (result.dataUpdate) {
|
|
747
|
+
session = await this.updateData(session, result.dataUpdate as Partial<TData>);
|
|
748
|
+
logger.debug(
|
|
749
|
+
`[Agent] Tool updated collected data:`,
|
|
750
|
+
result.dataUpdate
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
logger.debug(
|
|
755
|
+
`[Agent] Executed dynamic tool: ${result.toolName} (success: ${result.success})`
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// TOOL LOOP: Allow AI to make follow-up tool calls after initial tool execution (streaming)
|
|
762
|
+
const MAX_TOOL_LOOPS = 5;
|
|
763
|
+
let toolLoopCount = 0;
|
|
764
|
+
let hasToolCalls = toolCalls && toolCalls.length > 0;
|
|
765
|
+
|
|
766
|
+
while (hasToolCalls && toolLoopCount < MAX_TOOL_LOOPS) {
|
|
767
|
+
toolLoopCount++;
|
|
768
|
+
logger.debug(
|
|
769
|
+
`[Agent] Starting streaming tool loop ${toolLoopCount}/${MAX_TOOL_LOOPS}`
|
|
770
|
+
);
|
|
771
|
+
|
|
772
|
+
// Add tool execution results to history so AI knows what happened
|
|
773
|
+
const toolResultsEvents: Event[] = [];
|
|
774
|
+
for (const toolCall of toolCalls || []) {
|
|
775
|
+
const tool = this.findAvailableTool(
|
|
776
|
+
toolCall.toolName,
|
|
777
|
+
selectedRoute
|
|
778
|
+
);
|
|
779
|
+
if (tool) {
|
|
780
|
+
toolResultsEvents.push({
|
|
781
|
+
kind: EventKind.TOOL,
|
|
782
|
+
source: MessageRole.AGENT,
|
|
783
|
+
timestamp: new Date().toISOString(),
|
|
784
|
+
data: {
|
|
785
|
+
tool_calls: [
|
|
786
|
+
{
|
|
787
|
+
tool_id: toolCall.toolName,
|
|
788
|
+
arguments: toolCall.arguments,
|
|
789
|
+
result: {
|
|
790
|
+
data: "Tool executed successfully",
|
|
791
|
+
},
|
|
792
|
+
},
|
|
793
|
+
],
|
|
794
|
+
},
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// Create updated history with tool results
|
|
800
|
+
const updatedHistory = [...history, ...toolResultsEvents];
|
|
801
|
+
|
|
802
|
+
// Make follow-up streaming AI call to see if more tools are needed
|
|
803
|
+
const followUpStream = this.options.provider.generateMessageStream({
|
|
804
|
+
prompt: responsePrompt,
|
|
805
|
+
history: updatedHistory,
|
|
806
|
+
context: effectiveContext,
|
|
807
|
+
tools: availableTools,
|
|
808
|
+
parameters: {
|
|
809
|
+
jsonSchema: responseSchema,
|
|
810
|
+
schemaName: "tool_followup",
|
|
811
|
+
},
|
|
812
|
+
signal,
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
let followUpToolCalls:
|
|
816
|
+
| Array<{ toolName: string; arguments: Record<string, unknown> }>
|
|
817
|
+
| undefined;
|
|
818
|
+
|
|
819
|
+
for await (const followUpChunk of followUpStream) {
|
|
820
|
+
// Extract tool calls from follow-up stream
|
|
821
|
+
if (followUpChunk.done && followUpChunk.structured?.toolCalls) {
|
|
822
|
+
followUpToolCalls = followUpChunk.structured.toolCalls;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
hasToolCalls = followUpToolCalls && followUpToolCalls.length > 0;
|
|
827
|
+
|
|
828
|
+
if (hasToolCalls) {
|
|
829
|
+
logger.debug(
|
|
830
|
+
`[Agent] Follow-up streaming call produced ${followUpToolCalls!.length
|
|
831
|
+
} additional tool calls`
|
|
832
|
+
);
|
|
833
|
+
|
|
834
|
+
// Execute the follow-up tool calls
|
|
835
|
+
for (const toolCall of followUpToolCalls!) {
|
|
836
|
+
const tool = this.findAvailableTool(
|
|
837
|
+
toolCall.toolName,
|
|
838
|
+
selectedRoute
|
|
839
|
+
);
|
|
840
|
+
if (!tool) {
|
|
841
|
+
logger.warn(
|
|
842
|
+
`[Agent] Tool not found in streaming follow-up: ${toolCall.toolName}`
|
|
843
|
+
);
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
const toolExecutor = new ToolExecutor<TContext, TData>();
|
|
848
|
+
const result = await toolExecutor.executeTool({
|
|
849
|
+
tool: tool,
|
|
850
|
+
context: effectiveContext,
|
|
851
|
+
updateContext: this.updateContext.bind(this),
|
|
852
|
+
updateData: this.updateCollectedData.bind(this),
|
|
853
|
+
history: updatedHistory,
|
|
854
|
+
data: session.data,
|
|
855
|
+
toolArguments: toolCall.arguments,
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
// Update context with follow-up tool results
|
|
859
|
+
if (result.contextUpdate) {
|
|
860
|
+
await this.updateContext(
|
|
861
|
+
result.contextUpdate as Partial<TContext>
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
if (result.dataUpdate) {
|
|
866
|
+
session = await this.updateData(session, result.dataUpdate as Partial<TData>);
|
|
867
|
+
logger.debug(
|
|
868
|
+
`[Agent] Streaming follow-up tool updated collected data:`,
|
|
869
|
+
result.dataUpdate
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
logger.debug(
|
|
874
|
+
`[Agent] Executed streaming follow-up tool: ${result.toolName} (success: ${result.success})`
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// Update toolCalls for next iteration
|
|
879
|
+
toolCalls = followUpToolCalls;
|
|
880
|
+
} else {
|
|
881
|
+
logger.debug(
|
|
882
|
+
`[Agent] Streaming tool loop completed after ${toolLoopCount} iterations`
|
|
883
|
+
);
|
|
884
|
+
// Update toolCalls for final response
|
|
885
|
+
toolCalls = followUpToolCalls || [];
|
|
886
|
+
break;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
if (toolLoopCount >= MAX_TOOL_LOOPS) {
|
|
891
|
+
logger.warn(
|
|
892
|
+
`[Agent] Streaming tool loop limit reached (${MAX_TOOL_LOOPS}), stopping`
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
|
|
511
896
|
// Extract collected data on final chunk
|
|
512
|
-
if (chunk.done && chunk.structured && nextStep.
|
|
897
|
+
if (chunk.done && chunk.structured && nextStep.collect) {
|
|
513
898
|
const collectedData: Record<string, unknown> = {};
|
|
514
899
|
// The structured response includes both base fields and collected extraction fields
|
|
515
900
|
const structuredData = chunk.structured as AgentStructuredResponse &
|
|
516
901
|
Record<string, unknown>;
|
|
517
902
|
|
|
518
|
-
for (const field of nextStep.
|
|
519
|
-
|
|
520
|
-
|
|
903
|
+
for (const field of nextStep.collect) {
|
|
904
|
+
const fieldKey = String(field);
|
|
905
|
+
if (fieldKey in structuredData) {
|
|
906
|
+
collectedData[fieldKey] = structuredData[fieldKey];
|
|
521
907
|
}
|
|
522
908
|
}
|
|
523
909
|
|
|
524
|
-
// Merge collected data into session
|
|
910
|
+
// Merge collected data into session using agent-level data validation
|
|
525
911
|
if (Object.keys(collectedData).length > 0) {
|
|
526
|
-
|
|
912
|
+
// Update agent-level collected data with validation
|
|
913
|
+
await this.updateCollectedData(collectedData as Partial<TData>);
|
|
914
|
+
|
|
915
|
+
// Update session with validated data
|
|
916
|
+
session = await this.updateData(session, collectedData as Partial<TData>);
|
|
527
917
|
logger.debug(`[Agent] Collected data:`, collectedData);
|
|
528
918
|
}
|
|
529
919
|
}
|
|
@@ -554,6 +944,28 @@ export class Agent<TContext = unknown> {
|
|
|
554
944
|
);
|
|
555
945
|
}
|
|
556
946
|
|
|
947
|
+
// Execute finalize function on final chunk
|
|
948
|
+
if (chunk.done && session.currentRoute && session.currentStep) {
|
|
949
|
+
const currentRoute = this.routes.find(
|
|
950
|
+
(r) => r.id === session.currentRoute?.id
|
|
951
|
+
);
|
|
952
|
+
if (currentRoute) {
|
|
953
|
+
const currentStep = currentRoute.getStep(session.currentStep.id);
|
|
954
|
+
if (currentStep?.finalize) {
|
|
955
|
+
logger.debug(
|
|
956
|
+
`[Agent] Executing finalize for step: ${currentStep.id}`
|
|
957
|
+
);
|
|
958
|
+
await this.executePrepareFinalize(
|
|
959
|
+
currentStep.finalize,
|
|
960
|
+
effectiveContext,
|
|
961
|
+
session.data,
|
|
962
|
+
currentRoute,
|
|
963
|
+
currentStep
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
557
969
|
// Update current session if we have one
|
|
558
970
|
if (chunk.done && this.currentSession) {
|
|
559
971
|
this.currentSession = session;
|
|
@@ -566,6 +978,8 @@ export class Agent<TContext = unknown> {
|
|
|
566
978
|
session, // Return updated session
|
|
567
979
|
toolCalls,
|
|
568
980
|
isRouteComplete,
|
|
981
|
+
metadata: chunk.metadata,
|
|
982
|
+
structured: chunk.structured,
|
|
569
983
|
};
|
|
570
984
|
}
|
|
571
985
|
} else if (isRouteComplete && selectedRoute) {
|
|
@@ -576,40 +990,51 @@ export class Agent<TContext = unknown> {
|
|
|
576
990
|
const endStepSpec = selectedRoute.endStepSpec;
|
|
577
991
|
|
|
578
992
|
// Create a temporary step for completion message generation using endStep configuration
|
|
579
|
-
const completionStep = new Step<TContext>(
|
|
580
|
-
|
|
581
|
-
endStepSpec.
|
|
582
|
-
|
|
583
|
-
endStepSpec.
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
"Summarize what was accomplished and confirm completion based on the conversation history and collected data"
|
|
589
|
-
);
|
|
993
|
+
const completionStep = new Step<TContext, TData>(selectedRoute.id, {
|
|
994
|
+
description: endStepSpec.description,
|
|
995
|
+
id: endStepSpec.id || END_ROUTE_ID,
|
|
996
|
+
collect: endStepSpec.collect,
|
|
997
|
+
requires: endStepSpec.requires,
|
|
998
|
+
prompt:
|
|
999
|
+
endStepSpec.prompt ||
|
|
1000
|
+
"Summarize what was accomplished and confirm completion based on the conversation history and collected data",
|
|
1001
|
+
});
|
|
590
1002
|
|
|
591
1003
|
// Build response schema for completion
|
|
592
1004
|
const responseSchema = this.responseEngine.responseSchemaForRoute(
|
|
593
1005
|
selectedRoute,
|
|
594
|
-
completionStep
|
|
1006
|
+
completionStep,
|
|
1007
|
+
this.schema
|
|
595
1008
|
);
|
|
1009
|
+
const templateContext = {
|
|
1010
|
+
context: effectiveContext,
|
|
1011
|
+
session,
|
|
1012
|
+
history,
|
|
1013
|
+
};
|
|
596
1014
|
|
|
597
1015
|
// Build completion response prompt
|
|
598
|
-
const completionPrompt = this.responseEngine.buildResponsePrompt(
|
|
599
|
-
selectedRoute,
|
|
600
|
-
completionStep,
|
|
601
|
-
selectedRoute.getRules(),
|
|
602
|
-
selectedRoute.getProhibitions(),
|
|
603
|
-
undefined, // No directives for completion
|
|
1016
|
+
const completionPrompt = await this.responseEngine.buildResponsePrompt({
|
|
1017
|
+
route: selectedRoute,
|
|
1018
|
+
currentStep: completionStep,
|
|
1019
|
+
rules: selectedRoute.getRules(),
|
|
1020
|
+
prohibitions: selectedRoute.getProhibitions(),
|
|
1021
|
+
directives: undefined, // No directives for completion
|
|
604
1022
|
history,
|
|
605
|
-
lastUserMessage,
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
1023
|
+
lastMessage: lastUserMessage,
|
|
1024
|
+
agentOptions: this.options,
|
|
1025
|
+
// Combine agent and route properties according to the specified logic
|
|
1026
|
+
combinedGuidelines: [
|
|
1027
|
+
...this.getGuidelines(),
|
|
1028
|
+
...selectedRoute.getGuidelines(),
|
|
1029
|
+
],
|
|
1030
|
+
combinedTerms: this.mergeTerms(
|
|
1031
|
+
this.getTerms(),
|
|
1032
|
+
selectedRoute.getTerms()
|
|
1033
|
+
),
|
|
1034
|
+
context: effectiveContext,
|
|
1035
|
+
session,
|
|
1036
|
+
agentSchema: this.schema,
|
|
1037
|
+
});
|
|
613
1038
|
|
|
614
1039
|
// Stream completion message using AI provider
|
|
615
1040
|
const stream = this.options.provider.generateMessageStream({
|
|
@@ -642,12 +1067,16 @@ export class Agent<TContext = unknown> {
|
|
|
642
1067
|
);
|
|
643
1068
|
|
|
644
1069
|
if (targetRoute) {
|
|
1070
|
+
const renderedCondition = await render(
|
|
1071
|
+
transitionConfig.condition,
|
|
1072
|
+
templateContext
|
|
1073
|
+
);
|
|
645
1074
|
// Set pending transition in session
|
|
646
1075
|
session = {
|
|
647
1076
|
...session,
|
|
648
1077
|
pendingTransition: {
|
|
649
1078
|
targetRouteId: targetRoute.id,
|
|
650
|
-
condition:
|
|
1079
|
+
condition: renderedCondition,
|
|
651
1080
|
reason: "route_complete",
|
|
652
1081
|
},
|
|
653
1082
|
};
|
|
@@ -681,22 +1110,20 @@ export class Agent<TContext = unknown> {
|
|
|
681
1110
|
session,
|
|
682
1111
|
toolCalls: undefined,
|
|
683
1112
|
isRouteComplete: true,
|
|
1113
|
+
metadata: chunk.metadata,
|
|
1114
|
+
structured: chunk.structured,
|
|
684
1115
|
};
|
|
685
1116
|
}
|
|
686
1117
|
} else {
|
|
687
1118
|
// Fallback: No routes defined, stream a simple response
|
|
688
|
-
const fallbackPrompt =
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
.addGlossary(this.terms)
|
|
697
|
-
.addGuidelines(this.guidelines)
|
|
698
|
-
.addCapabilities(this.capabilities)
|
|
699
|
-
.build();
|
|
1119
|
+
const fallbackPrompt = await this.responseEngine.buildFallbackPrompt({
|
|
1120
|
+
history,
|
|
1121
|
+
agentOptions: this.options,
|
|
1122
|
+
terms: this.terms,
|
|
1123
|
+
guidelines: this.guidelines,
|
|
1124
|
+
context: effectiveContext,
|
|
1125
|
+
session,
|
|
1126
|
+
});
|
|
700
1127
|
|
|
701
1128
|
const stream = this.options.provider.generateMessageStream({
|
|
702
1129
|
prompt: fallbackPrompt,
|
|
@@ -729,6 +1156,8 @@ export class Agent<TContext = unknown> {
|
|
|
729
1156
|
session, // Return updated session
|
|
730
1157
|
toolCalls: undefined,
|
|
731
1158
|
isRouteComplete: false,
|
|
1159
|
+
metadata: chunk.metadata,
|
|
1160
|
+
structured: chunk.structured,
|
|
732
1161
|
};
|
|
733
1162
|
}
|
|
734
1163
|
}
|
|
@@ -738,18 +1167,14 @@ export class Agent<TContext = unknown> {
|
|
|
738
1167
|
* Generate a response based on history and context
|
|
739
1168
|
*/
|
|
740
1169
|
async respond(params: {
|
|
741
|
-
history:
|
|
1170
|
+
history: History;
|
|
742
1171
|
step?: StepRef;
|
|
743
|
-
session?: SessionState
|
|
1172
|
+
session?: SessionState<TData>;
|
|
744
1173
|
contextOverride?: Partial<TContext>;
|
|
745
1174
|
signal?: AbortSignal;
|
|
746
|
-
}): Promise<{
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
toolCalls?: Array<{ toolName: string; arguments: Record<string, unknown> }>;
|
|
750
|
-
isRouteComplete?: boolean;
|
|
751
|
-
}> {
|
|
752
|
-
const { history, contextOverride, signal } = params;
|
|
1175
|
+
}): Promise<AgentResponse<TData>> {
|
|
1176
|
+
const { history: simpleHistory, contextOverride, signal } = params;
|
|
1177
|
+
const history = normalizeHistory(simpleHistory);
|
|
753
1178
|
|
|
754
1179
|
// Get current context (may fetch from provider)
|
|
755
1180
|
let currentContext = await this.getContext();
|
|
@@ -769,60 +1194,40 @@ export class Agent<TContext = unknown> {
|
|
|
769
1194
|
|
|
770
1195
|
// Initialize or get session (use current session if available)
|
|
771
1196
|
let session =
|
|
772
|
-
params.session ||
|
|
1197
|
+
cloneDeep(params.session) ||
|
|
1198
|
+
cloneDeep(this.currentSession) ||
|
|
1199
|
+
(await this.session.getOrCreate());
|
|
1200
|
+
|
|
1201
|
+
// Merge agent's collected data into session (agent data takes precedence)
|
|
1202
|
+
if (Object.keys(this.collectedData).length > 0) {
|
|
1203
|
+
session = mergeCollected(session, this.collectedData);
|
|
1204
|
+
logger.debug("[Agent] Merged agent collected data into session:", this.collectedData);
|
|
1205
|
+
}
|
|
773
1206
|
|
|
774
|
-
// PHASE 1:
|
|
1207
|
+
// PHASE 1: PREPARE - Execute prepare function if current step has one
|
|
775
1208
|
if (session.currentRoute && session.currentStep) {
|
|
776
1209
|
const currentRoute = this.routes.find(
|
|
777
1210
|
(r) => r.id === session.currentRoute?.id
|
|
778
1211
|
);
|
|
779
1212
|
if (currentRoute) {
|
|
780
1213
|
const currentStep = currentRoute.getStep(session.currentStep.id);
|
|
781
|
-
if (currentStep) {
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
toolTransition.spec.tool as ToolRef<TContext, unknown[], unknown>,
|
|
791
|
-
effectiveContext,
|
|
792
|
-
this.updateContext.bind(this),
|
|
793
|
-
history,
|
|
794
|
-
session.data,
|
|
795
|
-
allowedDomains
|
|
796
|
-
);
|
|
797
|
-
|
|
798
|
-
// Update context with tool results
|
|
799
|
-
if (result.contextUpdate) {
|
|
800
|
-
await this.updateContext(
|
|
801
|
-
result.contextUpdate as Partial<TContext>
|
|
802
|
-
);
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
// Update collected data with tool results
|
|
806
|
-
if (result.collectedUpdate) {
|
|
807
|
-
session = await this.updateData(session, result.collectedUpdate);
|
|
808
|
-
logger.debug(
|
|
809
|
-
`[Agent] Tool updated collected data:`,
|
|
810
|
-
result.collectedUpdate
|
|
811
|
-
);
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
logger.debug(
|
|
815
|
-
`[Agent] Executed tool: ${result.toolName} (success: ${result.success})`
|
|
816
|
-
);
|
|
817
|
-
}
|
|
1214
|
+
if (currentStep?.prepare) {
|
|
1215
|
+
logger.debug(`[Agent] Executing prepare for step: ${currentStep.id}`);
|
|
1216
|
+
await this.executePrepareFinalize(
|
|
1217
|
+
currentStep.prepare,
|
|
1218
|
+
effectiveContext,
|
|
1219
|
+
session.data,
|
|
1220
|
+
currentRoute,
|
|
1221
|
+
currentStep
|
|
1222
|
+
);
|
|
818
1223
|
}
|
|
819
1224
|
}
|
|
820
1225
|
}
|
|
821
1226
|
|
|
822
1227
|
// PHASE 2: ROUTING + STEP SELECTION - Determine which route and step to use (combined)
|
|
823
|
-
let selectedRoute: Route<TContext> | undefined;
|
|
1228
|
+
let selectedRoute: Route<TContext, TData> | undefined;
|
|
824
1229
|
let responseDirectives: string[] | undefined;
|
|
825
|
-
let selectedStep: Step<TContext> | undefined;
|
|
1230
|
+
let selectedStep: Step<TContext, TData> | undefined;
|
|
826
1231
|
let isRouteComplete = false;
|
|
827
1232
|
|
|
828
1233
|
// Check for pending transition from previous route completion
|
|
@@ -866,12 +1271,7 @@ export class Agent<TContext = unknown> {
|
|
|
866
1271
|
routes: this.routes,
|
|
867
1272
|
session,
|
|
868
1273
|
history,
|
|
869
|
-
|
|
870
|
-
name: this.options.name,
|
|
871
|
-
goal: this.options.goal,
|
|
872
|
-
description: this.options.description,
|
|
873
|
-
personality: this.options.personality,
|
|
874
|
-
},
|
|
1274
|
+
agentOptions: this.options,
|
|
875
1275
|
provider: this.options.provider,
|
|
876
1276
|
context: effectiveContext,
|
|
877
1277
|
signal,
|
|
@@ -893,13 +1293,23 @@ export class Agent<TContext = unknown> {
|
|
|
893
1293
|
|
|
894
1294
|
// PHASE 3: DETERMINE NEXT STEP - Use step from combined decision or get initial step
|
|
895
1295
|
let message: string;
|
|
896
|
-
|
|
1296
|
+
let toolCalls:
|
|
897
1297
|
| Array<{ toolName: string; arguments: Record<string, unknown> }>
|
|
898
1298
|
| undefined = undefined;
|
|
1299
|
+
let responsePrompt: string;
|
|
1300
|
+
let availableTools: Array<{
|
|
1301
|
+
id: string;
|
|
1302
|
+
name: string;
|
|
1303
|
+
description?: string;
|
|
1304
|
+
parameters?: unknown;
|
|
1305
|
+
}> = [];
|
|
1306
|
+
let responseSchema: StructuredSchema | undefined;
|
|
1307
|
+
let nextStep: Step<TContext, TData> | undefined;
|
|
1308
|
+
|
|
1309
|
+
// Get last user message (needed for both route and completion handling)
|
|
1310
|
+
const lastUserMessage = getLastMessageFromHistory(history);
|
|
899
1311
|
|
|
900
1312
|
if (selectedRoute && !isRouteComplete) {
|
|
901
|
-
let nextStep: Step<TContext>;
|
|
902
|
-
|
|
903
1313
|
// If we have a selected step from the combined routing decision, use it
|
|
904
1314
|
if (selectedStep) {
|
|
905
1315
|
nextStep = selectedStep;
|
|
@@ -933,115 +1343,343 @@ export class Agent<TContext = unknown> {
|
|
|
933
1343
|
const lastUserMessage = getLastMessageFromHistory(history);
|
|
934
1344
|
|
|
935
1345
|
// Build response schema for this route (with collect fields from step)
|
|
936
|
-
|
|
1346
|
+
responseSchema = this.responseEngine.responseSchemaForRoute(
|
|
937
1347
|
selectedRoute,
|
|
938
|
-
nextStep
|
|
1348
|
+
nextStep,
|
|
1349
|
+
this.schema
|
|
939
1350
|
);
|
|
940
1351
|
|
|
941
1352
|
// Build response prompt
|
|
942
|
-
|
|
1353
|
+
responsePrompt = await this.responseEngine.buildResponsePrompt({
|
|
1354
|
+
route: selectedRoute,
|
|
1355
|
+
currentStep: nextStep,
|
|
1356
|
+
rules: selectedRoute.getRules(),
|
|
1357
|
+
prohibitions: selectedRoute.getProhibitions(),
|
|
1358
|
+
directives: responseDirectives,
|
|
1359
|
+
history,
|
|
1360
|
+
lastMessage: lastUserMessage,
|
|
1361
|
+
agentOptions: this.options,
|
|
1362
|
+
// Combine agent and route properties according to the specified logic
|
|
1363
|
+
combinedGuidelines: [
|
|
1364
|
+
...this.getGuidelines(),
|
|
1365
|
+
...selectedRoute.getGuidelines(),
|
|
1366
|
+
],
|
|
1367
|
+
combinedTerms: this.mergeTerms(
|
|
1368
|
+
this.getTerms(),
|
|
1369
|
+
selectedRoute.getTerms()
|
|
1370
|
+
),
|
|
1371
|
+
context: effectiveContext,
|
|
1372
|
+
session,
|
|
1373
|
+
agentSchema: this.schema,
|
|
1374
|
+
});
|
|
1375
|
+
|
|
1376
|
+
// Collect available tools for AI
|
|
1377
|
+
availableTools = this.collectAvailableTools(
|
|
943
1378
|
selectedRoute,
|
|
944
|
-
nextStep
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
1379
|
+
nextStep
|
|
1380
|
+
);
|
|
1381
|
+
} else {
|
|
1382
|
+
// No route selected - generate basic response without route context
|
|
1383
|
+
logger.debug(`[Agent] No route selected, generating basic response`);
|
|
1384
|
+
|
|
1385
|
+
// Build basic response prompt without route context
|
|
1386
|
+
responsePrompt = await this.responseEngine.buildFallbackPrompt({
|
|
948
1387
|
history,
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1388
|
+
agentOptions: this.options,
|
|
1389
|
+
terms: this.getTerms(),
|
|
1390
|
+
guidelines: this.getGuidelines(),
|
|
1391
|
+
context: effectiveContext,
|
|
1392
|
+
session,
|
|
1393
|
+
});
|
|
1394
|
+
|
|
1395
|
+
// Use agent-level tools only
|
|
1396
|
+
availableTools = this.collectAvailableTools();
|
|
1397
|
+
responseSchema = undefined;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
// Generate message using AI provider (common for both route and no-route cases)
|
|
1401
|
+
const result = await this.options.provider.generateMessage({
|
|
1402
|
+
prompt: responsePrompt,
|
|
1403
|
+
history,
|
|
1404
|
+
context: effectiveContext,
|
|
1405
|
+
tools: availableTools,
|
|
1406
|
+
signal,
|
|
1407
|
+
parameters: responseSchema
|
|
1408
|
+
? {
|
|
1409
|
+
jsonSchema: responseSchema,
|
|
1410
|
+
schemaName: "response_output",
|
|
1411
|
+
}
|
|
1412
|
+
: undefined,
|
|
1413
|
+
});
|
|
1414
|
+
|
|
1415
|
+
message = result.structured?.message || result.message;
|
|
1416
|
+
|
|
1417
|
+
// Process dynamic tool calls from AI response (common for both route and no-route cases)
|
|
1418
|
+
if (result.structured?.toolCalls) {
|
|
1419
|
+
toolCalls = result.structured.toolCalls;
|
|
1420
|
+
|
|
1421
|
+
// Execute dynamic tool calls
|
|
1422
|
+
if (toolCalls.length > 0) {
|
|
1423
|
+
logger.debug(
|
|
1424
|
+
`[Agent] Executing ${toolCalls.length} dynamic tool calls`
|
|
1425
|
+
);
|
|
1426
|
+
|
|
1427
|
+
for (const toolCall of toolCalls) {
|
|
1428
|
+
const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
|
|
1429
|
+
if (!tool) {
|
|
1430
|
+
logger.warn(`[Agent] Tool not found: ${toolCall.toolName}`);
|
|
1431
|
+
continue;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
const toolExecutor = new ToolExecutor<TContext, TData>();
|
|
1435
|
+
const toolResult = await toolExecutor.executeTool({
|
|
1436
|
+
tool: tool,
|
|
1437
|
+
context: effectiveContext,
|
|
1438
|
+
updateContext: this.updateContext.bind(this),
|
|
1439
|
+
updateData: this.updateCollectedData.bind(this),
|
|
1440
|
+
history,
|
|
1441
|
+
data: session.data,
|
|
1442
|
+
toolArguments: toolCall.arguments,
|
|
1443
|
+
});
|
|
1444
|
+
|
|
1445
|
+
// Update context with tool results
|
|
1446
|
+
if (toolResult.contextUpdate) {
|
|
1447
|
+
await this.updateContext(
|
|
1448
|
+
toolResult.contextUpdate as Partial<TContext>
|
|
1449
|
+
);
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
// Update collected data with tool results
|
|
1453
|
+
if (toolResult.dataUpdate) {
|
|
1454
|
+
session = await this.updateData(session, toolResult.dataUpdate as Partial<TData>);
|
|
1455
|
+
logger.debug(
|
|
1456
|
+
`[Agent] Tool updated collected data:`,
|
|
1457
|
+
toolResult.dataUpdate
|
|
1458
|
+
);
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
logger.debug(
|
|
1462
|
+
`[Agent] Executed dynamic tool: ${toolResult.toolName} (success: ${toolResult.success})`
|
|
1463
|
+
);
|
|
955
1464
|
}
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
// TOOL LOOP: Allow AI to make follow-up tool calls after initial tool execution
|
|
1469
|
+
const MAX_TOOL_LOOPS = 5;
|
|
1470
|
+
let toolLoopCount = 0;
|
|
1471
|
+
let hasToolCalls = toolCalls && toolCalls.length > 0;
|
|
1472
|
+
|
|
1473
|
+
while (hasToolCalls && toolLoopCount < MAX_TOOL_LOOPS) {
|
|
1474
|
+
toolLoopCount++;
|
|
1475
|
+
logger.debug(
|
|
1476
|
+
`[Agent] Starting tool loop ${toolLoopCount}/${MAX_TOOL_LOOPS}`
|
|
956
1477
|
);
|
|
957
1478
|
|
|
958
|
-
//
|
|
959
|
-
const
|
|
1479
|
+
// Add tool execution results to history so AI knows what happened
|
|
1480
|
+
const toolResultsEvents: Event[] = [];
|
|
1481
|
+
for (const toolCall of toolCalls || []) {
|
|
1482
|
+
const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
|
|
1483
|
+
if (tool) {
|
|
1484
|
+
toolResultsEvents.push({
|
|
1485
|
+
kind: EventKind.TOOL,
|
|
1486
|
+
source: MessageRole.AGENT,
|
|
1487
|
+
timestamp: new Date().toISOString(),
|
|
1488
|
+
data: {
|
|
1489
|
+
tool_calls: [
|
|
1490
|
+
{
|
|
1491
|
+
tool_id: toolCall.toolName,
|
|
1492
|
+
arguments: toolCall.arguments,
|
|
1493
|
+
result: {
|
|
1494
|
+
data: "Tool executed successfully",
|
|
1495
|
+
},
|
|
1496
|
+
},
|
|
1497
|
+
],
|
|
1498
|
+
},
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
// Create updated history with tool results
|
|
1504
|
+
const updatedHistory = [...history, ...toolResultsEvents];
|
|
1505
|
+
|
|
1506
|
+
// Make follow-up AI call to see if more tools are needed
|
|
1507
|
+
const followUpResult = await this.options.provider.generateMessage({
|
|
960
1508
|
prompt: responsePrompt,
|
|
961
|
-
history,
|
|
1509
|
+
history: updatedHistory,
|
|
962
1510
|
context: effectiveContext,
|
|
963
|
-
|
|
1511
|
+
tools: availableTools,
|
|
964
1512
|
parameters: {
|
|
965
|
-
jsonSchema: responseSchema,
|
|
966
|
-
schemaName: "
|
|
1513
|
+
jsonSchema: responseSchema as StructuredSchema,
|
|
1514
|
+
schemaName: "tool_followup",
|
|
967
1515
|
},
|
|
1516
|
+
signal,
|
|
968
1517
|
});
|
|
969
1518
|
|
|
970
|
-
|
|
1519
|
+
// Check if follow-up call has more tool calls
|
|
1520
|
+
const followUpToolCalls = followUpResult.structured?.toolCalls;
|
|
1521
|
+
hasToolCalls = followUpToolCalls && followUpToolCalls.length > 0;
|
|
1522
|
+
|
|
1523
|
+
if (hasToolCalls) {
|
|
1524
|
+
logger.debug(
|
|
1525
|
+
`[Agent] Follow-up call produced ${followUpToolCalls!.length
|
|
1526
|
+
} additional tool calls`
|
|
1527
|
+
);
|
|
1528
|
+
|
|
1529
|
+
// Execute the follow-up tool calls
|
|
1530
|
+
for (const toolCall of followUpToolCalls!) {
|
|
1531
|
+
const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
|
|
1532
|
+
if (!tool) {
|
|
1533
|
+
logger.warn(
|
|
1534
|
+
`[Agent] Tool not found in follow-up: ${toolCall.toolName}`
|
|
1535
|
+
);
|
|
1536
|
+
continue;
|
|
1537
|
+
}
|
|
971
1538
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1539
|
+
const toolExecutor = new ToolExecutor<TContext, TData>();
|
|
1540
|
+
const toolResult = await toolExecutor.executeTool({
|
|
1541
|
+
tool: tool,
|
|
1542
|
+
context: effectiveContext,
|
|
1543
|
+
updateContext: this.updateContext.bind(this),
|
|
1544
|
+
updateData: this.updateCollectedData.bind(this),
|
|
1545
|
+
history: updatedHistory,
|
|
1546
|
+
data: session.data,
|
|
1547
|
+
toolArguments: toolCall.arguments,
|
|
1548
|
+
});
|
|
1549
|
+
|
|
1550
|
+
// Update context with follow-up tool results
|
|
1551
|
+
if (toolResult.contextUpdate) {
|
|
1552
|
+
await this.updateContext(
|
|
1553
|
+
toolResult.contextUpdate as Partial<TContext>
|
|
1554
|
+
);
|
|
1555
|
+
}
|
|
978
1556
|
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1557
|
+
if (toolResult.dataUpdate) {
|
|
1558
|
+
session = await this.updateData(session, toolResult.dataUpdate as Partial<TData>);
|
|
1559
|
+
logger.debug(
|
|
1560
|
+
`[Agent] Follow-up tool updated collected data:`,
|
|
1561
|
+
toolResult.dataUpdate
|
|
1562
|
+
);
|
|
982
1563
|
}
|
|
1564
|
+
|
|
1565
|
+
logger.debug(
|
|
1566
|
+
`[Agent] Executed follow-up tool: ${toolResult.toolName} (success: ${toolResult.success})`
|
|
1567
|
+
);
|
|
983
1568
|
}
|
|
984
1569
|
|
|
985
|
-
//
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1570
|
+
// Update toolCalls for next iteration or final response
|
|
1571
|
+
toolCalls = followUpToolCalls;
|
|
1572
|
+
} else {
|
|
1573
|
+
logger.debug(
|
|
1574
|
+
`[Agent] Tool loop completed after ${toolLoopCount} iterations`
|
|
1575
|
+
);
|
|
1576
|
+
// Update final message and toolCalls from follow-up result if no more tools
|
|
1577
|
+
message = followUpResult.structured?.message || followUpResult.message;
|
|
1578
|
+
toolCalls = followUpToolCalls || [];
|
|
1579
|
+
break;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
if (toolLoopCount >= MAX_TOOL_LOOPS) {
|
|
1584
|
+
logger.warn(
|
|
1585
|
+
`[Agent] Tool loop limit reached (${MAX_TOOL_LOOPS}), stopping`
|
|
1586
|
+
);
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// Extract collected data from final response (only for route-based interactions)
|
|
1590
|
+
if (selectedRoute && result.structured && nextStep?.collect) {
|
|
1591
|
+
const collectedData: Record<string, unknown> = {};
|
|
1592
|
+
// The structured response includes both base fields and collected extraction fields
|
|
1593
|
+
const structuredData = result.structured as AgentStructuredResponse &
|
|
1594
|
+
Record<string, unknown>;
|
|
1595
|
+
|
|
1596
|
+
for (const field of nextStep.collect) {
|
|
1597
|
+
const fieldKey = String(field);
|
|
1598
|
+
if (fieldKey in structuredData) {
|
|
1599
|
+
collectedData[fieldKey] = structuredData[fieldKey];
|
|
989
1600
|
}
|
|
990
1601
|
}
|
|
991
1602
|
|
|
992
|
-
//
|
|
993
|
-
if (
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
await this.
|
|
999
|
-
|
|
1000
|
-
.contextUpdate as Partial<TContext>
|
|
1001
|
-
);
|
|
1603
|
+
// Merge collected data into session using agent-level data validation
|
|
1604
|
+
if (Object.keys(collectedData).length > 0) {
|
|
1605
|
+
// Update agent-level collected data with validation
|
|
1606
|
+
await this.updateCollectedData(collectedData as Partial<TData>);
|
|
1607
|
+
|
|
1608
|
+
// Update session with validated data
|
|
1609
|
+
session = await this.updateData(session, collectedData as Partial<TData>);
|
|
1610
|
+
logger.debug(`[Agent] Collected data:`, collectedData);
|
|
1002
1611
|
}
|
|
1003
|
-
}
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// Extract any additional data from structured response
|
|
1615
|
+
if (
|
|
1616
|
+
result.structured &&
|
|
1617
|
+
typeof result.structured === "object" &&
|
|
1618
|
+
"contextUpdate" in result.structured
|
|
1619
|
+
) {
|
|
1620
|
+
await this.updateContext(
|
|
1621
|
+
(result.structured as { contextUpdate?: Partial<TContext> })
|
|
1622
|
+
.contextUpdate as Partial<TContext>
|
|
1623
|
+
);
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
// Handle route completion if route is complete
|
|
1627
|
+
if (isRouteComplete) {
|
|
1004
1628
|
// Route is complete - generate completion message then check for onComplete transition
|
|
1005
|
-
const lastUserMessage = getLastMessageFromHistory(history);
|
|
1006
1629
|
|
|
1007
1630
|
// Get endStep spec from route
|
|
1008
|
-
const endStepSpec = selectedRoute
|
|
1631
|
+
const endStepSpec = selectedRoute!.endStepSpec;
|
|
1009
1632
|
|
|
1010
1633
|
// Create a temporary step for completion message generation using endStep configuration
|
|
1011
|
-
const completionStep = new Step<TContext>(
|
|
1012
|
-
|
|
1013
|
-
endStepSpec.
|
|
1014
|
-
|
|
1015
|
-
endStepSpec.
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
)
|
|
1634
|
+
const completionStep = new Step<TContext, TData>(selectedRoute!.id, {
|
|
1635
|
+
description: endStepSpec.description,
|
|
1636
|
+
id: endStepSpec.id || END_ROUTE_ID,
|
|
1637
|
+
collect: endStepSpec.collect,
|
|
1638
|
+
requires: endStepSpec.requires,
|
|
1639
|
+
prompt:
|
|
1640
|
+
endStepSpec.prompt ||
|
|
1641
|
+
"Summarize what was accomplished and confirm completion based on the conversation history and collected data",
|
|
1642
|
+
});
|
|
1643
|
+
|
|
1644
|
+
if (!selectedRoute) {
|
|
1645
|
+
throw new Error("Selected route is not defined");
|
|
1646
|
+
}
|
|
1022
1647
|
|
|
1023
1648
|
// Build response schema for completion
|
|
1024
1649
|
const responseSchema = this.responseEngine.responseSchemaForRoute(
|
|
1025
1650
|
selectedRoute,
|
|
1026
|
-
completionStep
|
|
1651
|
+
completionStep,
|
|
1652
|
+
this.schema
|
|
1027
1653
|
);
|
|
1654
|
+
const templateContext = {
|
|
1655
|
+
context: effectiveContext,
|
|
1656
|
+
session,
|
|
1657
|
+
history,
|
|
1658
|
+
};
|
|
1028
1659
|
|
|
1029
1660
|
// Build completion response prompt
|
|
1030
|
-
const completionPrompt = this.responseEngine.buildResponsePrompt(
|
|
1031
|
-
selectedRoute,
|
|
1032
|
-
completionStep,
|
|
1033
|
-
selectedRoute.getRules(),
|
|
1034
|
-
selectedRoute.getProhibitions(),
|
|
1035
|
-
undefined, // No directives for completion
|
|
1661
|
+
const completionPrompt = await this.responseEngine.buildResponsePrompt({
|
|
1662
|
+
route: selectedRoute,
|
|
1663
|
+
currentStep: completionStep,
|
|
1664
|
+
rules: selectedRoute.getRules(),
|
|
1665
|
+
prohibitions: selectedRoute.getProhibitions(),
|
|
1666
|
+
directives: undefined, // No directives for completion
|
|
1036
1667
|
history,
|
|
1037
|
-
lastUserMessage,
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1668
|
+
lastMessage: lastUserMessage,
|
|
1669
|
+
agentOptions: this.options,
|
|
1670
|
+
// Combine agent and route properties according to the specified logic
|
|
1671
|
+
combinedGuidelines: [
|
|
1672
|
+
...this.getGuidelines(),
|
|
1673
|
+
...selectedRoute.getGuidelines(),
|
|
1674
|
+
],
|
|
1675
|
+
combinedTerms: this.mergeTerms(
|
|
1676
|
+
this.getTerms(),
|
|
1677
|
+
selectedRoute.getTerms()
|
|
1678
|
+
),
|
|
1679
|
+
context: effectiveContext,
|
|
1680
|
+
session,
|
|
1681
|
+
agentSchema: this.schema,
|
|
1682
|
+
});
|
|
1045
1683
|
|
|
1046
1684
|
// Generate completion message using AI provider
|
|
1047
1685
|
const completionResult = await this.options.provider.generateMessage({
|
|
@@ -1076,12 +1714,16 @@ export class Agent<TContext = unknown> {
|
|
|
1076
1714
|
);
|
|
1077
1715
|
|
|
1078
1716
|
if (targetRoute) {
|
|
1717
|
+
const renderedCondition = await render(
|
|
1718
|
+
transitionConfig.condition,
|
|
1719
|
+
templateContext
|
|
1720
|
+
);
|
|
1079
1721
|
// Set pending transition in session
|
|
1080
1722
|
session = {
|
|
1081
1723
|
...session,
|
|
1082
1724
|
pendingTransition: {
|
|
1083
1725
|
targetRouteId: targetRoute.id,
|
|
1084
|
-
condition:
|
|
1726
|
+
condition: renderedCondition,
|
|
1085
1727
|
reason: "route_complete",
|
|
1086
1728
|
},
|
|
1087
1729
|
};
|
|
@@ -1102,18 +1744,14 @@ export class Agent<TContext = unknown> {
|
|
|
1102
1744
|
);
|
|
1103
1745
|
} else {
|
|
1104
1746
|
// Fallback: No routes defined, generate a simple response
|
|
1105
|
-
const fallbackPrompt =
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
.addGlossary(this.terms)
|
|
1114
|
-
.addGuidelines(this.guidelines)
|
|
1115
|
-
.addCapabilities(this.capabilities)
|
|
1116
|
-
.build();
|
|
1747
|
+
const fallbackPrompt = await this.responseEngine.buildFallbackPrompt({
|
|
1748
|
+
history,
|
|
1749
|
+
agentOptions: this.options,
|
|
1750
|
+
terms: this.terms,
|
|
1751
|
+
guidelines: this.guidelines,
|
|
1752
|
+
context: effectiveContext,
|
|
1753
|
+
session,
|
|
1754
|
+
});
|
|
1117
1755
|
|
|
1118
1756
|
const result = await this.options.provider.generateMessage({
|
|
1119
1757
|
prompt: fallbackPrompt,
|
|
@@ -1148,6 +1786,28 @@ export class Agent<TContext = unknown> {
|
|
|
1148
1786
|
);
|
|
1149
1787
|
}
|
|
1150
1788
|
|
|
1789
|
+
// Execute finalize function
|
|
1790
|
+
if (session.currentRoute && session.currentStep) {
|
|
1791
|
+
const currentRoute = this.routes.find(
|
|
1792
|
+
(r) => r.id === session.currentRoute?.id
|
|
1793
|
+
);
|
|
1794
|
+
if (currentRoute) {
|
|
1795
|
+
const currentStep = currentRoute.getStep(session.currentStep.id);
|
|
1796
|
+
if (currentStep?.finalize) {
|
|
1797
|
+
logger.debug(
|
|
1798
|
+
`[Agent] Executing finalize for step: ${currentStep.id}`
|
|
1799
|
+
);
|
|
1800
|
+
await this.executePrepareFinalize(
|
|
1801
|
+
currentStep.finalize,
|
|
1802
|
+
effectiveContext,
|
|
1803
|
+
session.data,
|
|
1804
|
+
currentRoute,
|
|
1805
|
+
currentStep
|
|
1806
|
+
);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1151
1811
|
// Update current session if we have one
|
|
1152
1812
|
if (this.currentSession) {
|
|
1153
1813
|
this.currentSession = session;
|
|
@@ -1164,86 +1824,268 @@ export class Agent<TContext = unknown> {
|
|
|
1164
1824
|
/**
|
|
1165
1825
|
* Get all routes
|
|
1166
1826
|
*/
|
|
1167
|
-
getRoutes(): Route<TContext,
|
|
1827
|
+
getRoutes(): Route<TContext, TData>[] {
|
|
1168
1828
|
return [...this.routes];
|
|
1169
1829
|
}
|
|
1170
1830
|
|
|
1171
1831
|
/**
|
|
1172
1832
|
* Get all terms
|
|
1173
1833
|
*/
|
|
1174
|
-
getTerms(): Term[] {
|
|
1834
|
+
getTerms(): Term<TContext, TData>[] {
|
|
1175
1835
|
return [...this.terms];
|
|
1176
1836
|
}
|
|
1177
1837
|
|
|
1178
1838
|
/**
|
|
1179
|
-
* Get all
|
|
1839
|
+
* Get all tools
|
|
1180
1840
|
*/
|
|
1181
|
-
|
|
1182
|
-
return [...this.
|
|
1841
|
+
getTools(): Tool<TContext, TData, unknown[], unknown>[] {
|
|
1842
|
+
return [...this.tools];
|
|
1183
1843
|
}
|
|
1184
1844
|
|
|
1185
1845
|
/**
|
|
1186
|
-
*
|
|
1846
|
+
* Find an available tool by name for the given route
|
|
1847
|
+
* Route-level tools take precedence over agent-level tools
|
|
1848
|
+
* @private
|
|
1187
1849
|
*/
|
|
1188
|
-
|
|
1189
|
-
|
|
1850
|
+
private findAvailableTool(
|
|
1851
|
+
toolName: string,
|
|
1852
|
+
route?: Route<TContext, TData>
|
|
1853
|
+
): Tool<TContext, TData, unknown[], unknown> | undefined {
|
|
1854
|
+
// Check route-level tools first (if route provided)
|
|
1855
|
+
if (route) {
|
|
1856
|
+
const routeTool = route
|
|
1857
|
+
.getTools()
|
|
1858
|
+
.find((tool) => tool.id === toolName || tool.name === toolName);
|
|
1859
|
+
if (routeTool) return routeTool;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
// Fall back to agent-level tools
|
|
1863
|
+
return this.tools.find(
|
|
1864
|
+
(tool) => tool.id === toolName || tool.name === toolName
|
|
1865
|
+
);
|
|
1190
1866
|
}
|
|
1191
1867
|
|
|
1192
1868
|
/**
|
|
1193
|
-
*
|
|
1869
|
+
* Collect all available tools for the given route and step context
|
|
1870
|
+
* @private
|
|
1194
1871
|
*/
|
|
1195
|
-
|
|
1196
|
-
|
|
1872
|
+
private collectAvailableTools(
|
|
1873
|
+
route?: Route<TContext, TData>,
|
|
1874
|
+
step?: Step<TContext, TData>
|
|
1875
|
+
): Array<{
|
|
1876
|
+
id: string;
|
|
1877
|
+
name: string;
|
|
1878
|
+
description?: string;
|
|
1879
|
+
parameters?: unknown;
|
|
1880
|
+
}> {
|
|
1881
|
+
const availableTools = new Map<
|
|
1882
|
+
string,
|
|
1883
|
+
Tool<TContext, TData, unknown[], unknown>
|
|
1884
|
+
>();
|
|
1885
|
+
|
|
1886
|
+
// Add agent-level tools
|
|
1887
|
+
this.tools.forEach((tool) => {
|
|
1888
|
+
availableTools.set(tool.id, tool);
|
|
1889
|
+
});
|
|
1890
|
+
|
|
1891
|
+
// Add route-level tools (these take precedence)
|
|
1892
|
+
if (route) {
|
|
1893
|
+
route.getTools().forEach((tool) => {
|
|
1894
|
+
availableTools.set(tool.id, tool);
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
// Filter by step-level allowed tools if specified
|
|
1899
|
+
if (step?.tools) {
|
|
1900
|
+
const allowedToolIds = new Set<string>();
|
|
1901
|
+
const stepTools: Tool<TContext, TData, unknown[], unknown>[] = [];
|
|
1902
|
+
|
|
1903
|
+
for (const toolRef of step.tools) {
|
|
1904
|
+
if (typeof toolRef === "string") {
|
|
1905
|
+
// Reference to registered tool
|
|
1906
|
+
allowedToolIds.add(toolRef);
|
|
1907
|
+
} else {
|
|
1908
|
+
// Inline tool definition
|
|
1909
|
+
if (toolRef.id) {
|
|
1910
|
+
allowedToolIds.add(toolRef.id);
|
|
1911
|
+
stepTools.push(toolRef);
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
// If step specifies tools, only include those
|
|
1917
|
+
if (allowedToolIds.size > 0) {
|
|
1918
|
+
const filteredTools = new Map<
|
|
1919
|
+
string,
|
|
1920
|
+
Tool<TContext, TData, unknown[], unknown>
|
|
1921
|
+
>();
|
|
1922
|
+
for (const toolId of allowedToolIds) {
|
|
1923
|
+
const tool = availableTools.get(toolId);
|
|
1924
|
+
if (tool) {
|
|
1925
|
+
filteredTools.set(toolId, tool);
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
// Add inline tools
|
|
1929
|
+
stepTools.forEach((tool) => {
|
|
1930
|
+
if (tool.id) {
|
|
1931
|
+
filteredTools.set(tool.id, tool);
|
|
1932
|
+
}
|
|
1933
|
+
});
|
|
1934
|
+
availableTools.clear();
|
|
1935
|
+
filteredTools.forEach((tool, id) => availableTools.set(id, tool));
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
// Convert to the format expected by AI providers
|
|
1940
|
+
return Array.from(availableTools.values()).map((tool) => ({
|
|
1941
|
+
id: tool.id,
|
|
1942
|
+
name: tool.name || tool.id,
|
|
1943
|
+
description: tool.description,
|
|
1944
|
+
parameters: tool.parameters,
|
|
1945
|
+
}));
|
|
1197
1946
|
}
|
|
1198
1947
|
|
|
1199
1948
|
/**
|
|
1200
|
-
*
|
|
1949
|
+
* Execute a prepare or finalize function/tool
|
|
1950
|
+
* @private
|
|
1201
1951
|
*/
|
|
1202
|
-
|
|
1203
|
-
|
|
1952
|
+
private async executePrepareFinalize(
|
|
1953
|
+
prepareOrFinalize:
|
|
1954
|
+
| string
|
|
1955
|
+
| Tool<TContext, TData, unknown[], unknown>
|
|
1956
|
+
| ((context: TContext, data?: Partial<TData>) => void | Promise<void>)
|
|
1957
|
+
| undefined,
|
|
1958
|
+
context: TContext,
|
|
1959
|
+
data?: Partial<TData>,
|
|
1960
|
+
route?: Route<TContext, TData>,
|
|
1961
|
+
step?: Step<TContext, TData>
|
|
1962
|
+
): Promise<void> {
|
|
1963
|
+
if (!prepareOrFinalize) return;
|
|
1964
|
+
|
|
1965
|
+
if (typeof prepareOrFinalize === "function") {
|
|
1966
|
+
// It's a function - call it directly
|
|
1967
|
+
await prepareOrFinalize(context, data);
|
|
1968
|
+
} else {
|
|
1969
|
+
// It's a tool reference - find and execute the tool
|
|
1970
|
+
let tool: Tool<TContext, TData, unknown[], unknown> | undefined;
|
|
1971
|
+
|
|
1972
|
+
if (typeof prepareOrFinalize === "string") {
|
|
1973
|
+
// Tool ID - find it in available tools
|
|
1974
|
+
const availableTools = new Map<
|
|
1975
|
+
string,
|
|
1976
|
+
Tool<TContext, TData, unknown[], unknown>
|
|
1977
|
+
>();
|
|
1978
|
+
|
|
1979
|
+
// Add agent-level tools
|
|
1980
|
+
this.tools.forEach((t) => {
|
|
1981
|
+
availableTools.set(t.id, t);
|
|
1982
|
+
});
|
|
1983
|
+
|
|
1984
|
+
// Add route-level tools
|
|
1985
|
+
if (route) {
|
|
1986
|
+
route.getTools().forEach((t) => {
|
|
1987
|
+
availableTools.set(t.id, t);
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
// Add step-level tools
|
|
1992
|
+
if (step?.tools) {
|
|
1993
|
+
for (const toolRef of step.tools) {
|
|
1994
|
+
if (typeof toolRef === "string") {
|
|
1995
|
+
// Keep as is
|
|
1996
|
+
} else if (toolRef.id) {
|
|
1997
|
+
availableTools.set(toolRef.id, toolRef);
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
tool = availableTools.get(prepareOrFinalize);
|
|
2003
|
+
} else {
|
|
2004
|
+
// Tool object - use directly
|
|
2005
|
+
tool = prepareOrFinalize;
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
if (tool) {
|
|
2009
|
+
const toolExecutor = new ToolExecutor<TContext, TData>();
|
|
2010
|
+
const result = await toolExecutor.executeTool({
|
|
2011
|
+
tool,
|
|
2012
|
+
context,
|
|
2013
|
+
updateContext: this.updateContext.bind(this),
|
|
2014
|
+
updateData: this.updateCollectedData.bind(this),
|
|
2015
|
+
history: [], // Empty history for prepare/finalize
|
|
2016
|
+
data,
|
|
2017
|
+
});
|
|
2018
|
+
|
|
2019
|
+
if (!result.success) {
|
|
2020
|
+
logger.error(
|
|
2021
|
+
`[Agent] Tool execution failed in prepare/finalize: ${result.error}`
|
|
2022
|
+
);
|
|
2023
|
+
throw new Error(`Tool execution failed: ${result.error}`);
|
|
2024
|
+
}
|
|
2025
|
+
} else {
|
|
2026
|
+
logger.warn(
|
|
2027
|
+
`[Agent] Tool not found for prepare/finalize: ${typeof prepareOrFinalize === "string"
|
|
2028
|
+
? prepareOrFinalize
|
|
2029
|
+
: "inline tool"
|
|
2030
|
+
}`
|
|
2031
|
+
);
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
1204
2034
|
}
|
|
1205
2035
|
|
|
1206
2036
|
/**
|
|
1207
|
-
*
|
|
2037
|
+
* Get all guidelines
|
|
1208
2038
|
*/
|
|
1209
|
-
|
|
1210
|
-
return this.
|
|
2039
|
+
getGuidelines(): Guideline<TContext, TData>[] {
|
|
2040
|
+
return [...this.guidelines];
|
|
1211
2041
|
}
|
|
1212
2042
|
|
|
1213
2043
|
/**
|
|
1214
|
-
* Get
|
|
1215
|
-
* @param routeId - Route ID to check
|
|
1216
|
-
* @returns Filtered domains object, or all domains if route has no restrictions
|
|
2044
|
+
* Get the agent's knowledge base
|
|
1217
2045
|
*/
|
|
1218
|
-
|
|
1219
|
-
|
|
2046
|
+
getKnowledgeBase(): Record<string, unknown> {
|
|
2047
|
+
return { ...this.knowledgeBase };
|
|
2048
|
+
}
|
|
1220
2049
|
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
2050
|
+
/**
|
|
2051
|
+
* Merge terms with route-specific taking precedence on conflicts
|
|
2052
|
+
* @private
|
|
2053
|
+
*/
|
|
2054
|
+
private mergeTerms(
|
|
2055
|
+
agentTerms: Term<TContext, TData>[],
|
|
2056
|
+
routeTerms: Term<TContext, TData>[]
|
|
2057
|
+
): Term<TContext, TData>[] {
|
|
2058
|
+
const merged = new Map<string, Term<TContext, TData>>();
|
|
2059
|
+
|
|
2060
|
+
// Add agent terms first
|
|
2061
|
+
agentTerms.forEach((term) => {
|
|
2062
|
+
const name =
|
|
2063
|
+
typeof term.name === "string" ? term.name : term.name.toString();
|
|
2064
|
+
merged.set(name, term);
|
|
2065
|
+
});
|
|
2066
|
+
|
|
2067
|
+
// Add route terms (these take precedence)
|
|
2068
|
+
routeTerms.forEach((term) => {
|
|
2069
|
+
const name =
|
|
2070
|
+
typeof term.name === "string" ? term.name : term.name.toString();
|
|
2071
|
+
merged.set(name, term);
|
|
2072
|
+
});
|
|
1225
2073
|
|
|
1226
|
-
|
|
1227
|
-
return this.domainRegistry.getFiltered(allowedDomains);
|
|
2074
|
+
return Array.from(merged.values());
|
|
1228
2075
|
}
|
|
1229
2076
|
|
|
1230
2077
|
/**
|
|
1231
|
-
* Get
|
|
1232
|
-
* @param routeTitle - Route title to check
|
|
1233
|
-
* @returns Filtered domains object, or all domains if route has no restrictions
|
|
2078
|
+
* Get the persistence manager (if configured)
|
|
1234
2079
|
*/
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
const route = this.routes.find((r) => r.title === routeTitle);
|
|
1239
|
-
|
|
1240
|
-
if (!route) {
|
|
1241
|
-
// Route not found, return all domains
|
|
1242
|
-
return this.domainRegistry.all();
|
|
1243
|
-
}
|
|
2080
|
+
getPersistenceManager(): PersistenceManager<TData> | undefined {
|
|
2081
|
+
return this.persistenceManager;
|
|
2082
|
+
}
|
|
1244
2083
|
|
|
1245
|
-
|
|
1246
|
-
|
|
2084
|
+
/**
|
|
2085
|
+
* Check if persistence is enabled
|
|
2086
|
+
*/
|
|
2087
|
+
hasPersistence(): boolean {
|
|
2088
|
+
return this.persistenceManager !== undefined;
|
|
1247
2089
|
}
|
|
1248
2090
|
|
|
1249
2091
|
/**
|
|
@@ -1269,21 +2111,20 @@ export class Agent<TContext = unknown> {
|
|
|
1269
2111
|
}
|
|
1270
2112
|
|
|
1271
2113
|
/**
|
|
1272
|
-
* Get collected data from current session
|
|
2114
|
+
* Get collected data from current session or agent-level collected data
|
|
1273
2115
|
* @param routeId - Optional route ID to get data for (uses current route if not provided)
|
|
1274
|
-
* @returns The collected data from the current session
|
|
2116
|
+
* @returns The collected data from the current session or agent-level data
|
|
1275
2117
|
*/
|
|
1276
|
-
getData
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
return (
|
|
1282
|
-
(this.currentSession.dataByRoute?.[routeId] as Partial<TData>) ||
|
|
1283
|
-
({} as Partial<TData>)
|
|
1284
|
-
);
|
|
2118
|
+
getData(): Partial<TData> {
|
|
2119
|
+
// If we have a current session, use session data
|
|
2120
|
+
if (this.currentSession) {
|
|
2121
|
+
// With agent-level data, all routes share the same data structure
|
|
2122
|
+
// No need for route-specific data access
|
|
2123
|
+
return (this.currentSession.data) || {};
|
|
1285
2124
|
}
|
|
1286
|
-
|
|
2125
|
+
|
|
2126
|
+
// Otherwise, return agent-level collected data
|
|
2127
|
+
return this.getCollectedData();
|
|
1287
2128
|
}
|
|
1288
2129
|
|
|
1289
2130
|
/**
|
|
@@ -1303,11 +2144,12 @@ export class Agent<TContext = unknown> {
|
|
|
1303
2144
|
* const nextResponse = await agent.respond({ history, session: updatedSession });
|
|
1304
2145
|
* }
|
|
1305
2146
|
*/
|
|
1306
|
-
nextStepRoute(
|
|
2147
|
+
async nextStepRoute(
|
|
1307
2148
|
routeIdOrTitle: string,
|
|
1308
|
-
session?: SessionState
|
|
1309
|
-
condition?:
|
|
1310
|
-
|
|
2149
|
+
session?: SessionState<TData>,
|
|
2150
|
+
condition?: Template<TContext, TData>,
|
|
2151
|
+
history?: Event[]
|
|
2152
|
+
): Promise<SessionState<TData>> {
|
|
1311
2153
|
const targetSession = session || this.currentSession;
|
|
1312
2154
|
|
|
1313
2155
|
if (!targetSession) {
|
|
@@ -1328,13 +2170,20 @@ export class Agent<TContext = unknown> {
|
|
|
1328
2170
|
.join(", ")}`
|
|
1329
2171
|
);
|
|
1330
2172
|
}
|
|
2173
|
+
const templateContext = {
|
|
2174
|
+
context: this.context,
|
|
2175
|
+
session,
|
|
2176
|
+
history,
|
|
2177
|
+
data: this.currentSession?.data,
|
|
2178
|
+
};
|
|
2179
|
+
const renderedCondition = await render(condition, templateContext);
|
|
1331
2180
|
|
|
1332
|
-
const updatedSession: SessionState = {
|
|
2181
|
+
const updatedSession: SessionState<TData> = {
|
|
1333
2182
|
...targetSession,
|
|
1334
2183
|
pendingTransition: {
|
|
1335
2184
|
targetRouteId: targetRoute.id,
|
|
1336
|
-
condition,
|
|
1337
|
-
reason: "
|
|
2185
|
+
condition: renderedCondition,
|
|
2186
|
+
reason: "route_complete",
|
|
1338
2187
|
},
|
|
1339
2188
|
};
|
|
1340
2189
|
|
|
@@ -1344,9 +2193,65 @@ export class Agent<TContext = unknown> {
|
|
|
1344
2193
|
}
|
|
1345
2194
|
|
|
1346
2195
|
logger.debug(
|
|
1347
|
-
`[Agent] Set pending
|
|
2196
|
+
`[Agent] Set pending transition to route: ${targetRoute.title}`
|
|
1348
2197
|
);
|
|
1349
2198
|
|
|
1350
2199
|
return updatedSession;
|
|
1351
2200
|
}
|
|
2201
|
+
|
|
2202
|
+
/**
|
|
2203
|
+
* Simplified respond method using SessionManager
|
|
2204
|
+
* Automatically manages conversation history through the session
|
|
2205
|
+
*/
|
|
2206
|
+
async chat(
|
|
2207
|
+
message?: string,
|
|
2208
|
+
options?: {
|
|
2209
|
+
history?: History; // Optional: override session history for this response
|
|
2210
|
+
contextOverride?: Partial<TContext>;
|
|
2211
|
+
signal?: AbortSignal;
|
|
2212
|
+
}
|
|
2213
|
+
): Promise<AgentResponse<TData>> {
|
|
2214
|
+
// Determine which history to use
|
|
2215
|
+
let history: History;
|
|
2216
|
+
if (options?.history) {
|
|
2217
|
+
// Use provided history for this response only
|
|
2218
|
+
history = options.history;
|
|
2219
|
+
} else {
|
|
2220
|
+
// Add user message to session history if provided
|
|
2221
|
+
if (message) {
|
|
2222
|
+
await this.session.addMessage("user", message);
|
|
2223
|
+
}
|
|
2224
|
+
history = this.session.getHistory();
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
// Get or create session
|
|
2228
|
+
let session = await this.session.getOrCreate();
|
|
2229
|
+
|
|
2230
|
+
// Merge agent's collected data into session (agent data takes precedence)
|
|
2231
|
+
if (Object.keys(this.collectedData).length > 0) {
|
|
2232
|
+
session = mergeCollected(session, this.collectedData);
|
|
2233
|
+
// Update the session manager with the merged data
|
|
2234
|
+
await this.session.setData(this.collectedData);
|
|
2235
|
+
logger.debug("[Agent] Merged agent collected data into chat session:", this.collectedData);
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
// Use existing respond method with session-managed history
|
|
2239
|
+
const result = await this.respond({
|
|
2240
|
+
history,
|
|
2241
|
+
session,
|
|
2242
|
+
contextOverride: options?.contextOverride,
|
|
2243
|
+
signal: options?.signal,
|
|
2244
|
+
});
|
|
2245
|
+
|
|
2246
|
+
// Add agent response to session history (only if not using override history)
|
|
2247
|
+
if (!options?.history) {
|
|
2248
|
+
await this.session.addMessage("assistant", result.message);
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
// Ensure the result includes the current session
|
|
2252
|
+
return {
|
|
2253
|
+
...result,
|
|
2254
|
+
session: result.session || this.session.current,
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
1352
2257
|
}
|