@falai/agent 4.0.0-alpha.13 → 4.0.0-alpha.15
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 +5 -3
- package/dist/cjs/core/Agent.js +9 -0
- 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 +8 -3
- package/dist/cjs/core/CompactionEngine.js.map +1 -1
- package/dist/cjs/core/FlowSpec.d.ts +19 -2
- package/dist/cjs/core/FlowSpec.d.ts.map +1 -1
- package/dist/cjs/core/FlowSpec.js +256 -57
- package/dist/cjs/core/FlowSpec.js.map +1 -1
- package/dist/cjs/core/Prompt.d.ts +16 -0
- package/dist/cjs/core/Prompt.d.ts.map +1 -1
- package/dist/cjs/core/Prompt.js +39 -0
- package/dist/cjs/core/Prompt.js.map +1 -1
- package/dist/cjs/core/Runner.d.ts +2 -0
- package/dist/cjs/core/Runner.d.ts.map +1 -1
- package/dist/cjs/core/Runner.js +26 -15
- package/dist/cjs/core/Runner.js.map +1 -1
- package/dist/cjs/core/Speak.d.ts.map +1 -1
- package/dist/cjs/core/Speak.js +22 -16
- package/dist/cjs/core/Speak.js.map +1 -1
- package/dist/cjs/core/Understand.d.ts +4 -3
- package/dist/cjs/core/Understand.d.ts.map +1 -1
- package/dist/cjs/core/Understand.js +5 -38
- package/dist/cjs/core/Understand.js.map +1 -1
- package/dist/cjs/core/contracts.d.ts +5 -5
- package/dist/cjs/core/contracts.d.ts.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/persistence/OpenSearchStore.d.ts +2 -1
- package/dist/cjs/persistence/OpenSearchStore.d.ts.map +1 -1
- package/dist/cjs/persistence/OpenSearchStore.js +2 -2
- package/dist/cjs/persistence/OpenSearchStore.js.map +1 -1
- package/dist/cjs/persistence/RedisStore.d.ts +1 -1
- package/dist/cjs/persistence/RedisStore.d.ts.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.d.ts +2 -5
- package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.js +3 -4
- package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
- package/dist/cjs/providers/DeepSeekProvider.d.ts.map +1 -1
- package/dist/cjs/providers/DeepSeekProvider.js +3 -4
- package/dist/cjs/providers/DeepSeekProvider.js.map +1 -1
- package/dist/cjs/providers/FallbackAiProvider.js +1 -1
- package/dist/cjs/providers/FallbackAiProvider.js.map +1 -1
- package/dist/cjs/providers/GeminiProvider.d.ts +1 -2
- package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/cjs/providers/GeminiProvider.js +3 -4
- package/dist/cjs/providers/GeminiProvider.js.map +1 -1
- package/dist/cjs/providers/GenericOpenAICompatibleProvider.js +4 -4
- package/dist/cjs/providers/GenericOpenAICompatibleProvider.js.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.js +3 -4
- package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.js +4 -3
- package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
- package/dist/cjs/providers/ProviderAdapter.d.ts +12 -9
- package/dist/cjs/providers/ProviderAdapter.d.ts.map +1 -1
- package/dist/cjs/providers/ProviderAdapter.js +8 -3
- package/dist/cjs/providers/ProviderAdapter.js.map +1 -1
- package/dist/cjs/providers/ZaiProvider.js +1 -1
- package/dist/cjs/providers/ZaiProvider.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +1 -1
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/ai.d.ts +4 -6
- package/dist/cjs/types/ai.d.ts.map +1 -1
- package/dist/cjs/types/compaction.d.ts +1 -0
- package/dist/cjs/types/compaction.d.ts.map +1 -1
- package/dist/cjs/types/errors.d.ts +4 -9
- package/dist/cjs/types/errors.d.ts.map +1 -1
- package/dist/cjs/types/errors.js +12 -12
- package/dist/cjs/types/errors.js.map +1 -1
- package/dist/cjs/types/history.d.ts +0 -7
- package/dist/cjs/types/history.d.ts.map +1 -1
- package/dist/cjs/types/index.d.ts +1 -1
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/cjs/types/session.d.ts +1 -1
- package/dist/cjs/types/session.d.ts.map +1 -1
- package/dist/cjs/utils/clock.js +1 -1
- package/dist/cjs/utils/clock.js.map +1 -1
- package/dist/cjs/utils/schema.d.ts +2 -11
- package/dist/cjs/utils/schema.d.ts.map +1 -1
- package/dist/cjs/utils/schema.js +2 -43
- package/dist/cjs/utils/schema.js.map +1 -1
- package/dist/core/Agent.js +10 -1
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/CompactionEngine.d.ts.map +1 -1
- package/dist/core/CompactionEngine.js +8 -3
- package/dist/core/CompactionEngine.js.map +1 -1
- package/dist/core/FlowSpec.d.ts +19 -2
- package/dist/core/FlowSpec.d.ts.map +1 -1
- package/dist/core/FlowSpec.js +254 -57
- package/dist/core/FlowSpec.js.map +1 -1
- package/dist/core/Prompt.d.ts +16 -0
- package/dist/core/Prompt.d.ts.map +1 -1
- package/dist/core/Prompt.js +37 -0
- package/dist/core/Prompt.js.map +1 -1
- package/dist/core/Runner.d.ts +2 -0
- package/dist/core/Runner.d.ts.map +1 -1
- package/dist/core/Runner.js +27 -16
- package/dist/core/Runner.js.map +1 -1
- package/dist/core/Speak.d.ts.map +1 -1
- package/dist/core/Speak.js +23 -17
- package/dist/core/Speak.js.map +1 -1
- package/dist/core/Understand.d.ts +4 -3
- package/dist/core/Understand.d.ts.map +1 -1
- package/dist/core/Understand.js +5 -38
- package/dist/core/Understand.js.map +1 -1
- package/dist/core/contracts.d.ts +5 -5
- package/dist/core/contracts.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/persistence/OpenSearchStore.d.ts +2 -1
- package/dist/persistence/OpenSearchStore.d.ts.map +1 -1
- package/dist/persistence/OpenSearchStore.js +2 -2
- package/dist/persistence/OpenSearchStore.js.map +1 -1
- package/dist/persistence/RedisStore.d.ts +1 -1
- package/dist/persistence/RedisStore.d.ts.map +1 -1
- package/dist/providers/AnthropicProvider.d.ts +2 -5
- package/dist/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/providers/AnthropicProvider.js +3 -4
- package/dist/providers/AnthropicProvider.js.map +1 -1
- package/dist/providers/DeepSeekProvider.d.ts.map +1 -1
- package/dist/providers/DeepSeekProvider.js +3 -4
- package/dist/providers/DeepSeekProvider.js.map +1 -1
- package/dist/providers/FallbackAiProvider.js +1 -1
- package/dist/providers/FallbackAiProvider.js.map +1 -1
- package/dist/providers/GeminiProvider.d.ts +1 -2
- package/dist/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/GeminiProvider.js +3 -4
- package/dist/providers/GeminiProvider.js.map +1 -1
- package/dist/providers/GenericOpenAICompatibleProvider.js +4 -4
- package/dist/providers/GenericOpenAICompatibleProvider.js.map +1 -1
- package/dist/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/providers/OpenAIProvider.js +3 -4
- package/dist/providers/OpenAIProvider.js.map +1 -1
- package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/providers/OpenRouterProvider.js +4 -3
- package/dist/providers/OpenRouterProvider.js.map +1 -1
- package/dist/providers/ProviderAdapter.d.ts +12 -9
- package/dist/providers/ProviderAdapter.d.ts.map +1 -1
- package/dist/providers/ProviderAdapter.js +8 -3
- package/dist/providers/ProviderAdapter.js.map +1 -1
- package/dist/providers/ZaiProvider.js +1 -1
- package/dist/providers/ZaiProvider.js.map +1 -1
- package/dist/types/agent.d.ts +1 -1
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/ai.d.ts +4 -6
- package/dist/types/ai.d.ts.map +1 -1
- package/dist/types/compaction.d.ts +1 -0
- package/dist/types/compaction.d.ts.map +1 -1
- package/dist/types/errors.d.ts +4 -9
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/errors.js +12 -12
- package/dist/types/errors.js.map +1 -1
- package/dist/types/history.d.ts +0 -7
- package/dist/types/history.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/session.d.ts +1 -1
- package/dist/types/session.d.ts.map +1 -1
- package/dist/utils/clock.js +1 -1
- package/dist/utils/clock.js.map +1 -1
- package/dist/utils/schema.d.ts +2 -11
- package/dist/utils/schema.d.ts.map +1 -1
- package/dist/utils/schema.js +2 -42
- package/dist/utils/schema.js.map +1 -1
- package/docs/concepts/pipeline.md +2 -2
- package/docs/concepts/runs-and-waits.md +1 -1
- package/docs/guides/actions-and-events.md +1 -1
- package/docs/guides/branching.md +1 -1
- package/docs/guides/compaction.md +2 -2
- package/docs/guides/error-handling.md +1 -1
- package/docs/guides/flow-control.md +1 -1
- package/docs/guides/persistence.md +1 -1
- package/docs/migration/v3-to-v4.md +2 -2
- package/docs/reference/agent.md +1 -1
- package/docs/reference/errors.md +10 -7
- package/docs/reference/flow-spec.md +29 -5
- package/docs/reference/outcomes.md +1 -1
- package/docs/reference/providers.md +2 -0
- package/docs/reference/step.md +1 -1
- package/docs/reference/stores.md +5 -3
- package/docs/start/01-install.md +5 -3
- package/package.json +2 -2
- package/src/core/Agent.ts +12 -1
- package/src/core/CompactionEngine.ts +10 -3
- package/src/core/FlowSpec.ts +263 -65
- package/src/core/Prompt.ts +40 -0
- package/src/core/Runner.ts +27 -14
- package/src/core/Speak.ts +23 -16
- package/src/core/Understand.ts +5 -39
- package/src/core/contracts.ts +5 -3
- package/src/index.ts +0 -1
- package/src/persistence/OpenSearchStore.ts +3 -2
- package/src/persistence/RedisStore.ts +1 -1
- package/src/providers/AnthropicProvider.ts +4 -9
- package/src/providers/DeepSeekProvider.ts +2 -4
- package/src/providers/FallbackAiProvider.ts +1 -1
- package/src/providers/GeminiProvider.ts +3 -6
- package/src/providers/GenericOpenAICompatibleProvider.ts +4 -4
- package/src/providers/OpenAIProvider.ts +3 -4
- package/src/providers/OpenRouterProvider.ts +4 -2
- package/src/providers/ProviderAdapter.ts +15 -12
- package/src/providers/ZaiProvider.ts +1 -1
- package/src/types/agent.ts +1 -1
- package/src/types/ai.ts +4 -6
- package/src/types/compaction.ts +1 -0
- package/src/types/errors.ts +11 -12
- package/src/types/history.ts +0 -10
- package/src/types/index.ts +0 -1
- package/src/types/session.ts +1 -1
- package/src/utils/clock.ts +1 -1
- package/src/utils/schema.ts +2 -48
- package/dist/cjs/providers/index.d.ts +0 -26
- package/dist/cjs/providers/index.d.ts.map +0 -1
- package/dist/cjs/providers/index.js +0 -30
- package/dist/cjs/providers/index.js.map +0 -1
- package/dist/cjs/utils/clone.d.ts +0 -8
- package/dist/cjs/utils/clone.d.ts.map +0 -1
- package/dist/cjs/utils/clone.js +0 -32
- package/dist/cjs/utils/clone.js.map +0 -1
- package/dist/cjs/utils/index.d.ts +0 -9
- package/dist/cjs/utils/index.d.ts.map +0 -1
- package/dist/cjs/utils/index.js +0 -29
- package/dist/cjs/utils/index.js.map +0 -1
- package/dist/providers/index.d.ts +0 -26
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/index.js +0 -17
- package/dist/providers/index.js.map +0 -1
- package/dist/utils/clone.d.ts +0 -8
- package/dist/utils/clone.d.ts.map +0 -1
- package/dist/utils/clone.js +0 -29
- package/dist/utils/clone.js.map +0 -1
- package/dist/utils/index.d.ts +0 -9
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -9
- package/dist/utils/index.js.map +0 -1
- package/src/providers/index.ts +0 -38
- package/src/utils/clone.ts +0 -34
- package/src/utils/index.ts +0 -18
|
@@ -150,7 +150,7 @@ Most things that go wrong inside a turn become outcome lines, not exceptions:
|
|
|
150
150
|
|
|
151
151
|
The full detail vocabulary is in [outcomes](../reference/outcomes.md).
|
|
152
152
|
|
|
153
|
-
Two errors outside the four classes: a `compaction` option out of range throws a plain `Error` at construction (`compactionThreshold must be between 0.5 and 0.95
|
|
153
|
+
Two errors outside the four classes: a `compaction` option out of range throws a plain `Error` at construction (`[CompactionEngine] compactionThreshold is 2: it must be between 0.5 and 0.95. Use 0.8 unless you measured otherwise.`), and `PrismaStore` throws a `TypeError` when the client has no model by the given name. [Errors](../reference/errors.md) lists the other wiring errors thrown at construction.
|
|
154
154
|
|
|
155
155
|
## Catching by class
|
|
156
156
|
|
|
@@ -144,7 +144,7 @@ Ends this run with `reason: 'flow'` and starts the other flow in the same turn.
|
|
|
144
144
|
- has `hop` one higher than the parent. A chain deeper than 5 stops: the child is skipped with `code: 'hop-limit'`.
|
|
145
145
|
- repeats by default (`'always'`), so a flow may be chained into many times; its claim carries the parent's step key.
|
|
146
146
|
|
|
147
|
-
`flow` is a template: `{{input.flowId}}` resolves against the run's input and context. A flow id
|
|
147
|
+
`flow` is a template: `{{input.flowId}}` resolves against the run's input and context. A literal flow id the agent does not have logs a warning when the agent is built and is skipped at run time with `code: 'flow-gone'`; a template that resolves to no flow lands in `skipped[]` with `code: 'flow-gone'`; a child flow with a live run for the same anchor is skipped with `code: 'already-running'`. This is how one flow hands the conversation to another: the last step of a qualifying flow can `then: { flow: 'agendamento' }`.
|
|
148
148
|
|
|
149
149
|
## `onEnd`: after the last step
|
|
150
150
|
|
|
@@ -156,7 +156,7 @@ const viaRedis = new RedisStore({ redis, keyPrefix: "agent:", sessionTTL: 7 * 24
|
|
|
156
156
|
const viaMongo = new MongoStore({ client: mongo, databaseName: "app" });
|
|
157
157
|
const sqlite = new SQLiteStore({ db });
|
|
158
158
|
await sqlite.initialize();
|
|
159
|
-
const search = new OpenSearchStore(opensearch,
|
|
159
|
+
const search = new OpenSearchStore({ client: opensearch, refresh: "wait_for" });
|
|
160
160
|
await search.initialize();
|
|
161
161
|
|
|
162
162
|
console.log([postgres, viaPrisma, viaRedis, viaMongo, sqlite, search].length); // 6
|
|
@@ -206,7 +206,7 @@ Per-field wording lives on the field (`ask`); a step may override it (`ask: { no
|
|
|
206
206
|
type Next = string /* step id or 'end' */ | { step: string; clear?: string[] } | { flow: string; input?: unknown };
|
|
207
207
|
```
|
|
208
208
|
|
|
209
|
-
Branches stay on talk steps, judged while the step is asking: `{ when: '...', then }` for the model, `{ if: pred, then }` for code. A `wait` step takes `if` branches only, judged when the customer replies, and only when the step also has an `else`; a `when` branch on a wait
|
|
209
|
+
Branches stay on talk steps, judged while the step is asking: `{ when: '...', then }` for the model, `{ if: pred, then }` for code. A `wait` step takes `if` branches only, judged when the customer replies, and only when the step also has an `else`; `validateFlow` rejects a `when` branch on a wait, since nothing would ever judge it. There is no standalone AI-judged step: the model forks only where fresh customer text exists.
|
|
210
210
|
|
|
211
211
|
```ts fragment
|
|
212
212
|
// ─── v3 ───
|
|
@@ -349,7 +349,7 @@ interface Store<D> {
|
|
|
349
349
|
}
|
|
350
350
|
```
|
|
351
351
|
|
|
352
|
-
The seven adapters survive as `Store` implementations and take the same client you passed before: `MemoryStore`, `PostgresStore`, `PrismaStore`, `RedisStore`, `MongoStore`, `SQLiteStore`, `OpenSearchStore`. They persist the v4 blob and a version, nothing else; message repositories, `SessionRepository`, `status`, `currentFlow` / `currentStep` columns, `PersistenceManager`, `autoSave`, `schemaVersion` and `restoreSession` are gone. The framework never calls a store: you `load`, `turn`, `save`.
|
|
352
|
+
The seven adapters survive as `Store` implementations and take the same client you passed before: `MemoryStore`, `PostgresStore`, `PrismaStore`, `RedisStore`, `MongoStore`, `SQLiteStore`, `OpenSearchStore`. Each takes one options object with the client in it, so OpenSearch's becomes `new OpenSearchStore({ client, ...options })`. They persist the v4 blob and a version, nothing else; message repositories, `SessionRepository`, `status`, `currentFlow` / `currentStep` columns, `PersistenceManager`, `autoSave`, `schemaVersion` and `restoreSession` are gone. The framework never calls a store: you `load`, `turn`, `save`.
|
|
353
353
|
|
|
354
354
|
**Use a fresh table.** The default names are the 3.x ones (`agent_sessions`, `agent:` prefix), so pass a new one (`tables.sessions` on Postgres, SQLite and Prisma, `collections.sessions` on Mongo, `indices.sessions` on OpenSearch, `keyPrefix` on Redis) or drop the old table first; `initialize()` (Postgres, SQLite, OpenSearch) only creates the table or index when it is missing, and does nothing while one of that name exists. A v4 store read against a live 3.x row fails loudly: Redis, Mongo, Prisma and OpenSearch throw `InvalidSessionError` (no `blob`), Postgres and SQLite fail on the missing `blob` column. Create the new table, then migrate rows on first load as §10 shows.
|
|
355
355
|
|
package/docs/reference/agent.md
CHANGED
|
@@ -86,7 +86,7 @@ class Agent<C, D> {
|
|
|
86
86
|
|
|
87
87
|
`Idle` is `{ prompt: Template; tools?: string[]; instructions?: Instruction[] } | 'silent'`. Its `tools` list must name tools registered on the agent.
|
|
88
88
|
|
|
89
|
-
`AgentCompactionConfig` is `{ maxTokens: number; compactionThreshold?: number; preserveRecentCount?: number; maxToolResultChars?: number; enabled?: boolean }`. Defaults: `compactionThreshold` `0.8` — compaction runs when the history passes 80% of `maxTokens` (allowed 0.5 to 0.95); keep the 4 most recent messages (at least 2); cut each tool result at 5000 characters (more than 0); `enabled: true`. Values outside those ranges throw at construction.
|
|
89
|
+
`AgentCompactionConfig` is `{ maxTokens: number; compactionThreshold?: number; preserveRecentCount?: number; maxToolResultChars?: number; enabled?: boolean }`. `maxTokens` must be more than 0. Defaults: `compactionThreshold` `0.8` — compaction runs when the history passes 80% of `maxTokens` (allowed 0.5 to 0.95); keep the 4 most recent messages (at least 2); cut each tool result at 5000 characters (more than 0); `enabled: true`. Values outside those ranges throw at construction.
|
|
90
90
|
|
|
91
91
|
## turn()
|
|
92
92
|
|
package/docs/reference/errors.md
CHANGED
|
@@ -59,7 +59,7 @@ From `src/types/errors.ts`, `src/core/Migrate.ts` and `@providerkit/core`.
|
|
|
59
59
|
| Class | Thrown by | When | What to do |
|
|
60
60
|
|-------|-----------|------|------------|
|
|
61
61
|
| `FlowConfigurationError` | `f.agent()` / `new Agent()`, `validateFlow`, `fromSpec` | A flow cannot run as written: a flow id or step id declared twice; a step with no id, or with the reserved id `"end"`; an unknown field in `collect` / `ask` / `clearOnStart`; an unknown action, event, condition or tool; a `then` pointing at a step that does not exist; an action `with` missing a required parameter; a duration that does not parse; a talk step with neither `prompt` nor `collect`. Also thrown at run time when a JSON predicate names a condition the agent does not have. | Fix the flow. It is a bug in the flow or the registries, never something to retry. |
|
|
62
|
-
| `SessionConflictError` | every `Store.save` | The stored version is not `expectedVersion`: another turn saved first,
|
|
62
|
+
| `SessionConflictError` | every `Store.save` | The stored version is not `expectedVersion`: another turn saved first, a `save(…, 0)` found a row, or the row was deleted or expired after it was loaded (`actualVersion` is `undefined`). | Load the session again and replay the same input. Nothing was sent, so nothing is duplicated. |
|
|
63
63
|
| `InvalidSessionError` | every `Store.load`, `assertSession`, `migrateSession` | A stored row is not a v4 session and not a recognisable 3.x one: wrong `v`, an `id` that does not match the row, a missing `data`, a run with a bad `status`, text that is not JSON. | Repair or delete the row. The framework never replaces a bad row with a fresh conversation, because that would re-ask every field and re-fire every once-flow. |
|
|
64
64
|
| `ProviderError` | the built-in providers, `FallbackAiProvider` | A model call failed after the provider's own retries, backup models and fallbacks. `kind` says what would fix it. | Match on `kind` (table below). |
|
|
65
65
|
|
|
@@ -80,8 +80,8 @@ The bracket names the class, the text before the colon says what is wrong and wh
|
|
|
80
80
|
[FlowConfigurationError] flow "triagem": has no steps list. Write steps as a list, even an empty one.
|
|
81
81
|
[FlowConfigurationError] flow "a" is declared twice: flow ids must be unique. Rename one of them.
|
|
82
82
|
[FlowConfigurationError] idle: unknown tool "buscar_preco". Register it in the agent's tools or fix the name.
|
|
83
|
-
[SessionConflictError] Session "s1" was modified concurrently: expected version
|
|
84
|
-
[SessionConflictError] Session "s1"
|
|
83
|
+
[SessionConflictError] Session "s1" was modified concurrently: expected version 3, found 4. Reload the session and retry the operation.
|
|
84
|
+
[SessionConflictError] Session "s1" is gone from the store: it was at version 3 and has since been deleted or expired. Load it again; a load that finds nothing starts a new conversation.
|
|
85
85
|
[InvalidSessionError] stored session "s1" is unreadable: v is 3, expected 4. Repair or delete the row; it is never replaced by a fresh conversation.
|
|
86
86
|
[InvalidSessionError] stored session "s1" is unreadable: expected an object, got "garbage". Repair or delete the row; it is never replaced by a fresh conversation.
|
|
87
87
|
[InvalidSessionError] stored session "s1" is unreadable: data is missing, expected an object; not a 3.x session either. Repair or delete the row; it is never replaced by a fresh conversation.
|
|
@@ -124,11 +124,13 @@ The same `ProviderError` means two different things depending on which of the tw
|
|
|
124
124
|
|
|
125
125
|
A few throws are plain `Error` or `TypeError`. One happens at run time: a reply that parses to a blank message with no tool calls throws `Error: No response from <provider>` out of `generateMessage`, after the provider's retries and backup models have run. A speak call swallows it and defers the step; an understand call hands it to you, so catch `Error`, not only `ProviderError`. The rest are wiring bugs at construction:
|
|
126
126
|
|
|
127
|
-
- A provider built without a key or model: `
|
|
127
|
+
- A provider built without a key or model: `[GeminiProvider] apiKey is empty: the provider cannot authenticate. Pass { apiKey: process.env.GEMINI_API_KEY } and check the variable is set.`, `[OpenAIProvider] model is empty: there is no default. Pass one, e.g. { model: "gpt-5.6" }.`
|
|
128
128
|
- `createOpenAICompatibleProvider` without `name`, `baseURL`, `apiKey` or `model`.
|
|
129
129
|
- `FallbackAiProvider` with an empty `providers` list.
|
|
130
130
|
- `PrismaStore` whose client has no delegate for the model: `[TypeError] PrismaStore cannot use model "agentSession": …`.
|
|
131
|
-
- `compaction` options out of range (`compactionThreshold` outside 0.5–0.95, `preserveRecentCount` below 2, `maxToolResultChars` at or below 0).
|
|
131
|
+
- `compaction` options out of range (`maxTokens` at or below 0, `compactionThreshold` outside 0.5–0.95, `preserveRecentCount` below 2, `maxToolResultChars` at or below 0): `[CompactionEngine] compactionThreshold is 1.2: it must be between 0.5 and 0.95. Use 0.8 unless you measured otherwise.`
|
|
132
|
+
|
|
133
|
+
Each follows the same `[Class] what: why. how to fix.` shape as the package classes; only the class is plain `Error`.
|
|
132
134
|
|
|
133
135
|
## Example
|
|
134
136
|
|
|
@@ -147,7 +149,7 @@ import type { AiProvider, DataOf, Session, TurnResult } from "@falai/agent";
|
|
|
147
149
|
|
|
148
150
|
declare const provider: AiProvider;
|
|
149
151
|
declare function send(text: string, key: string, afterMs: number): Promise<void>;
|
|
150
|
-
declare function enqueue(jobId: string, at: Date): Promise<void>;
|
|
152
|
+
declare function enqueue(jobId: string, at: Date, payload: { sessionId: string; key: string }): Promise<void>;
|
|
151
153
|
declare function sleep(ms: number): Promise<void>;
|
|
152
154
|
|
|
153
155
|
const f = falai().fields({
|
|
@@ -213,7 +215,8 @@ async function handle(sessionId: string, text: string, id: string): Promise<Turn
|
|
|
213
215
|
}
|
|
214
216
|
|
|
215
217
|
for (const m of result.messages) await send(m.text, m.key, m.afterMs);
|
|
216
|
-
for (
|
|
218
|
+
// The key rides in the payload for turn({ wake: key }); BullMQ refuses a ":" in a custom id, so the job id is encoded.
|
|
219
|
+
for (const s of result.schedule) await enqueue(encodeURIComponent(s.key), s.at, { sessionId, key: s.key });
|
|
217
220
|
return result;
|
|
218
221
|
}
|
|
219
222
|
|
|
@@ -53,8 +53,10 @@ type BranchSpec = { then: Next } & ({ when: string } | { if: ConditionSpec });
|
|
|
53
53
|
|
|
54
54
|
type InstructionSpec = Omit<Instruction, "if"> & { if?: ConditionSpec };
|
|
55
55
|
|
|
56
|
-
/** What a flow's names resolve against. */
|
|
57
|
-
type Registries = Pick<AgentOptions, "fields" | "actions" | "events" | "conditions" | "tools"
|
|
56
|
+
/** What a flow's names resolve against. With `flows`, a literal `{ flow }` target must name one of them. */
|
|
57
|
+
type Registries = Pick<AgentOptions, "fields" | "actions" | "events" | "conditions" | "tools"> & {
|
|
58
|
+
flows?: ReadonlyArray<{ id: string }>;
|
|
59
|
+
};
|
|
58
60
|
|
|
59
61
|
function fromSpec<C = unknown, D = InferData<FieldDefs>>(spec: FlowSpec): Flow<C, D>;
|
|
60
62
|
function toSpec<C, D>(flow: Flow<C, D>): FlowSpec;
|
|
@@ -90,13 +92,13 @@ Every field means what it means on [Flow](./flow.md). The differences:
|
|
|
90
92
|
| `waitEvent` | `wait: { event }` | An event. |
|
|
91
93
|
| `if` | `if` step | A code fork. |
|
|
92
94
|
|
|
93
|
-
`fromSpec` drops `kind`; `toSpec` derives it from the step's shape by this table. A talk step with neither `prompt` nor `collect` makes `toSpec` throw.
|
|
95
|
+
`fromSpec` drops `kind`; `toSpec` derives it from the step's shape by this table. A talk step with neither `prompt` nor `collect` makes `toSpec` throw: `[FlowConfigurationError] flow "x", step "y": has neither prompt nor collect. A talk step needs a guideline, fields to collect, or both.`
|
|
94
96
|
|
|
95
97
|
## fromSpec
|
|
96
98
|
|
|
97
99
|
- Strips `null` from every optional value, at any depth.
|
|
98
100
|
- Removes `kind` from each step. Nothing else changes.
|
|
99
|
-
- Throws `FlowConfigurationError` when `
|
|
101
|
+
- Throws `FlowConfigurationError` when the JSON has the wrong shape, with the same shape checks `validateFlow` runs first: the flow or a step is not an object, a list is not a list (`steps`, `on`, `collect`, …), a step does two things or none, or its `kind` disagrees with its body. For example `[FlowConfigurationError] flow "x": has no steps list. Write steps as a list, even an empty one.`
|
|
100
102
|
- Does **not** check names. The result is typed as a `Flow` but nothing is verified yet; `validateFlow` does that, and the agent runs it on every flow it is built with.
|
|
101
103
|
|
|
102
104
|
## toSpec
|
|
@@ -121,6 +123,9 @@ Every message has the form `[FlowConfigurationError] <where>: <what>. <fix>`, wh
|
|
|
121
123
|
| Reserved step id | `uses the reserved id "end"` | "end" ends the run; pick another id. |
|
|
122
124
|
| Duplicate step id | `duplicates an earlier step id` | Give each step its own id. |
|
|
123
125
|
| Triggers, no steps | `has triggers but no steps` | Add at least one step or remove `on`. |
|
|
126
|
+
| Trigger with no kind | `names no trigger kind` (`<where>` is `flow "id", trigger #n`; the v3 `{ kind: 'message', when }` shape lands here) | A trigger is one of `message`, `mention`, `silence`, `event`. A flow the host starts itself has no `on` at all. |
|
|
127
|
+
| Only exclusions | `every message phrase starts with "!", so nothing can ever match it` (also `mention`) | A "!" phrase rules the trigger out. Add at least one plain phrase saying when it should fire, or use an empty list for a catch-all. |
|
|
128
|
+
| Step does nothing | `does nothing` | A step talks (`prompt` / `collect`), says (`say`), acts (`do`), waits (`wait`) or forks (`if`). |
|
|
124
129
|
| Unknown field | `unknown field "x" in collect` (the flow's or a step's; also `ask`, `clearOnStart`, `then.clear`, `while.equals`, `if.known`, …) | Add it to the agent's fields or fix the slug. |
|
|
125
130
|
| Unknown tool | `unknown tool "x"` (flow or step `tools`) | Register it in the agent's tools or fix the name. |
|
|
126
131
|
| Unknown action | `unknown action "x"` | Register it in actions or fix the name. |
|
|
@@ -128,6 +133,7 @@ Every message has the form `[FlowConfigurationError] <where>: <what>. <fix>`, wh
|
|
|
128
133
|
| Unknown condition | `unknown condition "x" in if` | Register it in conditions or use equals, known, silenced. |
|
|
129
134
|
| `equals` shape | `if.equals is not an object` | Write equals as { field: value }. |
|
|
130
135
|
| `equals` type | `if.equals gives "orcamento" a string, but the field is a number` | Write a number; values are not coerced. |
|
|
136
|
+
| `equals` off the list | `if.equals gives "etapa" "frio", which is not one of "novo", "quente"` | Use one of the listed values. |
|
|
131
137
|
| `known` shape | `if.known is not a list` | Write known as [field, ...]. |
|
|
132
138
|
| `silenced` shape | `if.silenced is not a boolean` | Write true or false. |
|
|
133
139
|
| Bad duration | `wait has duration "5 min", which does not parse` (also `silence`, `after`, `repeat.cooldown`, `wait.upTo`) | Write a number and a unit: "30s", "5m", "24h" or "3d". |
|
|
@@ -138,10 +144,26 @@ Every message has the form `[FlowConfigurationError] <where>: <what>. <fix>`, wh
|
|
|
138
144
|
| Branch without a test | `branches[0] has neither when nor if` | Give the branch an AI condition (when) or a code one (if). |
|
|
139
145
|
| Backward `if` with no `else` | `"if" jumps back to "quem" with no else` | Add else so the false branch has somewhere to go. |
|
|
140
146
|
| Fixed question, nothing to ask | `has a question but collects nothing` | A fixed question asks for fields: add collect, or send the text with a say step. |
|
|
147
|
+
| Not an object | `is null, not an object` (the flow), `steps[0] is null, not an object`, `with is "x", not an object` | Pass the flow itself: { id, name, steps }. / Write each entry of steps as an object. |
|
|
148
|
+
| Not a list | `collect is "nome", not a list` (also `on`, `steps`, `clearOnStart`, `tools`, `instructions`, `branches`, `message`, `mention`, `then.clear`) | Write collect: ["nome"]. |
|
|
149
|
+
| Not text | `say is 42, not text` (also `prompt`, `question`, `description`, `anchor`) | Write say as a string. |
|
|
150
|
+
| Not one of the values | `onEnd is "restart", which is not one of "end", "stay", "reset"` (also `instructions[n].kind`) | Use one of them. |
|
|
151
|
+
| Bad `repeat` | `repeat is "never"` | Use "once", "always" or { cooldown: "24h" }. |
|
|
152
|
+
| Bad `maxAsks` | `maxAsks is "3", not a whole number of 1 or more` | Write a number like 3. |
|
|
153
|
+
| Event wait without an event | `wait has no event` | Write wait: { event: "name" } to wait for an event, or a duration like "1h". |
|
|
154
|
+
| Bad target | `then is 5, not a step id or a target` (also `else`, `onFail`, `branches[n].then`) | Write a step id, "end", { step: "id" } or { flow: "id" }. |
|
|
155
|
+
| Step does two things | `mixes "say" and "do"` | A step does one thing. Split it into one step per kind. |
|
|
156
|
+
| `kind` disagrees with the body | `has kind "do", but its body is a "say" step` | Set kind to "say", or change the body to match. |
|
|
141
157
|
|
|
142
158
|
Parameter values are checked strictly: `"3"` is not a number, `3.5` is not an integer, and an `enum` must contain the value unless the string holds `{{`, because a template's value is only known at run time.
|
|
143
159
|
|
|
144
|
-
|
|
160
|
+
The agent constructor passes its own flows as `registries.flows`, so a literal chain to a flow it does not have is a warning in the log (see below). Five more checks live in the constructor rather than in `validateFlow`:
|
|
161
|
+
|
|
162
|
+
- `flow "x" is declared twice`
|
|
163
|
+
- `idle: unknown tool "x"`
|
|
164
|
+
- `tool "x": parameters must be a JSON Schema object`
|
|
165
|
+
- `condition "known" shadows a built-in`: `equals`, `known` and `silenced` are reserved
|
|
166
|
+
- an unknown condition in an agent or idle instruction's `if`: `agent: unknown condition "vip" in instructions[0].if`
|
|
145
167
|
|
|
146
168
|
### Warnings
|
|
147
169
|
|
|
@@ -150,6 +172,8 @@ Two checks live in the agent constructor rather than in `validateFlow`: `flow "x
|
|
|
150
172
|
| `flow "f", step "s": then jumps back to "quem" without clear; the fields collected since stay known and those steps skip. Add clear: [...] to re-ask them.` | A `then`, `else`, `onFail` or branch target points at the same or an earlier step and clears nothing, so a collect step it lands on is skipped with `code: 'already-known'`. |
|
|
151
173
|
| `flow "f", step "s": collects "nome", "empresa" with no prompt and no ask; the model has nothing to go on. Add a prompt or an ask per field.` | A collect step with no `prompt` and no `question`, where no listed field has an `ask` on the step or on the agent. |
|
|
152
174
|
| `flow "f": collect lists "confirmado", which is only taken from the answer to a step that asks it, and no step does. Add it to a step's collect, or set extract: 'anywhere' on the field.` | A field in the flow's `collect` with `extract: 'asked'` (every boolean, by default) that no step's `collect` lists, so nothing can ever fill it. |
|
|
175
|
+
| `flow "f", step "s": then names flow "humnao", which this agent does not have; a run skips this move with flow-gone. Use one of "vendas", "suporte", or add the flow.` | A literal `{ flow }` target missing from `registries.flows`; only checked when that list is set, and never for an id with `{{`. A warning rather than an error, so a host that drops one bad row keeps the rest of its agent. |
|
|
176
|
+
| `flow "f", step "s": branches[0] is a "when" branch on a wait step, which no call judges, so a reply goes to else. Use "if", or move the branch to a talk step.` | No call judges a wait step, so an AI condition there never fires. `flowSpecSchema` no longer offers one. |
|
|
153
177
|
|
|
154
178
|
## flowSpecSchema
|
|
155
179
|
|
|
@@ -227,7 +227,7 @@ The run leaves `session.runs`, appears in `TurnResult.ended`, and writes one lin
|
|
|
227
227
|
| `cooldown` | `repeat: { cooldown }` and the last run is younger than the cooldown. |
|
|
228
228
|
| `already-running` | A live run of this flow exists for this anchor, in this session or (through `turn({ claims })`) in another of the customer's sessions. |
|
|
229
229
|
| `hop-limit` | The start would be at hop 5. `{ flow }` jumps and `onEnd: 'reset'` each add a hop. |
|
|
230
|
-
| `flow-gone` | `turn({ start })`, a silence wake, or a `{ flow }` jump named a flow the agent does not have. |
|
|
230
|
+
| `flow-gone` | `turn({ start })`, a silence wake, or a `{ flow }` jump named a flow the agent does not have. For a literal `{ flow }` id, the agent build also logs a warning. |
|
|
231
231
|
|
|
232
232
|
A trigger whose `if` is false starts nothing and writes nothing.
|
|
233
233
|
|
|
@@ -618,6 +618,8 @@ Do not pick from that list. Ask the model you ship.
|
|
|
618
618
|
|
|
619
619
|
Every `ProviderAdapter` subclass has `probeJsonWithTools(opts?)`. It asks the bound model, on the wire, both ways, and reports which shape called the tool on every sample.
|
|
620
620
|
|
|
621
|
+
The probe asks the adapter's own model, never its fallbacks, one call at a time. A rate-limited fallback cannot make a working primary look broken, and a primary that fails the probe throws. A `FallbackAiProvider` has no probe of its own: probe each adapter you put in it.
|
|
622
|
+
|
|
621
623
|
```ts fragment
|
|
622
624
|
interface JsonWithToolsProbe {
|
|
623
625
|
/** The shape to configure, or null when neither called the tool on every sample. */
|
package/docs/reference/step.md
CHANGED
|
@@ -160,7 +160,7 @@ The code forks. No model call.
|
|
|
160
160
|
| `{ step: 'passo', clear: ['campo'] }` | Delete the listed fields from `session.data` and forget how many times the run asked them, then jump. The way to ask something again. |
|
|
161
161
|
| `{ flow: 'outro', input? }` | End this run (reason `'flow'`) and start `outro` in the same turn, one hop deeper. The child gets `input`, or this run's `input` when absent. It holds the floor when this run did, or when no run did: a `mention` flow that chains does not take the message from the run it was routed to. `flow` is a template. |
|
|
162
162
|
|
|
163
|
-
Entering a step counts a visit; the visit is part of every key minted there, so a step visited twice sends twice. A `{ step }` jump to an id that no longer exists ends the run with `code: 'step-gone'`; a `{ flow }` to an unknown flow is skipped with `code: 'flow-gone'`.
|
|
163
|
+
Entering a step counts a visit; the visit is part of every key minted there, so a step visited twice sends twice. A `{ step }` jump to an id that no longer exists ends the run with `code: 'step-gone'`; a `{ flow }` whose template resolves to an unknown flow is skipped with `code: 'flow-gone'`. A literal flow id the agent does not have is skipped the same way, and the agent build logs a warning for it.
|
|
164
164
|
|
|
165
165
|
## Caps
|
|
166
166
|
|
package/docs/reference/stores.md
CHANGED
|
@@ -261,6 +261,7 @@ return nil
|
|
|
261
261
|
`ARGV` is the expected version, the next version, the blob, now as ISO text and the TTL in seconds. `nil` back is success; a version back is the conflict (`actualVersion` is that number); `'missing'` means the hash is gone (`actualVersion` is `undefined`).
|
|
262
262
|
|
|
263
263
|
- `sessionTTL` defaults to `7 * 24 * 60 * 60` = 604800 seconds and is reset on every save. `0` never expires.
|
|
264
|
+
- A session that expires takes its parked runs and claims with it. A wait longer than the TTL (an event wait defaults to 30 days) wakes to no session, and a `once` flow can fire again. Set `sessionTTL` above your longest wait, or to `0`.
|
|
264
265
|
- `load` returns `null` when the hash has no fields.
|
|
265
266
|
|
|
266
267
|
### Example
|
|
@@ -409,12 +410,13 @@ console.log(await store.load("s1")); // null on a first turn
|
|
|
409
410
|
|
|
410
411
|
## OpenSearchStore
|
|
411
412
|
|
|
412
|
-
Over `@opensearch-project/opensearch`'s client; Elasticsearch 7.x fits the same calls. One document per session, with `blob` stored but not indexed.
|
|
413
|
+
Over `@opensearch-project/opensearch`'s client; Elasticsearch 7.x fits the same calls. One document per session, with `blob` stored but not indexed. Like the other stores, the constructor takes one object with the client in it.
|
|
413
414
|
|
|
414
415
|
### Signature
|
|
415
416
|
|
|
416
417
|
```ts fragment
|
|
417
418
|
interface OpenSearchStoreOptions {
|
|
419
|
+
client: OpenSearchClient;
|
|
418
420
|
/** Index name. Default `agent_sessions`. */
|
|
419
421
|
indices?: { sessions?: string };
|
|
420
422
|
/** Create the index with its mappings on `initialize()`. Default true. */
|
|
@@ -435,7 +437,7 @@ interface OpenSearchClient {
|
|
|
435
437
|
}
|
|
436
438
|
|
|
437
439
|
class OpenSearchStore<D = unknown> implements Store<D> {
|
|
438
|
-
constructor(
|
|
440
|
+
constructor(options: OpenSearchStoreOptions);
|
|
439
441
|
/** Create the index with its mappings when it is missing and `autoCreateIndices` is on. */
|
|
440
442
|
initialize(): Promise<void>;
|
|
441
443
|
}
|
|
@@ -479,7 +481,7 @@ import type { OpenSearchClient } from "@falai/agent";
|
|
|
479
481
|
// const client = new Client({ node: process.env.OPENSEARCH_URL }); // from @opensearch-project/opensearch
|
|
480
482
|
declare const client: OpenSearchClient;
|
|
481
483
|
|
|
482
|
-
const store = new OpenSearchStore<{ nome: string }>(client,
|
|
484
|
+
const store = new OpenSearchStore<{ nome: string }>({ client, indices: { sessions: "conversas" }, refresh: "wait_for" });
|
|
483
485
|
await store.initialize();
|
|
484
486
|
console.log(await store.load("s1")); // null on a first turn
|
|
485
487
|
```
|
package/docs/start/01-install.md
CHANGED
|
@@ -19,17 +19,19 @@ This page puts the package in a project and runs the first example.
|
|
|
19
19
|
## Add it to your project
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
bun add @falai/agent
|
|
22
|
+
bun add @falai/agent@alpha
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
With npm or pnpm:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
npm install @falai/agent
|
|
28
|
+
npm install @falai/agent@alpha
|
|
29
29
|
# or
|
|
30
|
-
pnpm add @falai/agent
|
|
30
|
+
pnpm add @falai/agent@alpha
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
4.0 is in alpha. Plain `@falai/agent`, with no tag, still installs 3.x, and this tutorial will not compile against it.
|
|
34
|
+
|
|
33
35
|
The package ships an ESM build, a CommonJS build and its own TypeScript types. There is nothing else to install.
|
|
34
36
|
|
|
35
37
|
## Set a provider key
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@falai/agent",
|
|
3
3
|
"packageManager": "bun@1.4.2",
|
|
4
|
-
"version": "4.0.0-alpha.
|
|
4
|
+
"version": "4.0.0-alpha.15",
|
|
5
5
|
"description": "Conversational state engine for TypeScript where the AI understands, but the code is in control",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/cjs/index.js",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"typescript-eslint": "^8.18.2"
|
|
98
98
|
},
|
|
99
99
|
"dependencies": {
|
|
100
|
-
"@providerkit/core": "^0.11.
|
|
100
|
+
"@providerkit/core": "^0.11.2",
|
|
101
101
|
"loglevel": "^1.9.2"
|
|
102
102
|
},
|
|
103
103
|
"peerDependencies": {
|
package/src/core/Agent.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { logger, LoggerLevel } from "../utils/logger.js";
|
|
|
15
15
|
import { addUsage } from "../utils/usage.js";
|
|
16
16
|
import { CompactionEngine } from "./CompactionEngine.js";
|
|
17
17
|
import type { IdleRequest, SpeakOutcome, TalkRequest } from "./contracts.js";
|
|
18
|
-
import { validateFlow } from "./FlowSpec.js";
|
|
18
|
+
import { BUILT_IN_CONDITIONS, checkPred, validateFlow } from "./FlowSpec.js";
|
|
19
19
|
import { Runner, type Turn } from "./Runner.js";
|
|
20
20
|
import { Speak } from "./Speak.js";
|
|
21
21
|
import { Understand } from "./Understand.js";
|
|
@@ -110,6 +110,16 @@ function compactionOptions<C, D>(options: AgentOptions<C, D>): CompactionOptions
|
|
|
110
110
|
|
|
111
111
|
/** Every name a flow uses must resolve now, not on the turn that first reaches it. */
|
|
112
112
|
function validate<C, D>(options: AgentOptions<C, D>): void {
|
|
113
|
+
// A host condition under a built-in's name is never called: the built-in answers first.
|
|
114
|
+
for (const name of Object.keys(options.conditions ?? {})) {
|
|
115
|
+
if (BUILT_IN_CONDITIONS.includes(name)) {
|
|
116
|
+
throw new FlowConfigurationError(
|
|
117
|
+
`[FlowConfigurationError] condition "${name}" shadows a built-in: ${BUILT_IN_CONDITIONS.join(", ")} are reserved. Rename it.`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// An agent-level `if` is judged on every turn, so an unknown name here would throw on every turn.
|
|
122
|
+
options.instructions?.forEach((ins, i) => checkPred(ins.if, "agent", `instructions[${i}].if`, options));
|
|
113
123
|
const ids = new Set<string>();
|
|
114
124
|
for (const flow of options.flows ?? []) {
|
|
115
125
|
if (ids.has(flow.id)) {
|
|
@@ -134,6 +144,7 @@ function validate<C, D>(options: AgentOptions<C, D>): void {
|
|
|
134
144
|
}
|
|
135
145
|
const { idle } = options;
|
|
136
146
|
if (idle && idle !== "silent") {
|
|
147
|
+
idle.instructions?.forEach((ins, i) => checkPred(ins.if, "idle", `instructions[${i}].if`, options));
|
|
137
148
|
const known = new Set((options.tools ?? []).map((tool) => tool.id));
|
|
138
149
|
for (const name of idle.tools ?? []) {
|
|
139
150
|
if (!known.has(name)) {
|
|
@@ -19,13 +19,20 @@ export class CompactionEngine {
|
|
|
19
19
|
* Validate CompactionOptions. Throws on invalid values.
|
|
20
20
|
*/
|
|
21
21
|
static validateOptions(options: CompactionOptions): void {
|
|
22
|
+
if (typeof options.maxTokens !== "number" || !(options.maxTokens > 0)) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`[CompactionEngine] maxTokens is ${String(options.maxTokens)}: it must be above 0. ` +
|
|
25
|
+
`Set it to the most history, in tokens, each call should carry, e.g. 100000.`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
22
28
|
if (
|
|
23
29
|
typeof options.compactionThreshold !== "number" ||
|
|
24
30
|
options.compactionThreshold < 0.5 ||
|
|
25
31
|
options.compactionThreshold > 0.95
|
|
26
32
|
) {
|
|
27
33
|
throw new Error(
|
|
28
|
-
`compactionThreshold must be between 0.5 and 0.95
|
|
34
|
+
`[CompactionEngine] compactionThreshold is ${String(options.compactionThreshold)}: it must be between 0.5 and 0.95. ` +
|
|
35
|
+
`Use 0.8 unless you measured otherwise.`
|
|
29
36
|
);
|
|
30
37
|
}
|
|
31
38
|
if (
|
|
@@ -33,7 +40,7 @@ export class CompactionEngine {
|
|
|
33
40
|
options.preserveRecentCount < 2
|
|
34
41
|
) {
|
|
35
42
|
throw new Error(
|
|
36
|
-
`preserveRecentCount must be
|
|
43
|
+
`[CompactionEngine] preserveRecentCount is ${String(options.preserveRecentCount)}: it must be 2 or more. Use 4, the default.`
|
|
37
44
|
);
|
|
38
45
|
}
|
|
39
46
|
if (
|
|
@@ -41,7 +48,7 @@ export class CompactionEngine {
|
|
|
41
48
|
options.maxToolResultChars <= 0
|
|
42
49
|
) {
|
|
43
50
|
throw new Error(
|
|
44
|
-
`maxToolResultChars must be
|
|
51
|
+
`[CompactionEngine] maxToolResultChars is ${String(options.maxToolResultChars)}: it must be above 0. Use 5000, the default.`
|
|
45
52
|
);
|
|
46
53
|
}
|
|
47
54
|
}
|