@botpress/adk-cli 1.19.0-beta.3 → 1.19.0-beta.5
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/assets/ui-dist/assets/{index-CSTz-oTK.js → index-B3b7PPgV.js} +4 -4
- package/{dist/index-DNqXHLNi-27nvpkee.js → assets/ui-dist/assets/index-BESZeTO5.js} +2 -2
- package/{dist/index-DAztRXeA-vr85xnxs.css → assets/ui-dist/assets/index-DIFGtVmI.css} +1 -1
- package/{dist/index-DfjXY3Oo-szsanhnt.js → assets/ui-dist/assets/index-a3ZL0_Jr.js} +2 -2
- package/assets/ui-dist/index.html +2 -2
- package/dist/{README-wjb7z7f0.md → README-824wa4jj.md} +1 -1
- package/dist/SKILL-75ym6tcj.md +257 -0
- package/dist/SKILL-chnwzmt1.md +548 -0
- package/dist/SKILL-d4bs53y3.md +244 -0
- package/dist/SKILL-h4b2f1n9.md +114 -0
- package/dist/SKILL-hbw5nt95.md +111 -0
- package/dist/actions-h7hxxvxr.md +823 -0
- package/dist/adk-build-emhwd0tg.md +39 -0
- package/dist/adk-debug-yrrdtpmb.md +23 -0
- package/dist/adk-dev-console-7z9rtjpy.md +17 -0
- package/dist/adk-document-ras6zzzh.md +68 -0
- package/dist/adk-eval-vx76dwq0.md +17 -0
- package/dist/adk-explain-fcsaw852.md +22 -0
- package/dist/adk-init-9kmxmbbe.md +30 -0
- package/dist/adk-ship-dybzn884.md +29 -0
- package/dist/adk-test-xq67savs.md +27 -0
- package/dist/adk-validate-1p4skv4m.md +31 -0
- package/dist/advanced-patterns-z86m9h1a.md +669 -0
- package/dist/agent-config-1aqvkrgf.md +450 -0
- package/dist/agent-steps-zj4c1n9t.md +115 -0
- package/dist/agent0-assets/skills/adk/SKILL.md +5 -5
- package/dist/agent0-assets/skills/adk/references/cli.md +265 -24
- package/dist/agent0-assets/skills/adk-debugger/SKILL.md +5 -5
- package/dist/agent0-assets/skills/adk-debugger/references/common-failures.md +1 -1
- package/dist/agent0-assets/skills/adk-debugger/references/debug-workflow.md +3 -3
- package/dist/agent0-assets/skills/adk-debugger/references/traces-and-logs.md +15 -7
- package/dist/assets-q7jt8r2q.md +237 -0
- package/dist/autonomous-execution-fymc12e5.md +747 -0
- package/dist/cli-c31e06yr.md +1334 -0
- package/dist/cli.js +963 -548
- package/dist/commands.json +24 -3
- package/dist/common-failures-ht29w2db.md +231 -0
- package/dist/component-registry-bbpw0see.md +43 -0
- package/dist/context-api-760cvh0h.md +461 -0
- package/dist/conversation-analysis-8ka4nrm4.md +420 -0
- package/dist/conversation-lifecycle-4seqqjc1.md +311 -0
- package/dist/conversations-gwfvxaee.md +1111 -0
- package/dist/custom-components-y5wdgpey.md +167 -0
- package/dist/debug-workflow-vz84q80v.md +239 -0
- package/dist/desk-k54ktvsd.md +852 -0
- package/dist/doc-standards-ck6ax2jd.md +124 -0
- package/dist/eval-format-7n9brja4.md +354 -0
- package/dist/evals-y1zcr1mc.md +11 -0
- package/dist/explain-config-1yaq3zzd.md +424 -0
- package/dist/files-dzt0wxn3.md +918 -0
- package/dist/{index-gqxgqnyz.html → index-3t354nbw.html} +2 -2
- package/dist/{index-CSTz-oTK-mdfh47r8.js → index-B3b7PPgV-zetkb01w.js} +4 -4
- package/{assets/ui-dist/assets/index-DNqXHLNi.js → dist/index-BESZeTO5-2rpmcefa.js} +2 -2
- package/{assets/ui-dist/assets/index-DAztRXeA.css → dist/index-DIFGtVmI-d4dhjg97.css} +1 -1
- package/{assets/ui-dist/assets/index-DfjXY3Oo.js → dist/index-a3ZL0_Jr-q145h35p.js} +2 -2
- package/dist/integration-actions-xang7tma.md +659 -0
- package/dist/integrations-k5ss07f4.md +266 -0
- package/dist/interfaces-6t0bpxs4.md +97 -0
- package/dist/knowledge-bases-20rg8p5c.md +456 -0
- package/dist/llm-debugging-76ng8rzm.md +209 -0
- package/dist/mcp-server-n1asyzhv.md +112 -0
- package/dist/messages-gkkg0b54.md +769 -0
- package/dist/model-configuration-f273kgd0.md +201 -0
- package/dist/multi-agent-dashboard-1mdfjdts.md +70 -0
- package/dist/pages-xn18xn73.md +273 -0
- package/dist/patterns-mistakes-5vb99dhf.md +469 -0
- package/dist/plugins-ttnx8f49.md +280 -0
- package/dist/tables-x45t36dd.md +1136 -0
- package/dist/tags-46td3qrg.md +516 -0
- package/dist/test-patterns-hj47w4nc.md +355 -0
- package/dist/testing-workflow-2vkp932f.md +225 -0
- package/dist/tools-wk7bq24z.md +581 -0
- package/dist/trace-summarization-hdyx1c2e.md +313 -0
- package/dist/traces-and-logs-f2kjfh19.md +203 -0
- package/dist/triggers-4j2cc86x.md +719 -0
- package/dist/ui-server-entry.js +977 -162
- package/dist/workflow-steps-mrdrf1c5.md +851 -0
- package/dist/workflows-gadx3s69.md +1408 -0
- package/dist/zai-agent-reference-e6644aad.md +454 -0
- package/dist/zai-complete-guide-mnna5d6f.md +1408 -0
- package/package.json +4 -4
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# Conversation Lifecycle (Nudge / Expiration)
|
|
2
|
+
|
|
3
|
+
The lifecycle system lets conversations re-engage idle users (nudge) and clean up stale sessions (expiration). It is an opt-in feature configured per conversation via the `lifecycle` property.
|
|
4
|
+
|
|
5
|
+
For basic conversation setup, channel routing, and handler parameters, see **[conversations.md](./conversations.md)**.
|
|
6
|
+
|
|
7
|
+
## Enabling Lifecycle
|
|
8
|
+
|
|
9
|
+
Add a `lifecycle` object to any Conversation definition. Durations use `ms`-compatible strings (`"30s"`, `"5m"`, `"1h"`, `"1d"`).
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { Conversation, z } from "@botpress/runtime";
|
|
13
|
+
|
|
14
|
+
export const Chat = new Conversation({
|
|
15
|
+
channel: "webchat.channel",
|
|
16
|
+
|
|
17
|
+
lifecycle: {
|
|
18
|
+
nudge: {
|
|
19
|
+
after: "5m", // First nudge fires 5 min after last user message
|
|
20
|
+
interval: "10m", // Subsequent nudges fire every 10 min
|
|
21
|
+
max: 3, // Stop after 3 nudges (omit for unlimited)
|
|
22
|
+
},
|
|
23
|
+
expire: {
|
|
24
|
+
after: "1h", // Session expires 1 hour after last user message
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
state: z.object({
|
|
29
|
+
topic: z.string().optional(),
|
|
30
|
+
}),
|
|
31
|
+
|
|
32
|
+
handler: async (props) => {
|
|
33
|
+
// handler receives nudge and expire events alongside regular messages
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Configuration Fields
|
|
39
|
+
|
|
40
|
+
| Field | Type | Required | Description |
|
|
41
|
+
|-------|------|----------|-------------|
|
|
42
|
+
| `nudge.after` | `string` | Yes (if nudge) | Duration of silence before the first nudge fires |
|
|
43
|
+
| `nudge.interval` | `string` | No | Duration between subsequent nudges. Defaults to `nudge.after` |
|
|
44
|
+
| `nudge.max` | `number` | No | Max nudges per session. Must be a positive integer. Omit for unlimited |
|
|
45
|
+
| `expire.after` | `string` | Yes (if expire) | Duration of silence before the session expires |
|
|
46
|
+
|
|
47
|
+
Nudge and expire are independent -- you can configure either or both.
|
|
48
|
+
|
|
49
|
+
## Handler Types
|
|
50
|
+
|
|
51
|
+
When lifecycle is enabled, the conversation handler receives two additional event types: `"nudge"` and `"expire"`. Discriminate using the `type` parameter.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
handler: async ({ type, conversation, state, execute }) => {
|
|
55
|
+
if (type === "nudge") {
|
|
56
|
+
// User has been silent -- send a re-engagement message
|
|
57
|
+
await conversation.send({
|
|
58
|
+
type: "text",
|
|
59
|
+
payload: { text: "Still there? Let me know if you need anything." },
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (type === "expire") {
|
|
65
|
+
// Session is about to be torn down -- send a farewell
|
|
66
|
+
await conversation.send({
|
|
67
|
+
type: "text",
|
|
68
|
+
payload: { text: "This session has expired. Send a new message to start fresh." },
|
|
69
|
+
});
|
|
70
|
+
// After the handler returns, the runtime automatically:
|
|
71
|
+
// 1. Cancels all active workflows on the conversation
|
|
72
|
+
// 2. Tags the conversation with sessionExpired = "true"
|
|
73
|
+
// 3. Resets conversation state to defaults
|
|
74
|
+
// 4. Clears the transcript
|
|
75
|
+
// 5. Sets lifecycle status to "expired"
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (type === "message") {
|
|
80
|
+
// Normal message handling
|
|
81
|
+
await execute({
|
|
82
|
+
instructions: "You are a helpful assistant",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Handler Props by Type
|
|
89
|
+
|
|
90
|
+
When `type === "nudge"`:
|
|
91
|
+
- `event` is typed as `LifecycleNudgeEventType` with payload `{ conversationId, sessionId, scheduledAt }`
|
|
92
|
+
- `message` is `undefined`
|
|
93
|
+
|
|
94
|
+
When `type === "expire"`:
|
|
95
|
+
- `event` is typed as `LifecycleExpireEventType` with payload `{ conversationId, sessionId, scheduledAt }`
|
|
96
|
+
- `message` is `undefined`
|
|
97
|
+
|
|
98
|
+
All other handler props (`conversation`, `state`, `execute`, `client`, `chat`) are available as normal. The `chat` prop provides access to transcript and message operations within the conversation (e.g., `chat.fetchTranscript()`, `chat.clearTranscript()`, `chat.saveTranscript()`).
|
|
99
|
+
|
|
100
|
+
## Session Object
|
|
101
|
+
|
|
102
|
+
Lifecycle-enabled conversations expose a read-only `conversation.session` property:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
handler: async ({ type, conversation }) => {
|
|
106
|
+
const session = conversation.session;
|
|
107
|
+
if (!session) return; // undefined when lifecycle is not configured
|
|
108
|
+
|
|
109
|
+
console.log(session.id); // ULID identifying the current session
|
|
110
|
+
console.log(session.number); // Monotonically increasing (1, 2, 3...)
|
|
111
|
+
console.log(session.status); // "active" or "expired"
|
|
112
|
+
console.log(session.startedAt); // ISO timestamp
|
|
113
|
+
console.log(session.lastActivityAt); // ISO timestamp of last user message
|
|
114
|
+
console.log(session.nudgeCount); // Number of nudges fired this session
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Session Fields
|
|
119
|
+
|
|
120
|
+
| Field | Type | Description |
|
|
121
|
+
|-------|------|-------------|
|
|
122
|
+
| `id` | `string` | ULID unique to the current session |
|
|
123
|
+
| `number` | `number` | Session counter, increments on each expiration (starts at 1) |
|
|
124
|
+
| `status` | `"active" \| "expired"` | Current session status |
|
|
125
|
+
| `startedAt` | `string` | ISO timestamp when this session began |
|
|
126
|
+
| `lastActivityAt` | `string` | ISO timestamp of the last user message |
|
|
127
|
+
| `nudgeCount` | `number` | Number of nudges delivered in this session |
|
|
128
|
+
|
|
129
|
+
## How Timers Work
|
|
130
|
+
|
|
131
|
+
Understanding the timer lifecycle is important for building correct nudge/expire logic.
|
|
132
|
+
|
|
133
|
+
### On Every User Message
|
|
134
|
+
|
|
135
|
+
1. `lastActivityAt` is updated to now
|
|
136
|
+
2. `nudgeCount` resets to 0
|
|
137
|
+
3. Any pending nudge and expire scheduled events are cancelled
|
|
138
|
+
4. New nudge and expire events are scheduled based on the configured delays
|
|
139
|
+
|
|
140
|
+
This means every user message resets the clock. Timers always measure silence from the most recent message.
|
|
141
|
+
|
|
142
|
+
### On Nudge Event
|
|
143
|
+
|
|
144
|
+
1. **Race guard**: If `lastActivityAt > scheduledAt`, the nudge is silently dropped (a message arrived after the nudge was scheduled)
|
|
145
|
+
2. **Workflow suppression**: If any active workflows exist on the conversation (`pending`, `in_progress`, `listening`, `paused`), the nudge is silently rescheduled at `intervalMs` and skipped
|
|
146
|
+
3. `nudgeCount` increments
|
|
147
|
+
4. The user's handler runs
|
|
148
|
+
5. If `nudgeCount < max` (or no max), the next nudge is scheduled at `intervalMs`
|
|
149
|
+
|
|
150
|
+
### On Expire Event
|
|
151
|
+
|
|
152
|
+
1. **Race guard**: If `lastActivityAt > scheduledAt`, the expire is silently dropped
|
|
153
|
+
2. The user's handler runs (errors are caught -- expiration cleanup always proceeds)
|
|
154
|
+
3. **Hard-kill expiration sequence** (strict ordering):
|
|
155
|
+
- Cancel all active workflows on the conversation
|
|
156
|
+
- Tag conversation with `sessionExpired = "true"`
|
|
157
|
+
- Reset conversation state to defaults (empty object)
|
|
158
|
+
- Clear the transcript
|
|
159
|
+
- Set lifecycle status to `"expired"`
|
|
160
|
+
|
|
161
|
+
### Session Renewal
|
|
162
|
+
|
|
163
|
+
When a message arrives on an expired session:
|
|
164
|
+
1. A new `sessionId` (ULID) is generated
|
|
165
|
+
2. `sessionNumber` increments
|
|
166
|
+
3. `nudgeCount` resets to 0
|
|
167
|
+
4. `status` becomes `"active"`
|
|
168
|
+
5. `startedAt` and `lastActivityAt` update to now
|
|
169
|
+
6. The `sessionExpired` conversation tag is cleared
|
|
170
|
+
7. New timers are scheduled
|
|
171
|
+
|
|
172
|
+
## Built-in Tags
|
|
173
|
+
|
|
174
|
+
Lifecycle automatically manages these tags without any user configuration:
|
|
175
|
+
|
|
176
|
+
### Conversation Tags
|
|
177
|
+
|
|
178
|
+
| Tag | Description |
|
|
179
|
+
|-----|-------------|
|
|
180
|
+
| `sessionExpired` | Set to `"true"` when a session expires. Cleared on renewal |
|
|
181
|
+
|
|
182
|
+
### Message Tags
|
|
183
|
+
|
|
184
|
+
| Tag | Description |
|
|
185
|
+
|-----|-------------|
|
|
186
|
+
| `sessionId` | ULID of the session this message belongs to |
|
|
187
|
+
| `sessionNumber` | Session sequence number at the time the message was sent |
|
|
188
|
+
|
|
189
|
+
Message tags are applied automatically to both incoming user messages and outgoing bot messages. They enable grouping messages by session.
|
|
190
|
+
|
|
191
|
+
## Internal State Namespace
|
|
192
|
+
|
|
193
|
+
Lifecycle state is managed by the runtime in a separate internal state namespace, not in the user-facing conversation state. This means:
|
|
194
|
+
|
|
195
|
+
- Lifecycle state survives conversation state resets (which happen on expiration)
|
|
196
|
+
- You cannot accidentally overwrite lifecycle fields by mutating `state`
|
|
197
|
+
- The `conversation.session` property is the intended read-only interface for accessing lifecycle data -- use it instead of attempting to access internal state directly
|
|
198
|
+
|
|
199
|
+
## Common Patterns
|
|
200
|
+
|
|
201
|
+
### Escalating Nudge Messages
|
|
202
|
+
|
|
203
|
+
Use `conversation.session.nudgeCount` to vary the message urgency:
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
handler: async ({ type, conversation }) => {
|
|
207
|
+
if (type === "nudge") {
|
|
208
|
+
const count = conversation.session?.nudgeCount ?? 0;
|
|
209
|
+
|
|
210
|
+
if (count === 1) {
|
|
211
|
+
await conversation.send({
|
|
212
|
+
type: "text",
|
|
213
|
+
payload: { text: "Just checking in -- do you still need help?" },
|
|
214
|
+
});
|
|
215
|
+
} else if (count === 2) {
|
|
216
|
+
await conversation.send({
|
|
217
|
+
type: "text",
|
|
218
|
+
payload: { text: "I'm still here if you need me." },
|
|
219
|
+
});
|
|
220
|
+
} else {
|
|
221
|
+
await conversation.send({
|
|
222
|
+
type: "text",
|
|
223
|
+
payload: { text: "This conversation will expire soon if there's no activity." },
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
// ... handle messages
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Expire with Summary
|
|
233
|
+
|
|
234
|
+
Send a session summary before the session is torn down:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
handler: async ({ type, conversation, state, execute }) => {
|
|
238
|
+
if (type === "expire") {
|
|
239
|
+
if (state.topic) {
|
|
240
|
+
await conversation.send({
|
|
241
|
+
type: "text",
|
|
242
|
+
payload: {
|
|
243
|
+
text: `Session expired. You were working on: ${state.topic}. Send a message to start a new session.`,
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
// State will be reset after this handler returns
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
// ... handle messages
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Nudge-Only (No Expiration)
|
|
255
|
+
|
|
256
|
+
You can configure nudges without expiration:
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
lifecycle: {
|
|
260
|
+
nudge: {
|
|
261
|
+
after: "3m",
|
|
262
|
+
max: 2,
|
|
263
|
+
},
|
|
264
|
+
// no expire -- sessions stay active indefinitely
|
|
265
|
+
},
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Expire-Only (No Nudges)
|
|
269
|
+
|
|
270
|
+
You can configure expiration without nudges:
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
lifecycle: {
|
|
274
|
+
// no nudge -- no re-engagement messages
|
|
275
|
+
expire: {
|
|
276
|
+
after: "30m",
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Pitfalls
|
|
282
|
+
|
|
283
|
+
### Nudges Do Not Fire During Active Workflows
|
|
284
|
+
|
|
285
|
+
If a workflow is running on the conversation (status `pending`, `in_progress`, `listening`, or `paused`), nudges are silently suppressed and rescheduled. This prevents the bot from sending "Are you still there?" while a workflow is actively processing. The check is fail-closed: if the workflow status query fails, the nudge is suppressed. See [workflow-steps.md](./workflow-steps.md) for the step methods that put workflows into these statuses.
|
|
286
|
+
|
|
287
|
+
### Expire Handler Errors Do Not Block Cleanup
|
|
288
|
+
|
|
289
|
+
If the expire handler throws, the error is caught and the expiration sequence still runs (workflow cancellation, state reset, transcript clear). The error is re-thrown after cleanup completes. Do not rely on throwing in the expire handler to prevent expiration.
|
|
290
|
+
|
|
291
|
+
### State Is Reset on Expiration
|
|
292
|
+
|
|
293
|
+
After expiration, conversation state is reset to defaults (the empty object triggers schema defaults on the next load). Any data you need to survive across sessions must be stored elsewhere (user state, bot state, or tables).
|
|
294
|
+
|
|
295
|
+
### Typing Indicators Are Suppressed for Lifecycle Events
|
|
296
|
+
|
|
297
|
+
The runtime does not show typing indicators for nudge or expire events. This is intentional -- lifecycle events should feel automatic, not like the bot is "thinking."
|
|
298
|
+
|
|
299
|
+
### Timer Scheduling Failures Are Non-Fatal
|
|
300
|
+
|
|
301
|
+
If the runtime fails to schedule a nudge or expire event (e.g., API error), it logs a warning but does not crash the handler. The next user message will attempt to schedule new timers.
|
|
302
|
+
|
|
303
|
+
### Race Guards Prevent Stale Events
|
|
304
|
+
|
|
305
|
+
Both nudge and expire events carry a `scheduledAt` timestamp. If a user message arrived after the event was scheduled (i.e., `lastActivityAt > scheduledAt`), the event is silently dropped. This prevents stale timers from firing after the user has already re-engaged.
|
|
306
|
+
|
|
307
|
+
## See Also
|
|
308
|
+
|
|
309
|
+
- [Conversations](./conversations.md) — Conversation setup, channel routing, and handler parameters
|
|
310
|
+
- [Workflows](./workflows.md) — Workflow basics and how workflows interact with conversations
|
|
311
|
+
- [Workflow Steps](./workflow-steps.md) — Step methods whose statuses affect nudge suppression
|