@falai/agent 3.4.5 → 4.0.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 +41 -34
- package/dist/cjs/core/Agent.d.ts +22 -378
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +104 -1178
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/CompactionEngine.d.ts.map +1 -1
- package/dist/cjs/core/CompactionEngine.js +5 -3
- package/dist/cjs/core/CompactionEngine.js.map +1 -1
- package/dist/cjs/core/FlowSpec.d.ts +136 -0
- package/dist/cjs/core/FlowSpec.d.ts.map +1 -0
- package/dist/cjs/core/FlowSpec.js +522 -0
- package/dist/cjs/core/FlowSpec.js.map +1 -0
- package/dist/cjs/core/Migrate.d.ts +38 -0
- package/dist/cjs/core/Migrate.d.ts.map +1 -0
- package/dist/cjs/core/Migrate.js +270 -0
- package/dist/cjs/core/Migrate.js.map +1 -0
- package/dist/cjs/core/Prompt.d.ts +54 -0
- package/dist/cjs/core/Prompt.d.ts.map +1 -0
- package/dist/cjs/core/Prompt.js +143 -0
- package/dist/cjs/core/Prompt.js.map +1 -0
- package/dist/cjs/core/Runner.d.ts +160 -0
- package/dist/cjs/core/Runner.d.ts.map +1 -0
- package/dist/cjs/core/Runner.js +1131 -0
- package/dist/cjs/core/Runner.js.map +1 -0
- package/dist/cjs/core/Speak.d.ts +37 -0
- package/dist/cjs/core/Speak.d.ts.map +1 -0
- package/dist/cjs/core/Speak.js +364 -0
- package/dist/cjs/core/Speak.js.map +1 -0
- package/dist/cjs/core/Understand.d.ts +28 -0
- package/dist/cjs/core/Understand.d.ts.map +1 -0
- package/dist/cjs/core/Understand.js +353 -0
- package/dist/cjs/core/Understand.js.map +1 -0
- package/dist/cjs/core/contracts.d.ts +122 -0
- package/dist/cjs/core/contracts.d.ts.map +1 -0
- package/dist/cjs/core/contracts.js +11 -0
- package/dist/cjs/core/contracts.js.map +1 -0
- package/dist/cjs/core/falai.d.ts +57 -0
- package/dist/cjs/core/falai.d.ts.map +1 -0
- package/dist/cjs/core/falai.js +43 -0
- package/dist/cjs/core/falai.js.map +1 -0
- package/dist/cjs/core/predicate.d.ts +9 -0
- package/dist/cjs/core/predicate.d.ts.map +1 -0
- package/dist/cjs/core/predicate.js +58 -0
- package/dist/cjs/core/predicate.js.map +1 -0
- package/dist/cjs/index.d.ts +26 -31
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +46 -68
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/persistence/MemoryStore.d.ts +15 -0
- package/dist/cjs/persistence/MemoryStore.d.ts.map +1 -0
- package/dist/cjs/persistence/MemoryStore.js +39 -0
- package/dist/cjs/persistence/MemoryStore.js.map +1 -0
- package/dist/cjs/persistence/MongoStore.d.ts +42 -0
- package/dist/cjs/persistence/MongoStore.d.ts.map +1 -0
- package/dist/cjs/persistence/MongoStore.js +60 -0
- package/dist/cjs/persistence/MongoStore.js.map +1 -0
- package/dist/cjs/persistence/OpenSearchStore.d.ts +86 -0
- package/dist/cjs/persistence/OpenSearchStore.d.ts.map +1 -0
- package/dist/cjs/persistence/OpenSearchStore.js +120 -0
- package/dist/cjs/persistence/OpenSearchStore.js.map +1 -0
- package/dist/cjs/persistence/PostgresStore.d.ts +41 -0
- package/dist/cjs/persistence/PostgresStore.d.ts.map +1 -0
- package/dist/cjs/persistence/PostgresStore.js +58 -0
- package/dist/cjs/persistence/PostgresStore.js.map +1 -0
- package/dist/cjs/persistence/PrismaStore.d.ts +65 -0
- package/dist/cjs/persistence/PrismaStore.d.ts.map +1 -0
- package/dist/cjs/persistence/PrismaStore.js +95 -0
- package/dist/cjs/persistence/PrismaStore.js.map +1 -0
- package/dist/cjs/persistence/RedisStore.d.ts +34 -0
- package/dist/cjs/persistence/RedisStore.d.ts.map +1 -0
- package/dist/cjs/persistence/RedisStore.js +61 -0
- package/dist/cjs/persistence/RedisStore.js.map +1 -0
- package/dist/cjs/persistence/SQLiteStore.d.ts +45 -0
- package/dist/cjs/persistence/SQLiteStore.d.ts.map +1 -0
- package/dist/cjs/persistence/SQLiteStore.js +74 -0
- package/dist/cjs/persistence/SQLiteStore.js.map +1 -0
- package/dist/cjs/persistence/sessionRow.d.ts +14 -0
- package/dist/cjs/persistence/sessionRow.d.ts.map +1 -0
- package/dist/cjs/persistence/sessionRow.js +50 -0
- package/dist/cjs/persistence/sessionRow.js.map +1 -0
- package/dist/cjs/providers/DeepSeekProvider.d.ts.map +1 -1
- package/dist/cjs/providers/DeepSeekProvider.js +8 -3
- package/dist/cjs/providers/DeepSeekProvider.js.map +1 -1
- package/dist/cjs/providers/GeminiProvider.d.ts +4 -3
- package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/cjs/providers/GeminiProvider.js +4 -3
- package/dist/cjs/providers/GeminiProvider.js.map +1 -1
- package/dist/cjs/providers/OpenAICompatibleProvider.d.ts +4 -0
- package/dist/cjs/providers/OpenAICompatibleProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenAICompatibleProvider.js +2 -0
- package/dist/cjs/providers/OpenAICompatibleProvider.js.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.js +2 -4
- package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
- package/dist/cjs/providers/ProviderAdapter.d.ts +11 -6
- package/dist/cjs/providers/ProviderAdapter.d.ts.map +1 -1
- package/dist/cjs/providers/ProviderAdapter.js +33 -10
- package/dist/cjs/providers/ProviderAdapter.js.map +1 -1
- package/dist/cjs/providers/ZaiProvider.d.ts +6 -4
- package/dist/cjs/providers/ZaiProvider.d.ts.map +1 -1
- package/dist/cjs/providers/ZaiProvider.js +6 -4
- package/dist/cjs/providers/ZaiProvider.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +153 -383
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/agent.js +1 -1
- package/dist/cjs/types/ai.d.ts +32 -1
- package/dist/cjs/types/ai.d.ts.map +1 -1
- package/dist/cjs/types/compaction.d.ts +3 -1
- package/dist/cjs/types/compaction.d.ts.map +1 -1
- package/dist/cjs/types/errors.d.ts +9 -12
- package/dist/cjs/types/errors.d.ts.map +1 -1
- package/dist/cjs/types/errors.js +14 -17
- package/dist/cjs/types/errors.js.map +1 -1
- package/dist/cjs/types/flow.d.ts +265 -513
- package/dist/cjs/types/flow.d.ts.map +1 -1
- package/dist/cjs/types/flow.js +7 -1
- package/dist/cjs/types/flow.js.map +1 -1
- package/dist/cjs/types/history.d.ts +7 -18
- package/dist/cjs/types/history.d.ts.map +1 -1
- package/dist/cjs/types/history.js.map +1 -1
- package/dist/cjs/types/index.d.ts +9 -15
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/cjs/types/index.js +4 -14
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/cjs/types/session.d.ts +94 -64
- package/dist/cjs/types/session.d.ts.map +1 -1
- package/dist/cjs/types/session.js +5 -1
- package/dist/cjs/types/session.js.map +1 -1
- package/dist/cjs/types/tool.d.ts +37 -207
- package/dist/cjs/types/tool.d.ts.map +1 -1
- package/dist/cjs/types/tool.js +5 -14
- package/dist/cjs/types/tool.js.map +1 -1
- package/dist/cjs/utils/clock.d.ts +28 -0
- package/dist/cjs/utils/clock.d.ts.map +1 -0
- package/dist/cjs/utils/clock.js +64 -0
- package/dist/cjs/utils/clock.js.map +1 -0
- package/dist/cjs/utils/duration.d.ts +11 -0
- package/dist/cjs/utils/duration.d.ts.map +1 -0
- package/dist/cjs/utils/duration.js +31 -0
- package/dist/cjs/utils/duration.js.map +1 -0
- package/dist/cjs/utils/history.d.ts +4 -1
- package/dist/cjs/utils/history.d.ts.map +1 -1
- package/dist/cjs/utils/history.js +2 -2
- package/dist/cjs/utils/history.js.map +1 -1
- package/dist/cjs/utils/index.d.ts +4 -10
- package/dist/cjs/utils/index.d.ts.map +1 -1
- package/dist/cjs/utils/index.js +14 -61
- package/dist/cjs/utils/index.js.map +1 -1
- package/dist/cjs/utils/json.d.ts +2 -0
- package/dist/cjs/utils/json.d.ts.map +1 -1
- package/dist/cjs/utils/json.js +5 -0
- package/dist/cjs/utils/json.js.map +1 -1
- package/dist/cjs/utils/outcomes.d.ts +48 -0
- package/dist/cjs/utils/outcomes.d.ts.map +1 -0
- package/dist/cjs/utils/outcomes.js +51 -0
- package/dist/cjs/utils/outcomes.js.map +1 -0
- package/dist/cjs/utils/schema.d.ts +50 -0
- package/dist/cjs/utils/schema.d.ts.map +1 -0
- package/dist/cjs/utils/schema.js +138 -0
- package/dist/cjs/utils/schema.js.map +1 -0
- package/dist/cjs/utils/streamingMessage.d.ts +3 -2
- package/dist/cjs/utils/streamingMessage.d.ts.map +1 -1
- package/dist/cjs/utils/streamingMessage.js +38 -4
- package/dist/cjs/utils/streamingMessage.js.map +1 -1
- package/dist/cjs/utils/template.d.ts +13 -149
- package/dist/cjs/utils/template.d.ts.map +1 -1
- package/dist/cjs/utils/template.js +31 -363
- package/dist/cjs/utils/template.js.map +1 -1
- package/dist/cjs/utils/usage.d.ts +19 -0
- package/dist/cjs/utils/usage.d.ts.map +1 -0
- package/dist/cjs/utils/usage.js +35 -0
- package/dist/cjs/utils/usage.js.map +1 -0
- package/dist/core/Agent.d.ts +22 -378
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +107 -1181
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/CompactionEngine.d.ts.map +1 -1
- package/dist/core/CompactionEngine.js +5 -3
- package/dist/core/CompactionEngine.js.map +1 -1
- package/dist/core/FlowSpec.d.ts +136 -0
- package/dist/core/FlowSpec.d.ts.map +1 -0
- package/dist/core/FlowSpec.js +516 -0
- package/dist/core/FlowSpec.js.map +1 -0
- package/dist/core/Migrate.d.ts +38 -0
- package/dist/core/Migrate.d.ts.map +1 -0
- package/dist/core/Migrate.js +264 -0
- package/dist/core/Migrate.js.map +1 -0
- package/dist/core/Prompt.d.ts +54 -0
- package/dist/core/Prompt.d.ts.map +1 -0
- package/dist/core/Prompt.js +133 -0
- package/dist/core/Prompt.js.map +1 -0
- package/dist/core/Runner.d.ts +160 -0
- package/dist/core/Runner.d.ts.map +1 -0
- package/dist/core/Runner.js +1127 -0
- package/dist/core/Runner.js.map +1 -0
- package/dist/core/Speak.d.ts +37 -0
- package/dist/core/Speak.d.ts.map +1 -0
- package/dist/core/Speak.js +360 -0
- package/dist/core/Speak.js.map +1 -0
- package/dist/core/Understand.d.ts +28 -0
- package/dist/core/Understand.d.ts.map +1 -0
- package/dist/core/Understand.js +349 -0
- package/dist/core/Understand.js.map +1 -0
- package/dist/core/contracts.d.ts +122 -0
- package/dist/core/contracts.d.ts.map +1 -0
- package/dist/core/contracts.js +10 -0
- package/dist/core/contracts.js.map +1 -0
- package/dist/core/falai.d.ts +57 -0
- package/dist/core/falai.d.ts.map +1 -0
- package/dist/core/falai.js +40 -0
- package/dist/core/falai.js.map +1 -0
- package/dist/core/predicate.d.ts +9 -0
- package/dist/core/predicate.d.ts.map +1 -0
- package/dist/core/predicate.js +54 -0
- package/dist/core/predicate.js.map +1 -0
- package/dist/index.d.ts +26 -31
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -24
- package/dist/index.js.map +1 -1
- package/dist/persistence/MemoryStore.d.ts +15 -0
- package/dist/persistence/MemoryStore.d.ts.map +1 -0
- package/dist/persistence/MemoryStore.js +35 -0
- package/dist/persistence/MemoryStore.js.map +1 -0
- package/dist/persistence/MongoStore.d.ts +42 -0
- package/dist/persistence/MongoStore.d.ts.map +1 -0
- package/dist/persistence/MongoStore.js +56 -0
- package/dist/persistence/MongoStore.js.map +1 -0
- package/dist/persistence/OpenSearchStore.d.ts +86 -0
- package/dist/persistence/OpenSearchStore.d.ts.map +1 -0
- package/dist/persistence/OpenSearchStore.js +116 -0
- package/dist/persistence/OpenSearchStore.js.map +1 -0
- package/dist/persistence/PostgresStore.d.ts +41 -0
- package/dist/persistence/PostgresStore.d.ts.map +1 -0
- package/dist/persistence/PostgresStore.js +54 -0
- package/dist/persistence/PostgresStore.js.map +1 -0
- package/dist/persistence/PrismaStore.d.ts +65 -0
- package/dist/persistence/PrismaStore.d.ts.map +1 -0
- package/dist/persistence/PrismaStore.js +91 -0
- package/dist/persistence/PrismaStore.js.map +1 -0
- package/dist/persistence/RedisStore.d.ts +34 -0
- package/dist/persistence/RedisStore.d.ts.map +1 -0
- package/dist/persistence/RedisStore.js +57 -0
- package/dist/persistence/RedisStore.js.map +1 -0
- package/dist/persistence/SQLiteStore.d.ts +45 -0
- package/dist/persistence/SQLiteStore.d.ts.map +1 -0
- package/dist/persistence/SQLiteStore.js +70 -0
- package/dist/persistence/SQLiteStore.js.map +1 -0
- package/dist/persistence/sessionRow.d.ts +14 -0
- package/dist/persistence/sessionRow.d.ts.map +1 -0
- package/dist/persistence/sessionRow.js +45 -0
- package/dist/persistence/sessionRow.js.map +1 -0
- package/dist/providers/DeepSeekProvider.d.ts.map +1 -1
- package/dist/providers/DeepSeekProvider.js +8 -3
- package/dist/providers/DeepSeekProvider.js.map +1 -1
- package/dist/providers/GeminiProvider.d.ts +4 -3
- package/dist/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/GeminiProvider.js +4 -3
- package/dist/providers/GeminiProvider.js.map +1 -1
- package/dist/providers/OpenAICompatibleProvider.d.ts +4 -0
- package/dist/providers/OpenAICompatibleProvider.d.ts.map +1 -1
- package/dist/providers/OpenAICompatibleProvider.js +2 -0
- package/dist/providers/OpenAICompatibleProvider.js.map +1 -1
- package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/providers/OpenRouterProvider.js +2 -4
- package/dist/providers/OpenRouterProvider.js.map +1 -1
- package/dist/providers/ProviderAdapter.d.ts +11 -6
- package/dist/providers/ProviderAdapter.d.ts.map +1 -1
- package/dist/providers/ProviderAdapter.js +34 -11
- package/dist/providers/ProviderAdapter.js.map +1 -1
- package/dist/providers/ZaiProvider.d.ts +6 -4
- package/dist/providers/ZaiProvider.d.ts.map +1 -1
- package/dist/providers/ZaiProvider.js +6 -4
- package/dist/providers/ZaiProvider.js.map +1 -1
- package/dist/types/agent.d.ts +153 -383
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/agent.js +1 -1
- package/dist/types/ai.d.ts +32 -1
- package/dist/types/ai.d.ts.map +1 -1
- package/dist/types/compaction.d.ts +3 -1
- package/dist/types/compaction.d.ts.map +1 -1
- package/dist/types/errors.d.ts +9 -12
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/errors.js +12 -15
- package/dist/types/errors.js.map +1 -1
- package/dist/types/flow.d.ts +265 -513
- package/dist/types/flow.d.ts.map +1 -1
- package/dist/types/flow.js +7 -1
- package/dist/types/flow.js.map +1 -1
- package/dist/types/history.d.ts +7 -18
- package/dist/types/history.d.ts.map +1 -1
- package/dist/types/history.js.map +1 -1
- package/dist/types/index.d.ts +9 -15
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -7
- package/dist/types/index.js.map +1 -1
- package/dist/types/session.d.ts +94 -64
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js +5 -1
- package/dist/types/session.js.map +1 -1
- package/dist/types/tool.d.ts +37 -207
- package/dist/types/tool.d.ts.map +1 -1
- package/dist/types/tool.js +6 -13
- package/dist/types/tool.js.map +1 -1
- package/dist/utils/clock.d.ts +28 -0
- package/dist/utils/clock.d.ts.map +1 -0
- package/dist/utils/clock.js +59 -0
- package/dist/utils/clock.js.map +1 -0
- package/dist/utils/duration.d.ts +11 -0
- package/dist/utils/duration.d.ts.map +1 -0
- package/dist/utils/duration.js +26 -0
- package/dist/utils/duration.js.map +1 -0
- package/dist/utils/history.d.ts +4 -1
- package/dist/utils/history.d.ts.map +1 -1
- package/dist/utils/history.js +2 -2
- package/dist/utils/history.js.map +1 -1
- package/dist/utils/index.d.ts +4 -10
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +4 -21
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/json.d.ts +2 -0
- package/dist/utils/json.d.ts.map +1 -1
- package/dist/utils/json.js +4 -0
- package/dist/utils/json.js.map +1 -1
- package/dist/utils/outcomes.d.ts +48 -0
- package/dist/utils/outcomes.d.ts.map +1 -0
- package/dist/utils/outcomes.js +48 -0
- package/dist/utils/outcomes.js.map +1 -0
- package/dist/utils/schema.d.ts +50 -0
- package/dist/utils/schema.d.ts.map +1 -0
- package/dist/utils/schema.js +129 -0
- package/dist/utils/schema.js.map +1 -0
- package/dist/utils/streamingMessage.d.ts +3 -2
- package/dist/utils/streamingMessage.d.ts.map +1 -1
- package/dist/utils/streamingMessage.js +38 -4
- package/dist/utils/streamingMessage.js.map +1 -1
- package/dist/utils/template.d.ts +13 -149
- package/dist/utils/template.d.ts.map +1 -1
- package/dist/utils/template.js +28 -355
- package/dist/utils/template.js.map +1 -1
- package/dist/utils/usage.d.ts +19 -0
- package/dist/utils/usage.d.ts.map +1 -0
- package/dist/utils/usage.js +31 -0
- package/dist/utils/usage.js.map +1 -0
- package/docs/README.md +37 -19
- package/docs/concepts/architecture.md +117 -239
- package/docs/concepts/collection.md +170 -0
- package/docs/concepts/pipeline.md +132 -378
- package/docs/concepts/runs-and-waits.md +192 -0
- package/docs/guides/actions-and-events.md +276 -0
- package/docs/guides/branching.md +119 -208
- package/docs/guides/compaction.md +63 -158
- package/docs/guides/conditions.md +164 -128
- package/docs/guides/error-handling.md +168 -164
- package/docs/guides/flow-control.md +210 -349
- package/docs/guides/flows-from-json.md +224 -0
- package/docs/guides/instructions.md +125 -161
- package/docs/guides/persistence.md +182 -206
- package/docs/guides/streaming.md +50 -114
- package/docs/guides/testing.md +284 -0
- package/docs/guides/triggers.md +401 -0
- package/docs/migration/README.md +8 -15
- package/docs/migration/v1-to-v2.md +1 -1
- package/docs/migration/v2-3-to-v2-4.md +2 -2
- package/docs/migration/v2-6-to-v2-7.md +4 -4
- package/docs/migration/v3-to-v4.md +452 -0
- package/docs/reference/actions-events-conditions.md +396 -0
- package/docs/reference/agent.md +244 -0
- package/docs/reference/branches.md +75 -203
- package/docs/reference/errors.md +188 -144
- package/docs/reference/fields.md +125 -0
- package/docs/reference/flow-spec.md +248 -0
- package/docs/reference/flow.md +104 -192
- package/docs/reference/instruction.md +83 -137
- package/docs/reference/outcomes.md +273 -0
- package/docs/reference/providers.md +525 -302
- package/docs/reference/session.md +210 -0
- package/docs/reference/step.md +194 -312
- package/docs/reference/stores.md +496 -0
- package/docs/reference/tool.md +162 -231
- package/docs/reference/trigger.md +180 -0
- package/docs/rfc/v4-one-flow.md +477 -0
- package/docs/start/01-install.md +59 -44
- package/docs/start/02-first-agent.md +97 -147
- package/docs/start/03-collect-data.md +78 -183
- package/docs/start/04-add-tools.md +159 -227
- package/docs/start/05-go-to-production.md +167 -164
- package/examples/01-quickstart.ts +26 -16
- package/examples/02-fields.ts +75 -0
- package/examples/03-tools.ts +79 -119
- package/examples/04-instructions.ts +60 -87
- package/examples/05-branches.ts +78 -0
- package/examples/06-triggers-and-waits.ts +148 -0
- package/examples/07-streaming.ts +34 -60
- package/examples/08-store-and-migration.ts +97 -0
- package/examples/09-flows-from-json.ts +107 -0
- package/package.json +9 -6
- package/src/core/Agent.ts +116 -1512
- package/src/core/CompactionEngine.ts +7 -4
- package/src/core/FlowSpec.ts +712 -0
- package/src/core/Migrate.ts +256 -0
- package/src/core/Prompt.ts +156 -0
- package/src/core/Runner.ts +1181 -0
- package/src/core/Speak.ts +451 -0
- package/src/core/Understand.ts +422 -0
- package/src/core/contracts.ts +111 -0
- package/src/core/falai.ts +86 -0
- package/src/core/predicate.ts +56 -0
- package/src/index.ts +119 -147
- package/src/persistence/MemoryStore.ts +37 -0
- package/src/persistence/MongoStore.ts +89 -0
- package/src/persistence/OpenSearchStore.ts +153 -0
- package/src/persistence/PostgresStore.ts +89 -0
- package/src/persistence/PrismaStore.ts +127 -0
- package/src/persistence/RedisStore.ts +90 -0
- package/src/persistence/SQLiteStore.ts +103 -0
- package/src/persistence/sessionRow.ts +45 -0
- package/src/providers/DeepSeekProvider.ts +8 -3
- package/src/providers/GeminiProvider.ts +4 -3
- package/src/providers/OpenAICompatibleProvider.ts +6 -0
- package/src/providers/OpenRouterProvider.ts +2 -4
- package/src/providers/ProviderAdapter.ts +46 -13
- package/src/providers/ZaiProvider.ts +6 -4
- package/src/types/agent.ts +124 -397
- package/src/types/ai.ts +33 -1
- package/src/types/compaction.ts +3 -1
- package/src/types/errors.ts +13 -16
- package/src/types/flow.ts +249 -550
- package/src/types/history.ts +7 -20
- package/src/types/index.ts +87 -139
- package/src/types/session.ts +135 -70
- package/src/types/tool.ts +42 -267
- package/src/utils/clock.ts +70 -0
- package/src/utils/duration.ts +33 -0
- package/src/utils/history.ts +3 -2
- package/src/utils/index.ts +8 -66
- package/src/utils/json.ts +5 -0
- package/src/utils/outcomes.ts +56 -0
- package/src/utils/schema.ts +145 -0
- package/src/utils/streamingMessage.ts +34 -4
- package/src/utils/template.ts +32 -423
- package/src/utils/usage.ts +37 -0
- package/dist/adapters/MemoryAdapter.d.ts +0 -47
- package/dist/adapters/MemoryAdapter.d.ts.map +0 -1
- package/dist/adapters/MemoryAdapter.js +0 -204
- package/dist/adapters/MemoryAdapter.js.map +0 -1
- package/dist/adapters/MongoAdapter.d.ts +0 -97
- package/dist/adapters/MongoAdapter.d.ts.map +0 -1
- package/dist/adapters/MongoAdapter.js +0 -196
- package/dist/adapters/MongoAdapter.js.map +0 -1
- package/dist/adapters/OpenSearchAdapter.d.ts +0 -169
- package/dist/adapters/OpenSearchAdapter.d.ts.map +0 -1
- package/dist/adapters/OpenSearchAdapter.js +0 -471
- package/dist/adapters/OpenSearchAdapter.js.map +0 -1
- package/dist/adapters/PostgreSQLAdapter.d.ts +0 -85
- package/dist/adapters/PostgreSQLAdapter.d.ts.map +0 -1
- package/dist/adapters/PostgreSQLAdapter.js +0 -308
- package/dist/adapters/PostgreSQLAdapter.js.map +0 -1
- package/dist/adapters/PrismaAdapter.d.ts +0 -115
- package/dist/adapters/PrismaAdapter.d.ts.map +0 -1
- package/dist/adapters/PrismaAdapter.js +0 -406
- package/dist/adapters/PrismaAdapter.js.map +0 -1
- package/dist/adapters/RedisAdapter.d.ts +0 -72
- package/dist/adapters/RedisAdapter.d.ts.map +0 -1
- package/dist/adapters/RedisAdapter.js +0 -286
- package/dist/adapters/RedisAdapter.js.map +0 -1
- package/dist/adapters/SQLiteAdapter.d.ts +0 -86
- package/dist/adapters/SQLiteAdapter.d.ts.map +0 -1
- package/dist/adapters/SQLiteAdapter.js +0 -337
- package/dist/adapters/SQLiteAdapter.js.map +0 -1
- package/dist/adapters/index.d.ts +0 -17
- package/dist/adapters/index.d.ts.map +0 -1
- package/dist/adapters/index.js +0 -11
- package/dist/adapters/index.js.map +0 -1
- package/dist/adapters/sessionRow.d.ts +0 -22
- package/dist/adapters/sessionRow.d.ts.map +0 -1
- package/dist/adapters/sessionRow.js +0 -48
- package/dist/adapters/sessionRow.js.map +0 -1
- package/dist/cjs/adapters/MemoryAdapter.d.ts +0 -47
- package/dist/cjs/adapters/MemoryAdapter.d.ts.map +0 -1
- package/dist/cjs/adapters/MemoryAdapter.js +0 -208
- package/dist/cjs/adapters/MemoryAdapter.js.map +0 -1
- package/dist/cjs/adapters/MongoAdapter.d.ts +0 -97
- package/dist/cjs/adapters/MongoAdapter.d.ts.map +0 -1
- package/dist/cjs/adapters/MongoAdapter.js +0 -200
- package/dist/cjs/adapters/MongoAdapter.js.map +0 -1
- package/dist/cjs/adapters/OpenSearchAdapter.d.ts +0 -169
- package/dist/cjs/adapters/OpenSearchAdapter.d.ts.map +0 -1
- package/dist/cjs/adapters/OpenSearchAdapter.js +0 -475
- package/dist/cjs/adapters/OpenSearchAdapter.js.map +0 -1
- package/dist/cjs/adapters/PostgreSQLAdapter.d.ts +0 -85
- package/dist/cjs/adapters/PostgreSQLAdapter.d.ts.map +0 -1
- package/dist/cjs/adapters/PostgreSQLAdapter.js +0 -312
- package/dist/cjs/adapters/PostgreSQLAdapter.js.map +0 -1
- package/dist/cjs/adapters/PrismaAdapter.d.ts +0 -115
- package/dist/cjs/adapters/PrismaAdapter.d.ts.map +0 -1
- package/dist/cjs/adapters/PrismaAdapter.js +0 -410
- package/dist/cjs/adapters/PrismaAdapter.js.map +0 -1
- package/dist/cjs/adapters/RedisAdapter.d.ts +0 -72
- package/dist/cjs/adapters/RedisAdapter.d.ts.map +0 -1
- package/dist/cjs/adapters/RedisAdapter.js +0 -290
- package/dist/cjs/adapters/RedisAdapter.js.map +0 -1
- package/dist/cjs/adapters/SQLiteAdapter.d.ts +0 -86
- package/dist/cjs/adapters/SQLiteAdapter.d.ts.map +0 -1
- package/dist/cjs/adapters/SQLiteAdapter.js +0 -341
- package/dist/cjs/adapters/SQLiteAdapter.js.map +0 -1
- package/dist/cjs/adapters/index.d.ts +0 -17
- package/dist/cjs/adapters/index.d.ts.map +0 -1
- package/dist/cjs/adapters/index.js +0 -21
- package/dist/cjs/adapters/index.js.map +0 -1
- package/dist/cjs/adapters/sessionRow.d.ts +0 -22
- package/dist/cjs/adapters/sessionRow.d.ts.map +0 -1
- package/dist/cjs/adapters/sessionRow.js +0 -52
- package/dist/cjs/adapters/sessionRow.js.map +0 -1
- package/dist/cjs/constants/index.d.ts +0 -1
- package/dist/cjs/constants/index.d.ts.map +0 -1
- package/dist/cjs/constants/index.js +0 -4
- package/dist/cjs/constants/index.js.map +0 -1
- package/dist/cjs/core/AutoChainExecutor.d.ts +0 -97
- package/dist/cjs/core/AutoChainExecutor.d.ts.map +0 -1
- package/dist/cjs/core/AutoChainExecutor.js +0 -288
- package/dist/cjs/core/AutoChainExecutor.js.map +0 -1
- package/dist/cjs/core/BranchEvaluator.d.ts +0 -55
- package/dist/cjs/core/BranchEvaluator.d.ts.map +0 -1
- package/dist/cjs/core/BranchEvaluator.js +0 -125
- package/dist/cjs/core/BranchEvaluator.js.map +0 -1
- package/dist/cjs/core/DirectiveChainTracker.d.ts +0 -49
- package/dist/cjs/core/DirectiveChainTracker.d.ts.map +0 -1
- package/dist/cjs/core/DirectiveChainTracker.js +0 -121
- package/dist/cjs/core/DirectiveChainTracker.js.map +0 -1
- package/dist/cjs/core/Events.d.ts +0 -26
- package/dist/cjs/core/Events.d.ts.map +0 -1
- package/dist/cjs/core/Events.js +0 -144
- package/dist/cjs/core/Events.js.map +0 -1
- package/dist/cjs/core/Flow.d.ts +0 -183
- package/dist/cjs/core/Flow.d.ts.map +0 -1
- package/dist/cjs/core/Flow.js +0 -551
- package/dist/cjs/core/Flow.js.map +0 -1
- package/dist/cjs/core/FlowRouter.d.ts +0 -183
- package/dist/cjs/core/FlowRouter.d.ts.map +0 -1
- package/dist/cjs/core/FlowRouter.js +0 -1047
- package/dist/cjs/core/FlowRouter.js.map +0 -1
- package/dist/cjs/core/PersistenceManager.d.ts +0 -114
- package/dist/cjs/core/PersistenceManager.d.ts.map +0 -1
- package/dist/cjs/core/PersistenceManager.js +0 -336
- package/dist/cjs/core/PersistenceManager.js.map +0 -1
- package/dist/cjs/core/PromptComposer.d.ts +0 -47
- package/dist/cjs/core/PromptComposer.d.ts.map +0 -1
- package/dist/cjs/core/PromptComposer.js +0 -397
- package/dist/cjs/core/PromptComposer.js.map +0 -1
- package/dist/cjs/core/PromptSectionCache.d.ts +0 -48
- package/dist/cjs/core/PromptSectionCache.d.ts.map +0 -1
- package/dist/cjs/core/PromptSectionCache.js +0 -108
- package/dist/cjs/core/PromptSectionCache.js.map +0 -1
- package/dist/cjs/core/ResponseEngine.d.ts +0 -43
- package/dist/cjs/core/ResponseEngine.d.ts.map +0 -1
- package/dist/cjs/core/ResponseEngine.js +0 -235
- package/dist/cjs/core/ResponseEngine.js.map +0 -1
- package/dist/cjs/core/ResponseGenerationError.d.ts +0 -30
- package/dist/cjs/core/ResponseGenerationError.d.ts.map +0 -1
- package/dist/cjs/core/ResponseGenerationError.js +0 -35
- package/dist/cjs/core/ResponseGenerationError.js.map +0 -1
- package/dist/cjs/core/ResponseModal.d.ts +0 -305
- package/dist/cjs/core/ResponseModal.d.ts.map +0 -1
- package/dist/cjs/core/ResponseModal.js +0 -1414
- package/dist/cjs/core/ResponseModal.js.map +0 -1
- package/dist/cjs/core/ResponsePipeline.d.ts +0 -220
- package/dist/cjs/core/ResponsePipeline.d.ts.map +0 -1
- package/dist/cjs/core/ResponsePipeline.js +0 -1040
- package/dist/cjs/core/ResponsePipeline.js.map +0 -1
- package/dist/cjs/core/SessionFinalizer.d.ts +0 -34
- package/dist/cjs/core/SessionFinalizer.d.ts.map +0 -1
- package/dist/cjs/core/SessionFinalizer.js +0 -88
- package/dist/cjs/core/SessionFinalizer.js.map +0 -1
- package/dist/cjs/core/SessionManager.d.ts +0 -112
- package/dist/cjs/core/SessionManager.d.ts.map +0 -1
- package/dist/cjs/core/SessionManager.js +0 -308
- package/dist/cjs/core/SessionManager.js.map +0 -1
- package/dist/cjs/core/SignalCoordinator.d.ts +0 -103
- package/dist/cjs/core/SignalCoordinator.d.ts.map +0 -1
- package/dist/cjs/core/SignalCoordinator.js +0 -207
- package/dist/cjs/core/SignalCoordinator.js.map +0 -1
- package/dist/cjs/core/SignalEvaluator.d.ts +0 -86
- package/dist/cjs/core/SignalEvaluator.d.ts.map +0 -1
- package/dist/cjs/core/SignalEvaluator.js +0 -319
- package/dist/cjs/core/SignalEvaluator.js.map +0 -1
- package/dist/cjs/core/SignalProcessor.d.ts +0 -152
- package/dist/cjs/core/SignalProcessor.d.ts.map +0 -1
- package/dist/cjs/core/SignalProcessor.js +0 -505
- package/dist/cjs/core/SignalProcessor.js.map +0 -1
- package/dist/cjs/core/Step.d.ts +0 -184
- package/dist/cjs/core/Step.d.ts.map +0 -1
- package/dist/cjs/core/Step.js +0 -599
- package/dist/cjs/core/Step.js.map +0 -1
- package/dist/cjs/core/StepLifecycle.d.ts +0 -43
- package/dist/cjs/core/StepLifecycle.d.ts.map +0 -1
- package/dist/cjs/core/StepLifecycle.js +0 -180
- package/dist/cjs/core/StepLifecycle.js.map +0 -1
- package/dist/cjs/core/StreamingToolExecutor.d.ts +0 -142
- package/dist/cjs/core/StreamingToolExecutor.d.ts.map +0 -1
- package/dist/cjs/core/StreamingToolExecutor.js +0 -490
- package/dist/cjs/core/StreamingToolExecutor.js.map +0 -1
- package/dist/cjs/core/ToolLoopExecutor.d.ts +0 -133
- package/dist/cjs/core/ToolLoopExecutor.d.ts.map +0 -1
- package/dist/cjs/core/ToolLoopExecutor.js +0 -568
- package/dist/cjs/core/ToolLoopExecutor.js.map +0 -1
- package/dist/cjs/core/ToolManager.d.ts +0 -250
- package/dist/cjs/core/ToolManager.d.ts.map +0 -1
- package/dist/cjs/core/ToolManager.js +0 -1104
- package/dist/cjs/core/ToolManager.js.map +0 -1
- package/dist/cjs/core/createAgent.d.ts +0 -35
- package/dist/cjs/core/createAgent.d.ts.map +0 -1
- package/dist/cjs/core/createAgent.js +0 -39
- package/dist/cjs/core/createAgent.js.map +0 -1
- package/dist/cjs/core/flow-namespace.d.ts +0 -64
- package/dist/cjs/core/flow-namespace.d.ts.map +0 -1
- package/dist/cjs/core/flow-namespace.js +0 -182
- package/dist/cjs/core/flow-namespace.js.map +0 -1
- package/dist/cjs/core/toolGates.d.ts +0 -24
- package/dist/cjs/core/toolGates.d.ts.map +0 -1
- package/dist/cjs/core/toolGates.js +0 -52
- package/dist/cjs/core/toolGates.js.map +0 -1
- package/dist/cjs/types/persistence.d.ts +0 -254
- package/dist/cjs/types/persistence.d.ts.map +0 -1
- package/dist/cjs/types/persistence.js +0 -7
- package/dist/cjs/types/persistence.js.map +0 -1
- package/dist/cjs/types/prompt-cache.d.ts +0 -15
- package/dist/cjs/types/prompt-cache.d.ts.map +0 -1
- package/dist/cjs/types/prompt-cache.js +0 -6
- package/dist/cjs/types/prompt-cache.js.map +0 -1
- package/dist/cjs/types/signals.d.ts +0 -263
- package/dist/cjs/types/signals.d.ts.map +0 -1
- package/dist/cjs/types/signals.js +0 -11
- package/dist/cjs/types/signals.js.map +0 -1
- package/dist/cjs/types/template.d.ts +0 -84
- package/dist/cjs/types/template.d.ts.map +0 -1
- package/dist/cjs/types/template.js +0 -3
- package/dist/cjs/types/template.js.map +0 -1
- package/dist/cjs/utils/condition.d.ts +0 -63
- package/dist/cjs/utils/condition.d.ts.map +0 -1
- package/dist/cjs/utils/condition.js +0 -239
- package/dist/cjs/utils/condition.js.map +0 -1
- package/dist/cjs/utils/event.d.ts +0 -6
- package/dist/cjs/utils/event.d.ts.map +0 -1
- package/dist/cjs/utils/event.js +0 -20
- package/dist/cjs/utils/event.js.map +0 -1
- package/dist/cjs/utils/id.d.ts +0 -33
- package/dist/cjs/utils/id.d.ts.map +0 -1
- package/dist/cjs/utils/id.js +0 -84
- package/dist/cjs/utils/id.js.map +0 -1
- package/dist/cjs/utils/serialize.d.ts +0 -36
- package/dist/cjs/utils/serialize.d.ts.map +0 -1
- package/dist/cjs/utils/serialize.js +0 -77
- package/dist/cjs/utils/serialize.js.map +0 -1
- package/dist/cjs/utils/session.d.ts +0 -124
- package/dist/cjs/utils/session.d.ts.map +0 -1
- package/dist/cjs/utils/session.js +0 -396
- package/dist/cjs/utils/session.js.map +0 -1
- package/dist/constants/index.d.ts +0 -2
- package/dist/constants/index.d.ts.map +0 -1
- package/dist/constants/index.js +0 -4
- package/dist/constants/index.js.map +0 -1
- package/dist/core/AutoChainExecutor.d.ts +0 -97
- package/dist/core/AutoChainExecutor.d.ts.map +0 -1
- package/dist/core/AutoChainExecutor.js +0 -284
- package/dist/core/AutoChainExecutor.js.map +0 -1
- package/dist/core/BranchEvaluator.d.ts +0 -55
- package/dist/core/BranchEvaluator.d.ts.map +0 -1
- package/dist/core/BranchEvaluator.js +0 -121
- package/dist/core/BranchEvaluator.js.map +0 -1
- package/dist/core/DirectiveChainTracker.d.ts +0 -49
- package/dist/core/DirectiveChainTracker.d.ts.map +0 -1
- package/dist/core/DirectiveChainTracker.js +0 -117
- package/dist/core/DirectiveChainTracker.js.map +0 -1
- package/dist/core/Events.d.ts +0 -26
- package/dist/core/Events.d.ts.map +0 -1
- package/dist/core/Events.js +0 -137
- package/dist/core/Events.js.map +0 -1
- package/dist/core/Flow.d.ts +0 -183
- package/dist/core/Flow.d.ts.map +0 -1
- package/dist/core/Flow.js +0 -547
- package/dist/core/Flow.js.map +0 -1
- package/dist/core/FlowRouter.d.ts +0 -183
- package/dist/core/FlowRouter.d.ts.map +0 -1
- package/dist/core/FlowRouter.js +0 -1043
- package/dist/core/FlowRouter.js.map +0 -1
- package/dist/core/PersistenceManager.d.ts +0 -114
- package/dist/core/PersistenceManager.d.ts.map +0 -1
- package/dist/core/PersistenceManager.js +0 -332
- package/dist/core/PersistenceManager.js.map +0 -1
- package/dist/core/PromptComposer.d.ts +0 -47
- package/dist/core/PromptComposer.d.ts.map +0 -1
- package/dist/core/PromptComposer.js +0 -393
- package/dist/core/PromptComposer.js.map +0 -1
- package/dist/core/PromptSectionCache.d.ts +0 -48
- package/dist/core/PromptSectionCache.d.ts.map +0 -1
- package/dist/core/PromptSectionCache.js +0 -104
- package/dist/core/PromptSectionCache.js.map +0 -1
- package/dist/core/ResponseEngine.d.ts +0 -43
- package/dist/core/ResponseEngine.d.ts.map +0 -1
- package/dist/core/ResponseEngine.js +0 -231
- package/dist/core/ResponseEngine.js.map +0 -1
- package/dist/core/ResponseGenerationError.d.ts +0 -30
- package/dist/core/ResponseGenerationError.d.ts.map +0 -1
- package/dist/core/ResponseGenerationError.js +0 -31
- package/dist/core/ResponseGenerationError.js.map +0 -1
- package/dist/core/ResponseModal.d.ts +0 -305
- package/dist/core/ResponseModal.d.ts.map +0 -1
- package/dist/core/ResponseModal.js +0 -1410
- package/dist/core/ResponseModal.js.map +0 -1
- package/dist/core/ResponsePipeline.d.ts +0 -220
- package/dist/core/ResponsePipeline.d.ts.map +0 -1
- package/dist/core/ResponsePipeline.js +0 -1035
- package/dist/core/ResponsePipeline.js.map +0 -1
- package/dist/core/SessionFinalizer.d.ts +0 -34
- package/dist/core/SessionFinalizer.d.ts.map +0 -1
- package/dist/core/SessionFinalizer.js +0 -84
- package/dist/core/SessionFinalizer.js.map +0 -1
- package/dist/core/SessionManager.d.ts +0 -112
- package/dist/core/SessionManager.d.ts.map +0 -1
- package/dist/core/SessionManager.js +0 -301
- package/dist/core/SessionManager.js.map +0 -1
- package/dist/core/SignalCoordinator.d.ts +0 -103
- package/dist/core/SignalCoordinator.d.ts.map +0 -1
- package/dist/core/SignalCoordinator.js +0 -203
- package/dist/core/SignalCoordinator.js.map +0 -1
- package/dist/core/SignalEvaluator.d.ts +0 -86
- package/dist/core/SignalEvaluator.d.ts.map +0 -1
- package/dist/core/SignalEvaluator.js +0 -312
- package/dist/core/SignalEvaluator.js.map +0 -1
- package/dist/core/SignalProcessor.d.ts +0 -152
- package/dist/core/SignalProcessor.d.ts.map +0 -1
- package/dist/core/SignalProcessor.js +0 -498
- package/dist/core/SignalProcessor.js.map +0 -1
- package/dist/core/Step.d.ts +0 -184
- package/dist/core/Step.d.ts.map +0 -1
- package/dist/core/Step.js +0 -594
- package/dist/core/Step.js.map +0 -1
- package/dist/core/StepLifecycle.d.ts +0 -43
- package/dist/core/StepLifecycle.d.ts.map +0 -1
- package/dist/core/StepLifecycle.js +0 -176
- package/dist/core/StepLifecycle.js.map +0 -1
- package/dist/core/StreamingToolExecutor.d.ts +0 -142
- package/dist/core/StreamingToolExecutor.d.ts.map +0 -1
- package/dist/core/StreamingToolExecutor.js +0 -483
- package/dist/core/StreamingToolExecutor.js.map +0 -1
- package/dist/core/ToolLoopExecutor.d.ts +0 -133
- package/dist/core/ToolLoopExecutor.d.ts.map +0 -1
- package/dist/core/ToolLoopExecutor.js +0 -564
- package/dist/core/ToolLoopExecutor.js.map +0 -1
- package/dist/core/ToolManager.d.ts +0 -250
- package/dist/core/ToolManager.d.ts.map +0 -1
- package/dist/core/ToolManager.js +0 -1098
- package/dist/core/ToolManager.js.map +0 -1
- package/dist/core/createAgent.d.ts +0 -35
- package/dist/core/createAgent.d.ts.map +0 -1
- package/dist/core/createAgent.js +0 -36
- package/dist/core/createAgent.js.map +0 -1
- package/dist/core/flow-namespace.d.ts +0 -64
- package/dist/core/flow-namespace.d.ts.map +0 -1
- package/dist/core/flow-namespace.js +0 -179
- package/dist/core/flow-namespace.js.map +0 -1
- package/dist/core/toolGates.d.ts +0 -24
- package/dist/core/toolGates.d.ts.map +0 -1
- package/dist/core/toolGates.js +0 -49
- package/dist/core/toolGates.js.map +0 -1
- package/dist/types/persistence.d.ts +0 -254
- package/dist/types/persistence.d.ts.map +0 -1
- package/dist/types/persistence.js +0 -6
- package/dist/types/persistence.js.map +0 -1
- package/dist/types/prompt-cache.d.ts +0 -15
- package/dist/types/prompt-cache.d.ts.map +0 -1
- package/dist/types/prompt-cache.js +0 -5
- package/dist/types/prompt-cache.js.map +0 -1
- package/dist/types/signals.d.ts +0 -263
- package/dist/types/signals.d.ts.map +0 -1
- package/dist/types/signals.js +0 -10
- package/dist/types/signals.js.map +0 -1
- package/dist/types/template.d.ts +0 -84
- package/dist/types/template.d.ts.map +0 -1
- package/dist/types/template.js +0 -2
- package/dist/types/template.js.map +0 -1
- package/dist/utils/condition.d.ts +0 -63
- package/dist/utils/condition.d.ts.map +0 -1
- package/dist/utils/condition.js +0 -230
- package/dist/utils/condition.js.map +0 -1
- package/dist/utils/event.d.ts +0 -6
- package/dist/utils/event.d.ts.map +0 -1
- package/dist/utils/event.js +0 -17
- package/dist/utils/event.js.map +0 -1
- package/dist/utils/id.d.ts +0 -33
- package/dist/utils/id.d.ts.map +0 -1
- package/dist/utils/id.js +0 -77
- package/dist/utils/id.js.map +0 -1
- package/dist/utils/serialize.d.ts +0 -36
- package/dist/utils/serialize.d.ts.map +0 -1
- package/dist/utils/serialize.js +0 -72
- package/dist/utils/serialize.js.map +0 -1
- package/dist/utils/session.d.ts +0 -124
- package/dist/utils/session.d.ts.map +0 -1
- package/dist/utils/session.js +0 -379
- package/dist/utils/session.js.map +0 -1
- package/docs/concepts/directives.md +0 -369
- package/docs/reference/adapters.md +0 -543
- package/docs/reference/create-agent.md +0 -216
- package/docs/reference/directive.md +0 -242
- package/docs/reference/signals.md +0 -368
- package/examples/02-data-extraction.ts +0 -90
- package/examples/05-branching.ts +0 -140
- package/examples/06-flow-control.ts +0 -103
- package/examples/08-persistence.ts +0 -98
- package/examples/09-signals.ts +0 -144
- package/src/adapters/MemoryAdapter.ts +0 -281
- package/src/adapters/MongoAdapter.ts +0 -341
- package/src/adapters/OpenSearchAdapter.ts +0 -693
- package/src/adapters/PostgreSQLAdapter.ts +0 -487
- package/src/adapters/PrismaAdapter.ts +0 -617
- package/src/adapters/RedisAdapter.ts +0 -439
- package/src/adapters/SQLiteAdapter.ts +0 -496
- package/src/adapters/index.ts +0 -43
- package/src/adapters/sessionRow.ts +0 -57
- package/src/constants/index.ts +0 -2
- package/src/core/AutoChainExecutor.ts +0 -397
- package/src/core/BranchEvaluator.ts +0 -161
- package/src/core/DirectiveChainTracker.ts +0 -144
- package/src/core/Events.ts +0 -164
- package/src/core/Flow.ts +0 -665
- package/src/core/FlowRouter.ts +0 -1540
- package/src/core/PersistenceManager.ts +0 -446
- package/src/core/PromptComposer.ts +0 -448
- package/src/core/PromptSectionCache.ts +0 -125
- package/src/core/ResponseEngine.ts +0 -338
- package/src/core/ResponseGenerationError.ts +0 -53
- package/src/core/ResponseModal.ts +0 -1902
- package/src/core/ResponsePipeline.ts +0 -1404
- package/src/core/SessionFinalizer.ts +0 -108
- package/src/core/SessionManager.ts +0 -372
- package/src/core/SignalCoordinator.ts +0 -263
- package/src/core/SignalEvaluator.ts +0 -404
- package/src/core/SignalProcessor.ts +0 -663
- package/src/core/Step.ts +0 -782
- package/src/core/StepLifecycle.ts +0 -242
- package/src/core/StreamingToolExecutor.ts +0 -609
- package/src/core/ToolLoopExecutor.ts +0 -749
- package/src/core/ToolManager.ts +0 -1379
- package/src/core/createAgent.ts +0 -40
- package/src/core/flow-namespace.ts +0 -227
- package/src/core/toolGates.ts +0 -72
- package/src/types/persistence.ts +0 -303
- package/src/types/prompt-cache.ts +0 -17
- package/src/types/signals.ts +0 -338
- package/src/types/template.ts +0 -98
- package/src/utils/condition.ts +0 -296
- package/src/utils/event.ts +0 -16
- package/src/utils/id.ts +0 -91
- package/src/utils/serialize.ts +0 -86
- package/src/utils/session.ts +0 -501
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "@falai/agent v4 — one Flow (design v2)"
|
|
3
|
+
description: "Design record for @falai/agent 4.0: flows, automations and signals become one Flow with triggers, runs, waits and per-field asks. Not part of the docs sidebar."
|
|
4
|
+
type: concept
|
|
5
|
+
order: 99
|
|
6
|
+
sidebar: false
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
> **Design record, approved 2026-09-20.** This is the reference the 4.0 implementation follows.
|
|
10
|
+
> It came out of a read-only research pass over `@falai/agent` 3.4.5 and its three consumers
|
|
11
|
+
> (prospectar, atendime, ilojista), five independent designs, three judges, one synthesis, four
|
|
12
|
+
> adversarial critiques (90 holes) and a repair pass. Section numbers below are cited from the
|
|
13
|
+
> implementation plan and from commit messages as "design §N".
|
|
14
|
+
>
|
|
15
|
+
> **Decided with Gus:** scope is the framework plus all three products; all seven persistence
|
|
16
|
+
> adapters stay, reshaped to `Store` implementations; the host gate (`silenced`) is one mouth and
|
|
17
|
+
> closes `say`, `prompt` and speaking `do` steps alike; routing, mention detection and extraction
|
|
18
|
+
> share one `understand` call, eval-gated on Gemini and GLM before `4.0.0`.
|
|
19
|
+
>
|
|
20
|
+
> **Assumed defaults:** `maxAsks` 3; `repeat: 'once'` per session for `message` flows; zero LLM
|
|
21
|
+
> calls while a human owns the lead unless `silenced: { understand: true }`; lead-only events run
|
|
22
|
+
> in a `lead:<id>` session with `silenced: 'sem conversa'`; END_FLOW / BOT_DETECTED / SCHEDULING
|
|
23
|
+
> ship as `kind: 'system'` recipes; `criar_fluxo` keeps the host's generation call with
|
|
24
|
+
> `flowSpecSchema`; session = conversation with `anchor: 'lead'`; migrated flows get
|
|
25
|
+
> `onEnd: 'stay'` written explicitly.
|
|
26
|
+
>
|
|
27
|
+
> **Refined during implementation (slices 1–2, 2026-09-20):**
|
|
28
|
+
> - The factory is a two-step chain: `falai<C>()` returns a root whose `.fields(defs)` binds the
|
|
29
|
+
> data type, and `flow` / `action` / `event` / `condition` / `agent` hang off that bound object,
|
|
30
|
+
> so `collect`, `ask`, `clearOnStart` and `ctx.set` are checked against the slugs. `f.agent()`
|
|
31
|
+
> passes the fields itself; `type Data = DataOf<typeof f>` replaces `InferData<typeof fields>`.
|
|
32
|
+
> TypeScript cannot infer one generic while another is written by hand, which is why §3's
|
|
33
|
+
> `const fields = f.fields(...)` then `f.agent({ fields })` shape was dropped.
|
|
34
|
+
> - Action, event and condition names inside a flow are plain strings, checked when the agent is
|
|
35
|
+
> built, the same path a JSON `FlowSpec` takes. The mapped-type unions in §3 (`{ [K in keyof
|
|
36
|
+
> A]: ... }[keyof A]`) would have made every flow generic in four parameters.
|
|
37
|
+
> - `Condition` is `{ check(ctx, arg) }` and `Action` is `{ parameters, run(params, ctx) }`, as
|
|
38
|
+
> method signatures: method bivariance lets a heterogeneous map type without `any`.
|
|
39
|
+
> - `Store.save` returns the saved session, with the bumped version, instead of `void`.
|
|
40
|
+
> - A mention trigger's `extract` is a flat map of parameter definitions (`{ trecho: { type:
|
|
41
|
+
> 'string' } }`), the S7 form, not a JSON schema object: one shape for fields, action
|
|
42
|
+
> parameters and extraction, and `toWireSchema` applies to all three.
|
|
43
|
+
> - Tool handlers take `(args, ctx)`, the order every provider SDK uses.
|
|
44
|
+
> - Prompt scaffolding (section headings, envelope instructions, routing rules) is written in
|
|
45
|
+
> English; the authored content it carries (prompts, `ask` texts, instructions, knowledge) and
|
|
46
|
+
> the outcome `detail` strings the host shows in *Execuções* stay in the product's language.
|
|
47
|
+
|
|
48
|
+
# @falai/agent v4 — one Flow (design v2)
|
|
49
|
+
|
|
50
|
+
## 1. Mental model
|
|
51
|
+
|
|
52
|
+
A **Flow** is a trigger plus an ordered list of steps. The trigger says when a **run** starts: the lead asks for this (`message`: the AI routes the conversation here), the lead mentions this (`mention`: the AI detects it, the run reacts beside the conversation), something happens in the host (`event` with a payload), the lead goes quiet (`silence`), or nothing — the host starts it (*Início manual*). Each step is one of five things: the AI talks (`prompt` / `collect`), a fixed message goes out (`say`), the host does something (`do`), the run waits (`wait`), or the code forks (`if`). Fields live on the agent schema, each with its own *como perguntar*; they land in any order and a step ends when its fields are known. A **session** (one per conversation) holds many runs; at most one run is asking a question at a time. One method, `agent.turn()`, takes whatever just happened, moves the runs by code, spends at most two LLM calls (understand the lead, phrase the reply) plus one per tool round, and hands back the messages to send, the timers to set and a per-step outcome line. The framework never sends, never sleeps, never saves; the host does those three things after a successful compare-and-swap. Product side: one page (*Fluxos*, sections *Funil* / *Atendimento*), one card (*Quando X → faça Y*), one editor, one assistant tool, one drawer (*Execuções*).
|
|
53
|
+
|
|
54
|
+
Child: "A flow is a list of steps that starts when something happens. The AI does the talking. The code does the rest." Junior: "`agent.turn({ message: 'oi' })` returns the messages to send."
|
|
55
|
+
|
|
56
|
+
## 2. Primitives: stays, merges, dies
|
|
57
|
+
|
|
58
|
+
| Old | Fate |
|
|
59
|
+
|---|---|
|
|
60
|
+
| Agent | Stays; immutable config, one instance serves every session. `_context`, `_pendingData`, `currentSession` die; PromptSectionCache becomes a per-turn instance (its property tests stay). `context` and `history` arrive per turn. |
|
|
61
|
+
| Flow | THE noun; absorbs Signal and CrmAutomation. Gains `on[]`, `anchor`, `while`, `clearOnStart`, `onEnd`. Loses `when`/`if` (→ triggers), `requiredFields`, `optionalFields`, `reentrant`, `onComplete`, `hooks`. |
|
|
62
|
+
| Step `prompt` / `auto` / `reply` | `prompt` stays (AI talks); `reply` → `say`; `auto` → `do`, `if`, `wait`. |
|
|
63
|
+
| `collect` | Stays as step completion: a talk step ends when its fields are known, skipped without a call when already known. `{ collect: [...] }` alone asks with the schema's `ask`. |
|
|
64
|
+
| `requires`, `skip`, `requiredFields`, `optionalFields` | Die. Order, known-field skipping, `if` steps and `maxAsks` cover every use. |
|
|
65
|
+
| `branches` (`when`/`if` mid-step exits) | Stay as `branches[]` on talk and `wait` steps, judged while the step is asking — `if` free, `when` inside the understand call. There is no standalone `when` step: AI forks only exist where fresh text exists. |
|
|
66
|
+
| `onComplete`, `reentrant` | → `onEnd` (`'end' \| 'stay' \| 'reset'`), `repeat` on the trigger, `clearOnStart`. |
|
|
67
|
+
| prepare / finalize / onEnter / onExit | Die → a `do` step at that position. |
|
|
68
|
+
| Directive, `dispatch`, `pendingDirective`, `flow.merge/validate`, five appliers | Die. Every position change goes through one `advance()`. Tools return `{ value?, data? }`. |
|
|
69
|
+
| Signal | → Flow with a `mention` trigger (§7). |
|
|
70
|
+
| CrmAutomation `trigger` | → `on[]`: `stage_entered`/`meeting_booked`/`tag_added`/`known_contact` → `event`; `stage_stagnant` → `event` + `after`; `signal` → `mention`; `conversation_idle` → `silence`; `manual` → no trigger. |
|
|
71
|
+
| CrmAutomation action / wait / wait_reply / condition | → `do` / `wait` / `wait` + `else` / `if`. |
|
|
72
|
+
| CrmAutomation `conditions`, `hopCount` | → trigger `if` as a JSON condition; `hop` rides on events and `then: { flow }`. |
|
|
73
|
+
| 14 actions | Host `do` actions, except `ai_message` → `prompt`, `send_whatsapp_message` → `say`, `start_automation` → `then: { flow }`. |
|
|
74
|
+
| StepIntegrations / flow integrations / step `media` / `initialData` | → `do` steps / `say` with `media` (`once: true` keeps "once per conversation"); `initialData` removed, no replacement. |
|
|
75
|
+
| `endBehavior` | → `onEnd`; *vai para outro fluxo* → `then: { flow }` on the last step. |
|
|
76
|
+
| `ai_message` + composer | → a `prompt` step reached by a wake. Composer deleted. |
|
|
77
|
+
| `haltReply` | → a `say` first in a mention flow: another run's `say` silences the floor's talk that turn. |
|
|
78
|
+
| behavior once/cooldown (two ledgers) | → one `claims` ledger (§5). |
|
|
79
|
+
| PersistenceAdapter ×7, `restoreSession`, `autoSave` | → `Store { load, save(expectedVersion) }`, `SessionConflictError`, `MemoryStore`, one `PostgresStore` reference. |
|
|
80
|
+
| `respondStream` | → `agent.turnStream()`. |
|
|
81
|
+
|
|
82
|
+
Core modules removed: ResponsePipeline, FlowRouter, ResponseModal, Signal*, AutoChainExecutor, StepLifecycle, DirectiveChainTracker, PersistenceManager, five adapters — about half of `src/core`. One `Runner` with one `advance()` plus `Understand.ts`, `Speak.ts`, `Envelope.ts`, `Migrate.ts`.
|
|
83
|
+
|
|
84
|
+
## 3. Public TypeScript API
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { falai, InferData, StructuredSchema, Instruction, tool } from '@falai/agent';
|
|
88
|
+
|
|
89
|
+
type Duration = `${number}${'s' | 'm' | 'h' | 'd'}`;
|
|
90
|
+
type Repeat = 'once' | 'always' | { cooldown: Duration };
|
|
91
|
+
type Template = string; // {{data.x}} {{context.x}} {{input.x}}
|
|
92
|
+
type Next = string | 'end' | { step: string; clear?: string[] } | { flow: Template; input?: unknown };
|
|
93
|
+
type PredCtx<C, D, P = unknown> = { context: C; data: Partial<D>; input: P; run: Run; silenced?: string; now: Date };
|
|
94
|
+
type ConditionSpec<Cond> = { [K in keyof Cond]?: Arg<Cond[K]> }; // JSON form; built-ins: equals, known, silenced
|
|
95
|
+
type Pred<C, D, Cond, P = unknown> = ((ctx: PredCtx<C, D, P>) => boolean) | ConditionSpec<Cond>;
|
|
96
|
+
type Branch<C, D, Cond> = { then: Next } & ({ when: string } | { if: Pred<C, D, Cond> });
|
|
97
|
+
|
|
98
|
+
type Trigger<C, D, Cond, E> = { repeat?: Repeat } & ( // default: message/mention/silence 'once', event 'always'
|
|
99
|
+
| { message: string[]; if?: Pred<C, D, Cond> } // "o cliente pede isso"; [] = catch-all
|
|
100
|
+
| { mention: string[]; extract?: StructuredSchema; if?: Pred<C, D, Cond> } // "o cliente fala disso"; [] + if = code-only
|
|
101
|
+
| { silence: Duration; if?: Pred<C, D, Cond>; businessHours?: boolean }
|
|
102
|
+
| { [K in keyof E]: { event: K; if?: Pred<C, D, Cond, E[K]>; after?: Duration; businessHours?: boolean } }[keyof E]
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
type Talk<C, D, Cond> = ({ prompt: Template; collect?: (keyof D)[] } | { collect: (keyof D)[]; prompt?: Template }) & {
|
|
106
|
+
ask?: Partial<Record<keyof D, string>>; // per-flow wording; schema `ask` is the default
|
|
107
|
+
maxAsks?: number; branches?: Branch<C, D, Cond>[]; tools?: string[]; instructions?: Instruction<C, D>[];
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
type Step<C, D, Cond, A extends ActionMap, E> = { id: string; label?: string; then?: Next; ui?: Record<string, unknown> } & (
|
|
111
|
+
| Talk<C, D, Cond>
|
|
112
|
+
| { say: Template; media?: { slug: string }; once?: boolean }
|
|
113
|
+
| { [K in keyof A]: { do: K; with: Params<A[K]>; onFail?: Next } }[keyof A]
|
|
114
|
+
| { wait: Duration; businessHours?: boolean; else?: Next; branches?: Branch<C, D, Cond>[] } // then = timed out, else = lead replied
|
|
115
|
+
| { [K in keyof E]: { wait: { event: K; upTo?: Duration }; else?: Next } }[keyof E] // then = event came, else = timed out (upTo default 30d)
|
|
116
|
+
| { if: Pred<C, D, Cond>; else?: Next } // then = true, else = false (default 'end')
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
interface Flow<C, D, Cond, A extends ActionMap, E> {
|
|
120
|
+
id: string; name: string; description?: string;
|
|
121
|
+
on?: Trigger<C, D, Cond, E>[]; // absent or [] = Início manual
|
|
122
|
+
anchor?: string; // 'session' (default) or a host anchor name — "vale por conversa / por lead"
|
|
123
|
+
while?: Pred<C, D, Cond>; // re-checked whenever the run moves; default = trigger `if`
|
|
124
|
+
clearOnStart?: (keyof D)[];
|
|
125
|
+
steps: Step<C, D, Cond, A, E>[]; // ids required, unique, never 'end'
|
|
126
|
+
onEnd?: 'end' | 'stay' | 'reset'; // default 'end'
|
|
127
|
+
instructions?: Instruction<C, D>[]; tools?: string[];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
type ActionResult = { ok: true; detail?: string; spoke?: true } | { skipped: string } | { failed: string } | { defer: Duration; detail: string };
|
|
131
|
+
interface ActionCtx<C, D> { context: C; data: Partial<D>; input: unknown; run: Run; key: string; dedupeKey: string; silenced?: string; now: Date; set(patch: Partial<D>): void }
|
|
132
|
+
type ToolResult<D> = { value?: unknown; data?: Partial<D> };
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
One factory carries the only explicit generic an app writes; everything else is inferred from values.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
interface LeadContext { lead: { id: string; name?: string; tags: string[]; stageId: string; assignee?: string; owner: 'ai' | 'human' }; phone: string }
|
|
139
|
+
const f = falai<LeadContext>(); // falai() with no context; createAgent = falai().agent
|
|
140
|
+
|
|
141
|
+
const fields = f.fields({
|
|
142
|
+
nome: { type: 'string', ask: 'Pergunte o nome de um jeito leve, sem tom de formulário.' },
|
|
143
|
+
empresa: { type: 'string', ask: 'Pergunte de qual empresa a pessoa fala.' },
|
|
144
|
+
tamanho: { type: 'string', enum: ['1-10', '11-50', '51-200', '200+'], ask: 'Pergunte quantas pessoas trabalham lá; ofereça as faixas.' },
|
|
145
|
+
urgencia: { type: 'string', enum: ['agora', '30 dias', 'sem prazo'], ask: 'Pergunte para quando precisam resolver.' },
|
|
146
|
+
orcamento: { type: 'number', ask: 'Pergunte a faixa de investimento, dizendo que é só para orientar.' },
|
|
147
|
+
confirmado:{ type: 'boolean', ask: 'Resuma em uma frase o que anotou e pergunte se está tudo certo.' }, // boolean → extract: 'asked' by default
|
|
148
|
+
});
|
|
149
|
+
type Data = InferData<typeof fields>;
|
|
150
|
+
|
|
151
|
+
const actions = {
|
|
152
|
+
notify: f.action({ parameters: { recipient: { type: 'string' }, message: { type: 'string' } },
|
|
153
|
+
run: async (w, ctx) => { await notifications.send(ctx.context.lead, w.recipient, w.message, { key: ctx.key }); return { ok: true, detail: 'aviso enviado' }; } }),
|
|
154
|
+
add_tags: f.action({ parameters: { tags: { type: 'array', items: { type: 'string' } } },
|
|
155
|
+
run: async (w, ctx) => { await crm.addTags(ctx.context.lead.id, w.tags); return { ok: true }; } }),
|
|
156
|
+
assign_lead: f.action({ parameters: { to: { type: 'string' } },
|
|
157
|
+
run: async (w, ctx) => (await ownership.assign(ctx.context.lead.id, w.to)) ? { ok: true } : { skipped: 'lead já está com uma pessoa' } }),
|
|
158
|
+
send_template: f.action({ parameters: { templateId: { type: 'string' } },
|
|
159
|
+
run: async (w, ctx) => { const r = await meta.sendPaid(ctx.context.phone, w.templateId, { idempotencyKey: ctx.key });
|
|
160
|
+
return r.noCredits ? { defer: '24h', detail: 'sem créditos' } : { ok: true, spoke: true }; } }),
|
|
161
|
+
book: f.action({ parameters: { date: { type: 'string' }, time: { type: 'string' } },
|
|
162
|
+
run: async (w, ctx) => { const ev = await agenda.create(w, { idempotencyKey: ctx.key }); ctx.set({ agenda_event_id: ev.id }); return { ok: true }; } }),
|
|
163
|
+
};
|
|
164
|
+
const events = {
|
|
165
|
+
stage_entered: f.event<{ stageId: string }>(),
|
|
166
|
+
meeting_booked: f.event<{ eventId: string }>(),
|
|
167
|
+
ig_comment: f.event<{ postId: string; text: string }>(),
|
|
168
|
+
reaction: f.event<{ emoji: string }>({ direction: 'inbound' }), // resolves reply waits, stamps lastUserAt
|
|
169
|
+
human_message: f.event<{ text: string }>({ direction: 'outbound' }), // stamps lastAssistantAt, re-arms silence
|
|
170
|
+
};
|
|
171
|
+
const conditions = { // plus built-ins equals / known / silenced
|
|
172
|
+
tagsAny: f.condition((ctx, tags: string[]) => tags.some(t => ctx.context.lead.tags.includes(t))),
|
|
173
|
+
inStage: f.condition((ctx, stageId: string) => ctx.context.lead.stageId === stageId),
|
|
174
|
+
channel: f.condition((ctx, kind: 'assistant' | 'campaign') => ctx.context.channel === kind),
|
|
175
|
+
};
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**S1 — Triagem, complete.** The confirmation is a collected boolean; a "no" clears it and re-asks; `avisa` runs only after the lead's ok.
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
const triagem = f.flow({
|
|
182
|
+
id: 'triagem', name: 'Triagem', description: 'Quando alguém chega querendo saber se o produto serve para a empresa dele',
|
|
183
|
+
on: [{ message: ['quer saber como funciona', 'pede um orçamento', 'quer saber se serve para a empresa'] }],
|
|
184
|
+
steps: [
|
|
185
|
+
{ id: 'quem', prompt: 'Descubra quem é e de onde fala.', collect: ['nome', 'empresa'] },
|
|
186
|
+
{ id: 'porte', collect: ['tamanho', 'urgencia'] },
|
|
187
|
+
{ id: 'grana', collect: ['orcamento'], maxAsks: 2 },
|
|
188
|
+
{ id: 'confirma', collect: ['confirmado'] },
|
|
189
|
+
{ id: 'ok', if: { equals: { confirmado: true } }, else: { step: 'quem', clear: ['confirmado'] } },
|
|
190
|
+
{ id: 'avisa', do: 'notify', with: { recipient: 'leadAssignee', message: 'Lead qualificado: {{data.nome}} ({{data.empresa}}), {{data.tamanho}} pessoas, {{data.urgencia}}.' } },
|
|
191
|
+
{ id: 'tchau', prompt: 'Agradeça e diga que um vendedor continua daqui.' },
|
|
192
|
+
],
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**S2 — Retomar quem sumiu, complete.** Started by the silence timer the framework arms after it speaks; any reply ends the run; while a human owns the lead the seller gets a reminder instead.
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
const retomar = f.flow({
|
|
200
|
+
id: 'retomar', name: 'Retomar quem sumiu',
|
|
201
|
+
on: [{ silence: '24h', businessHours: true, if: ({ context }) => context.lead.owner === 'ai' }],
|
|
202
|
+
anchor: 'lead',
|
|
203
|
+
steps: [
|
|
204
|
+
{ id: 'gate', if: { silenced: true }, then: 'lembra', else: 'p1' },
|
|
205
|
+
{ id: 'p1', prompt: 'Retome a conversa de forma leve: relembre o assunto em aberto e pergunte se ainda faz sentido.' },
|
|
206
|
+
{ id: 'w1', wait: '2d', else: 'end' },
|
|
207
|
+
{ id: 'p2', prompt: 'Última tentativa, curta e sem pressão: fica à disposição quando quiser retomar.' },
|
|
208
|
+
{ id: 'w2', wait: '3d', else: 'end' },
|
|
209
|
+
{ id: 'n1', do: 'notify', with: { recipient: 'leadAssignee', message: '{{data.nome}} não respondeu a duas retomadas — vale um contato manual.' }, then: 'end' },
|
|
210
|
+
{ id: 'lembra', do: 'notify', with: { recipient: 'leadAssignee', message: 'Hora de fazer follow-up com {{data.nome}}.' } },
|
|
211
|
+
],
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const agent = f.agent({
|
|
215
|
+
name: 'Ana', provider, fields, actions, events, conditions, tools: [checkAvailability],
|
|
216
|
+
flows: [triagem, retomar],
|
|
217
|
+
idle: { prompt: 'Responda pela empresa; não invente preços.' }, // the one speaker that is not a step; 'silent' mutes it
|
|
218
|
+
clock: () => new Date(), // tests pass fakeClock
|
|
219
|
+
businessHours: (at, ctx) => nextWorkingTime(at, ctx.context.lead), // snaps, never clamps
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Entry points and result:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
type TurnInput<C, D, E> = {
|
|
227
|
+
sessionId: string; session?: Session<D>; // absent = first turn; a wake never creates a session
|
|
228
|
+
context?: C; history?: History; // hosts pass history on EVERY input kind, wakes included
|
|
229
|
+
silenced?: string | { reason: string; understand?: boolean }; // host gate; default zero calls, understand: true opts in
|
|
230
|
+
anchors?: Record<string, { key: string; lastInboundAt?: string }>; // { lead: { key: 'lead:456', lastInboundAt } }
|
|
231
|
+
claims?: { held: Record<string, string>; active: string[] }; // dedupeKey → at; `${flowId}:${anchor}` live in other sessions
|
|
232
|
+
} & (
|
|
233
|
+
| { message: string; id?: string; at?: string } // hosts pass channel id + receipt time; the playground may omit
|
|
234
|
+
| { wake: string }
|
|
235
|
+
| { [K in keyof E]: { event: K; payload: E[K]; key: string; hop?: number } }[keyof E]
|
|
236
|
+
| { start: { flow: string; input?: unknown; key: string; hop?: number } }
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
interface TurnResult<D> {
|
|
240
|
+
session: Session<D>; changed: boolean; // changed: false → save nothing
|
|
241
|
+
messages: Array<{ text: string; kind: 'ai' | 'verbatim'; media?: { slug: string }; afterMs: number; key: string; runId?: string; stepId?: string }>;
|
|
242
|
+
schedule: Array<{ key: string; at: Date; replaces?: string }>; // jobId = key; at fire: turn({ wake: key })
|
|
243
|
+
outcomes: StepOutcome[];
|
|
244
|
+
started: Array<{ runId: string; flowId: string; anchor: string; dedupeKey: string }>;
|
|
245
|
+
ended: Array<Run & { reason: 'end' | 'flow' | 'reset' | 'skipped' | 'failed' | 'replaced' }>;
|
|
246
|
+
skipped: Array<{ flowId: string; anchor: string; triggerKey: string; detail: string }>; // trigger-level, for Execuções
|
|
247
|
+
llmCalls: number;
|
|
248
|
+
}
|
|
249
|
+
declare function turnStream(input: TurnInput): AsyncIterable<{ delta: string } | { done: true; result: TurnResult }>;
|
|
250
|
+
|
|
251
|
+
const r = await agent.turn({ sessionId: 'demo', message: 'oi' }); // README: one call, one obvious result
|
|
252
|
+
console.log(r.messages[0].text);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Stored flows load as `FlowSpec`: the same object as JSON with a **flat step** `{ id, kind: 'prompt' | 'collect' | 'say' | 'do' | 'wait' | 'waitEvent' | 'if', ...props, then?, else? }`, `do` names, condition names and field slugs resolved through the agent's registries. `validateFlow(spec, agent)` throws `FlowConfigurationError` naming the unknown field, action, event, condition or step id, the reserved id `end`, a missing `else` on a backward `if`, and warns on a backward edge without `clear`. `flowSpecSchema(agent)` returns FlowSpec's JSON schema with the workspace's actions (with their parameter schemas), events, conditions and fields enumerated — a closed schema Gemini accepts; it is the response schema of the host's generation call.
|
|
256
|
+
|
|
257
|
+
## 4. The turn pipeline
|
|
258
|
+
|
|
259
|
+
Eight phases, one order for every input; only 3 and 6 spend LLM calls. `turn()` does no I/O but the provider and the host's action handlers.
|
|
260
|
+
|
|
261
|
+
1. **Load** (code). Migrate a legacy blob (§10). `wake` with no session → `ignorado: sessão inexistente`, `changed: false`.
|
|
262
|
+
2. **Ingest** (code).
|
|
263
|
+
- `message` / inbound `event`: `lastUserAt = at ?? clock()`; a keyed `id` already in `session.inputs` → no-op, `changed: false`. Every run parked on a timer `wait` with `else` takes `else` (oldest first); a `wait: { event }` on this event takes `then`.
|
|
264
|
+
- Outbound `event`: stamps `lastAssistantAt`, nothing else.
|
|
265
|
+
- `wake` starting with `silence:`: honored only when its `lastAssistantAtMs` equals `session.lastAssistantAt` and the lead has not written since (`lastUserAt`, and the anchor's `lastInboundAt` for lead-anchored flows, both `< lastAssistantAt`); it then goes through the start order below with `trigger.kind: 'silence'`. Otherwise `ignorado: silêncio quebrado`, `changed: false`. Any other `wake`: the run whose `waiting.key` equals it, else `ignorado: wake antigo`. A timer `wait` with `else` whose lead wrote after `waiting.setAt` takes `else` — the reply beat the job.
|
|
266
|
+
- `event` / `start`: flows with a matching trigger start, in this order: `if` (payload as `input`) → `repeat` via claims (cooldown = interval check on `claims[key].at`) → `hop < 5` else `pulado: limite de encadeamento` → one active run per (flow, anchor) against `session.runs` and `claims.active`: a live run still parked on its own `after` timer is **replaced** (`ended.reason: 'replaced'`, its job self-skips), any other live run → `pulado: já em andamento` → `after` parks the new run.
|
|
267
|
+
- Every run about to move re-checks `while` with fresh context; false → ends `pulado: premissa mudou`. Silence-triggered runs add the premise "no lead message since the run started" → `pulado: lead escreveu nesse meio-tempo`.
|
|
268
|
+
3. **Understand** (≤1 call, `schemaName: 'understand'`). Only for `message` and inbound events with text; skipped under `silenced` unless `understand: true`, and when nothing is AI-conditioned. Candidates: the floor holder's flow (always, whatever its trigger), `message` flows passing `if` + `repeat`, `mention` flows with non-empty `mention` passing `if` + `repeat`. Envelope, every property required and nullable: `{ flows: { id: 0-100 }, mentions: { id: boolean }, extract: { id: {...} }, branches: { 'runId/stepId/i': boolean }, fields: { every pending field with extract 'anywhere' } }`. Shortcuts: exactly one eligible `message` flow and no floor → it starts, no scoring; zero candidates and zero pending fields → zero calls (S9).
|
|
269
|
+
4. **Decide** (code). Runs starting this turn apply `clearOnStart` first. Extracted values are checked: unknown keys dropped (`ignorado: campo desconhecido`), strings coerced to number/boolean, enum membership enforced, otherwise `campo descartado: valor fora da lista`; then written to `session.data` (one `known`: not `undefined | null | ''`). Mention runs start in flow order (a trigger `if` with `extract` sees `input` now); `mention: []` code-only detectors start here without the call. The first true branch of the asking step takes its `then`. Routing: if a run took or resumed the floor in Ingest, routing is skipped (scores recorded, not applied). Otherwise the floor (any run `running`/`asking`, not `waiting`) stays unless another flow scores ≥ current + 15 and ≥ 40; no floor → best ≥ 40 starts (a `suspended` run of that flow resumes instead), then a `message: []` catch-all, else the `idle` speaker.
|
|
270
|
+
5. **Run** (code). If no run is `asking`, the most recently `suspended` returns to `asking`. The Runner advances every run that can move, oldest first, through `advance()`: `do` runs the handler with `key = ${runId}:${stepId}:${visit}` (`run.visits[stepId]` increments on entry; at-least-once, before the save); `failed` writes a `failed` outcome and continues to `then` unless `onFail`; `skipped` continues; `defer` re-parks under a fresh wake key; `spoke: true` makes it the turn's speaker. `say` appends a message (`once` → claim `${flowId}:${stepId}:${sessionId}`). `wait ≤ 10s` carries as `afterMs` to the next message emitted this turn, or schedules a real wake if none follows; longer waits park with `waiting` and a `schedule` entry, snapped by `businessHours`. `if` picks `then`/`else`. A talk step whose fields are all known is skipped (0 calls); otherwise it is queued for phase 6 and the current asker becomes `suspended` (a run started this turn by routing never suspends one that took the floor in Ingest). Flow missing from the agent → `pulado: fluxo desativado ou removido`; step missing → `pulado: passo removido`. Caps: 50 steps per run per turn → `falhou: laço de passos`; hop 5.
|
|
271
|
+
6. **Speak** (≤1 call + tool rounds, `schemaName: 'speak'`). One talk step speaks. Talk step = `prompt`, `collect`, `say`. Rules: a `say` or `spoke: true` emitted this turn by a run **other than** the floor holder, in reply to a user message, silences the floor's talk (`pulado: outra resposta já saiu`; the run stays `asking`) — a run's own `say` never silences its own talk. Under `silenced` no message-producing step runs and no run advances past one: an `asking` run stays `asking`; a run whose talk step was reached by a wake, event or start ends `silenciado: <reason>` (an earlier `if: { silenced: true }` routes around it); `do` steps still run; zero calls. Prompt = identity + flow + step guideline + this step's pending fields with their `ask` (all of them; the step prompt says how many to ask) + known fields as facts + tools + instructions + history. A wake states *não há mensagem nova do cliente; você fala primeiro*. Envelope `{ message, ...pending fields of this step }`, all required and nullable, shallow-merged last-wins across tool rounds. Provider failure: in phase 3 the turn throws `ProviderError` (nothing ran, nothing saved; the host retries the input); in phase 6 the turn returns with the talk step re-parked under `${runId}:${stepId}:${visit}:retry:${atMs}` (+1m, +5m, +15m), outcome `deferred: IA indisponível`, session saved, phase 5 effects not repeated.
|
|
272
|
+
7. **Settle** (code, the ONE applier). A collect step stays `asking` until its fields are known, a branch fires, or a field hits `maxAsks` (default 3 → `campo pulado: perguntado 3 vezes`); `asked[field]` increments when the step spoke with the field pending and the next lead message left it unknown — detours count, the ceiling is documented. A prompt without `collect` advances after speaking once. `then`: step (visits++), `{ step, clear }` (clears, then jumps), `'end'` → `onEnd`, `{ flow }` (template resolved against `input`/`context`; this run ends `reason: 'flow'`, the new run has `trigger.kind: 'flow'`, key `${parentRunId}:${stepId}:${visit}`, hop+1, and holds the floor). When no run is `asking`, the most recently suspended resumes. If the assistant spoke last, every `silence` flow passing `if` and `repeat` gets `schedule: { key: silence:${flowId}:${sessionId}:${lastAssistantAtMs}, at, replaces: <previous key> }`.
|
|
273
|
+
8. **Return**: `session` (version unchanged; the host bumps it), `changed`, `messages` in emission order, `schedule`, `outcomes`, `started`, `ended`, `skipped`, `llmCalls`.
|
|
274
|
+
|
|
275
|
+
Budget: message turn 2 calls + tool rounds (1 with a single flow and no pending fields, 0 when silenced); wake to a talk step 1; wake to `do`/`wait` 0. `llmCalls` on every result makes it a test, not a promise.
|
|
276
|
+
|
|
277
|
+
## 5. Waiting, timers, events
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
interface Run {
|
|
281
|
+
id: string; // `${flowId}#${triggerKey}` — deterministic, replay mints the same keys
|
|
282
|
+
flowId: string; anchor: string; dedupeKey: string; stepId: string | null;
|
|
283
|
+
status: 'running' | 'asking' | 'waiting' | 'suspended';
|
|
284
|
+
trigger: { kind: 'message' | 'mention' | 'silence' | 'event' | 'start' | 'flow'; key: string; payload?: unknown };
|
|
285
|
+
input?: unknown; hop: number; startedAt: string;
|
|
286
|
+
waiting?: { kind: 'timer' | 'event'; key?: string; until?: string; setAt: string; event?: string };
|
|
287
|
+
asked: Record<string, number>; visits: Record<string, number>;
|
|
288
|
+
outcomes: StepOutcome[];
|
|
289
|
+
}
|
|
290
|
+
interface StepOutcome { runId?: string; flowId?: string; stepId?: string; key?: string;
|
|
291
|
+
kind: 'prompt' | 'collect' | 'say' | 'do' | 'wait' | 'if' | 'idle'; status: 'ok' | 'skipped' | 'failed' | 'waiting' | 'deferred';
|
|
292
|
+
detail?: string; next?: string; until?: string; at: string; llmCalls?: number }
|
|
293
|
+
|
|
294
|
+
interface Store<D> { load(id: string): Promise<Session<D> | null>; save(session: Session<D>, expectedVersion: number): Promise<void> } // 0 = insert-if-absent; throws SessionConflictError
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Invariants** (docs verbatim):
|
|
298
|
+
|
|
299
|
+
- I1. The session blob is the unit of consistency: `load(v) → turn → save(expectedVersion = v)`. A losing turn is discarded and the same input replayed.
|
|
300
|
+
- I2. Messages, schedules, claims and ended runs leave the host's hands only after a successful save. `do` handlers are the exception: they run inside the turn, **at-least-once**, and must be idempotent on `ctx.key` (or `ctx.dedupeKey` for `once`/cooldown flows shared across a lead's sessions). `always` flows cannot be made exactly-once across sessions by the framework.
|
|
301
|
+
- I3. A run wake is honored only by the run whose `waiting.key` equals it; a silence wake only when the blob still shows that silence. Everything else is `ignorado`, `changed: false`. Nothing is ever cancelled; `replaces` is a best-effort hint.
|
|
302
|
+
- I4. Every `do` and message carries `key = ${runId}:${stepId}:${visit}`; run ids come from host keys (message `id`, event/start `key`, wake key, parent run), so a replay of the same input mints the same keys and a revisit mints new ones.
|
|
303
|
+
- I5. A run's claim is written in the same save as its first step; a run never exists without its claim.
|
|
304
|
+
|
|
305
|
+
**Keys.** Trigger key: `message`/`mention` → the input `id` (playground: `at`, replays not idempotent); `silence` → `lastAssistantAtMs`; `event`/`start` → the host key; `flow` → `${parentRunId}:${stepId}:${visit}`. Wake key: `${runId}:${stepId}:${atMs}`. Dedupe key `${flowId}:${anchor}:${nonce}`, nonce `''` for `once` and cooldown (blocked while `now - claims[key].at < cooldown`, else overwritten), the trigger key for `always` (last 50 kept).
|
|
306
|
+
|
|
307
|
+
**Host contract:** (a) one `turn` per session at a time; a wake queues behind a debounced inbound not yet turned. (b) On every input: fresh `context`, `history`, `anchors` (with the lead's `lastInboundAt`), `claims` for non-`always` flows, `silenced` for every "cannot speak now" reason (ownership, Pausa, closed 24h window, quota, `sem conversa`); inbound messages carry the channel `id` and `at`. (c) `changed: false` → nothing. Else one transaction: `store.save`, `started[].dedupeKey` into a unique index, `started`/`ended` into a partial unique index `(flowId, anchor) WHERE live`, `outcomes`/`ended`/`skipped` into the `flowRuns` mirror, `messages[]` and `schedule[]` into the outbox. Conflict or unique violation → discard, replay. (d) Drain the outbox: send honoring `afterMs`, record a refused send against `key` in the mirror (`enviada`/`recusada` beside the framework's `gerada`); enqueue wakes with `jobId = key`, `queue.remove(replaces)` best-effort. (e) At fire: `turn({ wake, silenced })`. (f) Call `turn` for every inbound even while a human owns the lead. (g) `businessHours(at)` snaps, never clamps. (h) Lead-level events go to the lead's latest open conversation; none → session `lead:<id>` with `silenced: 'sem conversa'`. (i) `ProviderError` → retry the input with backoff.
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
const clock = fakeClock('2026-09-20T10:00Z');
|
|
311
|
+
const agent = f.agent({ provider: mockProvider({ understand: [...], speak: [...] }), clock, fields, actions: stubs, flows: [triagem, retomar] });
|
|
312
|
+
let r = await agent.turn({ sessionId: 's1', message: 'oi', id: 'm1' });
|
|
313
|
+
clock.advance('24h');
|
|
314
|
+
r = await agent.turn({ sessionId: 's1', session: r.session, wake: r.schedule[0].key, history });
|
|
315
|
+
assert.equal(r.messages[0].kind, 'ai'); assert.equal(r.llmCalls, 1); assert.equal(r.started[0].flowId, 'retomar');
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## 6. Field collection
|
|
319
|
+
|
|
320
|
+
`pending(step) = step.collect in order, minus known fields, minus fields at maxAsks`, computed by code; the model sees every pending field of the current step with its `ask` (step `ask` overrides the schema's). Fields are authored once: `{ type, enum?, description?, ask, extract?: 'anywhere' | 'asked' }`. `'anywhere'` (default for strings and numbers) is harvested by the understand call from any text; `'asked'` (default for booleans) only by the speak envelope of the step that lists it, so a stray "sim" never opens a code gate. `toWireSchema()` allow-lists JSON-schema keys and strips `ask`/`extract`. In the UI a field row is `{ slug, rótulo, tipo, como perguntar }`; `buildSchema(flows)` merges rows by slug and fails when two flows give one slug two types; suffix-based inference dies.
|
|
321
|
+
|
|
322
|
+
Out of order is the default. A collect step skips when its fields are known and stays `asking` until they are; a `prompt` without `collect` speaks once. Confirmation is a collected boolean behind an `if`; a correction clears it (`{ step, clear }`) and re-asks. `maxAsks` (default 3, loud in Execuções) replaces the `requires` deadlock; `maxAsks: 1` is "ask once, don't insist". `clearOnStart` (applied before this turn's extraction) replaces `reentrant`.
|
|
323
|
+
|
|
324
|
+
## 7. Signals → triggers
|
|
325
|
+
|
|
326
|
+
| Signal facet | v4 |
|
|
327
|
+
|---|---|
|
|
328
|
+
| `when[]` incl. `!` exclusions | `mention: [...]`, judged in the understand call |
|
|
329
|
+
| `if` | trigger `if`, free; with `extract` set it also sees `input` after the call; `mention: []` + `if` = code-only detector, no call |
|
|
330
|
+
| `extract` | trigger `extract` → `run.input`, `{{input.x}}`, never `data` |
|
|
331
|
+
| `phase: 'pre'` + `halt` + `reply` | first step `say` (or a `do` returning `spoke: true`); another run's say silences the floor that turn |
|
|
332
|
+
| `phase: 'post'` | dies; `do`-only mention flows run beside the reply, same turn |
|
|
333
|
+
| `behavior` once/always/cooldown | `repeat` → claim, one ledger, interval cooldown |
|
|
334
|
+
| `priority` | dies; flow order |
|
|
335
|
+
| `stopOtherSignals` | dies; one speaker per turn |
|
|
336
|
+
| handler side effects | `do` steps |
|
|
337
|
+
|
|
338
|
+
## 8. Concurrency and anchors
|
|
339
|
+
|
|
340
|
+
Session = conversation. Runs inside a session are concurrent; the floor is single: at most one run is `asking`; a talk step from another run pushes it onto a LIFO stack of `suspended` runs, and whenever nobody is asking the most recently suspended resumes. Order inside a turn: reply-branch resolutions → mention runs → floor talk; `messages[]` in that order.
|
|
341
|
+
|
|
342
|
+
Anchors are host keys per call; a flow names one or defaults to the session. Inside the session the framework dedupes; across a lead's sessions the host passes `claims.held` (nonce rule) and `claims.active` (live `${flowId}:${anchor}` pairs from its mirror) and writes both indexes in the save transaction. A lead-less campaign thread falls back to the session anchor.
|
|
343
|
+
|
|
344
|
+
S10 with versions. R1 (triagem) `asking`; R2 (retomar) `waiting` on W1. W1 fires: load v7 → premise holds → `p1` speaks (1 call) → `w1` parks, `schedule(W2)` → save v7→v8. The lead's text (id m9, at 10:00:02) lands concurrently: load v7 → save fails → replay on v8 → `w1.else` ends R2 (W2 fires stale later) → R1, still the floor, extracts and asks (2 calls) → save v8→v9. Wire: nudge, question. Had the text arrived first (or been debounced, per (a)), the wake finds `lastUserAt > setAt` → `w1.else` → no nudge.
|
|
345
|
+
|
|
346
|
+
## 9. Flow end
|
|
347
|
+
|
|
348
|
+
A run ends when `then` reaches `'end'` or the last step completes; `onEnd`: `'end'` (default; `ended[]` carries it, session idle), `'stay'` (*repete o último passo*), `'reset'` (first step, data kept). *Vai para outro fluxo* is `then: { flow }` on the last step. The framework never speaks at the boundary. A `message` flow is `repeat: 'once'` per session by default; S8 sets `'always'` + `clearOnStart`.
|
|
349
|
+
|
|
350
|
+
## 10. Persistence
|
|
351
|
+
|
|
352
|
+
```ts
|
|
353
|
+
interface Session<D> {
|
|
354
|
+
id: string; v: 4; version: number;
|
|
355
|
+
data: Partial<D>; runs: Run[]; // live runs only
|
|
356
|
+
claims: Record<string, { at: string }>; // once/cooldown: one key per flow; always: last 50
|
|
357
|
+
inputs: string[]; // last 50 keyed input ids
|
|
358
|
+
lastUserAt?: string; lastAssistantAt?: string;
|
|
359
|
+
history?: History; // playground only
|
|
360
|
+
metadata: Record<string, unknown>;
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
`migrateSession(blob, { flowIdOf })` runs at each app's choke point (`deserializeFalaiSessionState`): no `v` → `data` verbatim; `currentFlow/currentStep` → one run `{ id: '${flowId}#legacy', stepId, status: 'asking', trigger: { kind: 'message', key: 'legacy' }, visits: {} }`; `signals.triggers[key]` and `flowHistory[].completed` → `claims['${flowIdOf(key)}:${sessionId}:']`; `pendingDirective` dropped. `migrateFlows` gives signal-triggered flows `id = trigger.signal` so `flowIdOf` is identity for ilojista's 315 live once-signals — a real ilojista blob is a test fixture. Talk-step ids survive, so 844 + 315 cursors keep position; split-out steps get `${stepId}:media` / `${stepId}:integration`.
|
|
365
|
+
|
|
366
|
+
## 11. Consumer migration
|
|
367
|
+
|
|
368
|
+
**One stored object.** Table `flows` (replaces `workspace.agent.flows` and `crmAutomationRules`): `id, workspaceId, name, kind ('user' | 'system'), enabled, position, section (derived), stageId?, spec JSONB`. `migrateFlows.ts`: AgentFlow → `on: [{ message: when, if: { channel } }]` from `contexts`; steps → `prompt`+`collect` (media → preceding `say` with `once`, integrations → following `do`, branches → `branches[]` on the step); `onEnd: endBehavior ?? 'stay'` (special flows `'end'`), `redirect` → `then: { flow }`; `{{lead_nome}}`-style variables rewritten to `{{context.lead.name}}` from a table; canvas positions → `ui`. CrmAutomation → `on` per §2, steps 1:1, `conditions` → trigger `if`, `haltReply` → first `say`. `defaultFlows(workspace)` (interesse, pediu_humano, known_contact → Triagem, meeting_booked → rodízio, agenda, bot_detected, conversation_ended) seeds `kind: 'system'` rows where `seedDefaultAutomationRules` runs today. The worker builds `f.agent({ flows: rows.map(r => r.spec) })` once per workspace config version.
|
|
369
|
+
|
|
370
|
+
**One editor.** *Fluxos*: *Título*, *Descrição*, *Quando ativar* (*o cliente pede isso* · *o cliente fala disso* · *acontece algo* · *silêncio do cliente* · *início manual*), *Só se*, *Vale por: conversa / lead*, *Repetição*, *Continuar só enquanto*, *Campos* with *Como perguntar*, *Ao recomeçar, esquecer*, *Ao terminar*, *Regras só deste fluxo*. Canvas *+ Adicionar passo*: *Mensagem & IA* (Enviar mensagem = `say`, IA escreve a próxima mensagem = `prompt`, Perguntar = `collect` with *Insistir até N vezes* and *Ramificação (a IA decide)* edges, Enviar modelo aprovado = `do send_template`), *Esperar* (`wait`, edges *Sem resposta* / *Respondeu*), *CRM* (`do` …, Condição = `if`, edges *Sim* / *Não*), *Equipe & integrações* (`do` …, Iniciar outro fluxo = `then: { flow }` edge). *Execuções* reads `flowRuns`: row upserted from `started[]`, status = last outcome (`Parcial` when a failed/skipped sits beside an ok), `aguardando até` from `until`, *Seguiu "Respondeu"* from `next`, trigger skips from `skipped[]`.
|
|
371
|
+
|
|
372
|
+
**One assistant tool.** `criar_fluxo({ descricao })` runs the host's generation call with `flowSpecSchema(agent)` as response schema, then `validateFlow`, then saves; `ajustar_fluxo`, `ver_fluxos`. `criar_automacao` and siblings die.
|
|
373
|
+
|
|
374
|
+
**Per app (prospectar paths; siblings identical):**
|
|
375
|
+
|
|
376
|
+
| Delete | Move | Rewrite |
|
|
377
|
+
|---|---|---|
|
|
378
|
+
| `apps/worker/src/services/crm-automation.service.ts` (1,548), `automation-message-composer.service.ts`, `conversation-idle-sweep.service.ts`, `integration.service.ts`, `utils/integration-behavior.utils.ts`, `utils/follow-up-time.utils.ts`; `apps/api/src/services/crm-automation.service.ts`, `assistant-tools/automation.tools.ts`, `backfill-scheduling-flow.ts`; `packages/server/.../scheduling-flow.utils.ts`, `signals.builder.ts`, `modules/crm/signal-rule.mapper.ts`; `contracts/.../automation.types.ts`; AgentFlow/Step/StepIntegrations/FlowEndBehavior in `agent-core.types.ts`; `apps/web/src/components/Automation/**` (4,127) | From `flow.utils.ts`: `buildStructuredKnowledgeBase` → `knowledge.utils.ts`, `buildFalaiInstructions` → `instructions.builder.ts`; the rest of the file dies. `schema.utils.ts` → 30-line `buildSchema`. `automationLabels.ts`/`automationRecipes.ts` → `flowLabels.ts`/`flowRecipes.ts`. DEFAULT_SIGNALS → `defaultFlows.ts` | `falai-agent.factory.ts` (one agent per config version, no per-turn rebuild, `buildNativeSignals`/`createStepMediaHook` gone); `ai.service.ts` (`detectFlowEndThisTurn`, `reconcilePersistedSignalState`, `pinCampaignEntryFlows` gone; `runTurn` from §12); `apps/web/src/components/Agent/**` (5,169) merged with the automation canvas into one editor (~9.3k lines of UI touched); api call sites `overview.tool.ts`, `campaign.service.ts` (followUp → `flowId`), `playground.service.ts` (`immediateAutomationActions`, `buildSignalAiEffects` → FlowSpec walkers), `agent-entity.service.ts`, `onboarding-activation.service.ts`, `automation-rule.repository.ts` → `flow.repository.ts`; `StartAutomationDialog`/`SidePanel` graph walkers ported to FlowSpec |
|
|
379
|
+
|
|
380
|
+
New host code (~1k lines): `actions.ts` (11 handlers), `wake.worker.ts`, event publishers, `migrateFlows.ts`, `defaultFlows.ts`, the transactional save. Stays host-side: the gate (→ `silenced`), channels, CRM writes inside actions, BullMQ, history building, the `flowRuns` mirror, the Instagram comment matcher.
|
|
381
|
+
|
|
382
|
+
**Framework side.** Tests: about 50 of 76 files exercise routing, directives, signals, completion, adapters or the `respond()` shape and are deleted or rewritten; ~25 kept (providers, envelope salvage, streaming decoder, tool loop, templates, history, schema, prompt-section-cache); new: `understand`, `runner`, `wakes` (fakeClock + MemoryScheduler), `events`, `session-v4` + legacy fixtures (prospectar and ilojista blobs), one file per scenario. `mockProvider({ understand, speak })` holds FIFO queues per schema name (last entry repeats) and records `.calls`. Docs rewritten in place; `docs/migration/v3-to-v4.md` with a Removed | Replacement table and the blob recipe.
|
|
383
|
+
|
|
384
|
+
## 12. Scenarios
|
|
385
|
+
|
|
386
|
+
**S1 Triage.** "oi, quero saber como funciona" (id m1) → understand scores `triagem` (1 call) → `quem` asks (1 call). "João, da Acme, somos 30" → nome, empresa, tamanho land → `quem` complete, `porte` asks only urgência. "pra ontem, quanto custa?" → urgência lands; triagem is the floor and scored; `grana` answers price from the KB and asks orçamento. Two evasions → `maxAsks: 2` → `campo pulado`. `confirma` asks; "não, somos 50" → `confirmado: false`, tamanho updated → `ok` false → `clear: ['confirmado']`, `quem`/`porte`/`grana` skip, `confirma` re-asks. "sim" → `avisa` (key `…:avisa:1`) → `tchau` → `'end'`; "obrigado" gets the `idle` reply. 2 calls per turn.
|
|
387
|
+
|
|
388
|
+
**S2 Cadence.** Assistant speaks at T → `schedule(silence:retomar:s1:T)`. Wake: premise holds → run starts (claim now) → `gate` false → `p1` speaks first (1 call) → `w1` parks. Any reply on either channel → `w1.else` → end (the anchor's `lastInboundAt` covers the other channel at the next wake). Timeout → `p2` → `w2` → `n1` (0 calls). Human owns → `gate` true → `lembra` notifies the seller, run ends.
|
|
389
|
+
|
|
390
|
+
**S3 No-show.** `turn({ event: 'stage_entered', payload: { stageId }, key: 'stage:456:T' })` → `if: { inStage: 'nao-compareceu' }, after: '1h'` parks; a re-entry replaces it. Wake → `while` holds → `prompt` + `collect: ['data_preferida', 'horario_preferido']` with agenda tools speaks first (the lead having written at T+20m does not stop it: the premise is the stage), suspends triage, collects → `do book` → host emits `meeting_booked` → *Reunião marcada* notifies. Triage resumes when no-show stops asking.
|
|
391
|
+
|
|
392
|
+
**S4 Pediu humano.** `on: [{ mention: ['quer falar com uma pessoa'], repeat: { cooldown: '1h' } }]`, steps `say 'Já chamo alguém'` → `do assign_lead` → end. The say silences triage's talk that turn; triage stays `asking`. While a human owns, `silenced` turns cost 0 calls. Handback needs no event: the next message finds triage at the same step. A second request an hour later starts a fresh run.
|
|
393
|
+
|
|
394
|
+
**S5 Campaign.** `turn({ start: { flow: 'campanha', input: { campaignId, templateId, flowId }, key } })` → `do send_template` (`spoke: true`, `defer` on no credits) → `wait '1d', else: { flow: '{{input.flowId}}' }` → timeout → `prompt` nudge → `wait '2d', else: { flow: '{{input.flowId}}' }` → end. A reply enters the campaign's own funnel, which holds the floor: routing that turn is skipped. 0 calls until the nudge.
|
|
395
|
+
|
|
396
|
+
**S6 Instagram.** `ig_comment` on the IG session → `say` DM → `wait '1d', else: 'qualifica'` → `prompt` → `then: { flow: 'agendar' }`. `anchor: 'lead'` + `claims.active` stop a second run on WhatsApp.
|
|
397
|
+
|
|
398
|
+
**S7 Rule from chat.** Same FlowSpec, same table, same card; fires inside the understand call, no talk step, runs beside the reply, once per conversation.
|
|
399
|
+
|
|
400
|
+
```json
|
|
401
|
+
{ "id": "concorrente", "name": "Lead falou de concorrente",
|
|
402
|
+
"on": [{ "mention": ["o lead cita ou compara com um concorrente"], "extract": { "trecho": { "type": "string" } }, "repeat": "once" }],
|
|
403
|
+
"steps": [
|
|
404
|
+
{ "id": "tag", "kind": "do", "do": "add_tags", "with": { "tags": ["concorrente"] } },
|
|
405
|
+
{ "id": "avisa", "kind": "do", "do": "notify", "with": { "recipient": "owner", "message": "{{data.nome}} falou de concorrente: \"{{input.trecho}}\"" } }
|
|
406
|
+
] }
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
```ts
|
|
410
|
+
const criarFluxo = tool({
|
|
411
|
+
id: 'criar_fluxo', description: 'Cria um fluxo a partir do que o dono pediu no chat',
|
|
412
|
+
parameters: { descricao: { type: 'string' } },
|
|
413
|
+
handler: async ({ descricao }, ctx) => {
|
|
414
|
+
const spec = await generateFlowSpec(descricao, flowSpecSchema(agent)); // host generation call, closed schema
|
|
415
|
+
validateFlow(spec, agent); await db.flows.insert(ctx.context.workspaceId, spec);
|
|
416
|
+
return { value: { criado: spec.id } };
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
**S8 Scheduling.** `on: [{ message: ['quer marcar', 'quer remarcar'], repeat: 'always' }]`, `clearOnStart: ['data_preferida', 'horario_preferido', 'agenda_event_id', 'confirmado']` (applied before "sexta às 15h" lands), collect with agenda tools, `{ collect: ['confirmado'], ask: { confirmado: 'Confirme a data e o horário em uma frase.' } }`, `do book`, last step `then: { flow: 'pos-agendamento' }`.
|
|
422
|
+
|
|
423
|
+
**S9 Config assistant.** Zero flows, zero fields → understand skipped → one speak call with tools. Interview mode: one `message` flow → single-flow shortcut, no scoring; collect steps skip when known.
|
|
424
|
+
|
|
425
|
+
**S10.** §8.
|
|
426
|
+
|
|
427
|
+
**S11 Playground.** `agent.turn({ sessionId, session, message })` without `id`, `MemoryStore`, `schedule` fed to `MemoryScheduler.due(now)` or ignored; actions return `{ ok: true }`; mention flows run as no-ops and show in `started`.
|
|
428
|
+
|
|
429
|
+
**S12 AI speaks first, from a timer, mid-flow.** A `prompt` reached by a wake runs through the same agent, prompt, tools, instructions, `session.data` and the `history` the host passes; the gate arrives as `silenced`.
|
|
430
|
+
|
|
431
|
+
```ts
|
|
432
|
+
const propostaParada = f.flow({
|
|
433
|
+
id: 'proposta-parada', name: 'Proposta parada',
|
|
434
|
+
on: [{ event: 'stage_entered', if: { inStage: 'proposta-enviada' }, after: '3d', businessHours: true }],
|
|
435
|
+
steps: [
|
|
436
|
+
{ id: 'p1', prompt: 'A proposta foi enviada há três dias sem retorno. Pergunte, em uma frase, se ficou alguma dúvida — use o que já sabe sobre {{data.empresa}}.' },
|
|
437
|
+
{ id: 'w1', wait: '2d', else: 'end' },
|
|
438
|
+
{ id: 'n1', do: 'notify', with: { recipient: 'leadAssignee', message: 'Proposta de {{data.nome}} sem resposta há 5 dias.' } },
|
|
439
|
+
],
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
wakeQueue.process(async (job: { data: { sessionId: string; key: string } }) => {
|
|
443
|
+
const { sessionId, key } = job.data;
|
|
444
|
+
await runTurn(sessionId, { wake: key });
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
async function runTurn(sessionId: string, input: TurnInputBody) {
|
|
448
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
449
|
+
const [session, context, history] = await Promise.all([store.load(sessionId), loadLeadContext(sessionId), loadHistory(sessionId)]);
|
|
450
|
+
const anchors = { lead: { key: `lead:${context.lead.id}`, lastInboundAt: context.lead.lastInboundAt } };
|
|
451
|
+
const r = await agent.turn({ ...input, sessionId, session, context, history, anchors,
|
|
452
|
+
claims: await ledger.claims(anchors), silenced: await gate.reason(sessionId) });
|
|
453
|
+
if (!r.changed) return;
|
|
454
|
+
try {
|
|
455
|
+
await db.transaction(async (tx) => {
|
|
456
|
+
await store.save(r.session, session?.version ?? 0); // CAS, 0 = insert
|
|
457
|
+
await ledger.write(tx, r.started, r.ended, r.outcomes, r.skipped); // unique(dedupeKey), partial unique(flowId, anchor) WHERE live, Execuções
|
|
458
|
+
await outbox.put(tx, r.messages, r.schedule); // wakes ride in the same transaction
|
|
459
|
+
});
|
|
460
|
+
} catch (e) { if (e instanceof SessionConflictError || isUniqueViolation(e)) continue; throw e; }
|
|
461
|
+
await outbox.drain(sessionId); // send honoring afterMs, enqueue wakes jobId = key
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
**S13 TRID script.** `[{ id: 'a', say: 'Oi! Aqui é da TRID.' }, { id: 'p', wait: '3s' }, { id: 'b', say: 'Chegou o iPhone 17, pronta entrega.' }, { id: 'q', prompt: 'Pergunte qual modelo interessa.', collect: ['modelo'] }]` → one turn, `messages: [A, B afterMs 3000, C]`; own says never silence own talk. 2 calls (understand + speak).
|
|
468
|
+
|
|
469
|
+
## 13. Risks
|
|
470
|
+
|
|
471
|
+
1. The understand call bundles routing, mentions, branches and extraction; a weak model may score routing worse than today's dedicated call. Run a scenario eval on GLM and Gemini before cutover.
|
|
472
|
+
2. The speak envelope with pending fields must be probed per provider (Gemini strict, Zai prompt-only); `toWireSchema()` asserts `isStrictSchema` in tests.
|
|
473
|
+
3. A wake-started talk step suspending a live conversation is new UX; the silence premise and `while` are the guards.
|
|
474
|
+
4. Dropping five adapters affects npm users beyond the three consumers.
|
|
475
|
+
5. Three apps migrate blobs, stored flows and ~9k lines of editor UI in lockstep with the worker rewrite; the shared FlowSpec is the chance to share one package.
|
|
476
|
+
6. Instruction precedence ("cara ou coroa") is untouched.
|
|
477
|
+
7. `do` is at-least-once before the save; a host handler that ignores `ctx.key` can double-fire on a CAS replay.
|