@cursor/july 0.1.7 → 0.1.9
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/dist/bin/agent-serve.js +31 -2
- package/dist/channels/slack/channel-watch.d.ts +6 -1
- package/dist/channels/slack/channel-watch.d.ts.map +1 -1
- package/dist/channels/slack/channel-watch.js +22 -1
- package/dist/channels/slack/cursor-account.d.ts +4 -0
- package/dist/channels/slack/cursor-account.d.ts.map +1 -1
- package/dist/channels/slack/cursor-account.js +12 -2
- package/dist/channels/slack/defaults.d.ts +10 -5
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +39 -17
- package/dist/channels/slack/dispatch.d.ts +18 -0
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +141 -38
- package/dist/channels/slack/inbound.d.ts +13 -3
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +17 -9
- package/dist/channels/slack/index.d.ts +4 -0
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +4 -0
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +1 -0
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +3 -0
- package/dist/channels/slack/nudge-carrier.d.ts +19 -0
- package/dist/channels/slack/nudge-carrier.d.ts.map +1 -0
- package/dist/channels/slack/nudge-carrier.js +166 -0
- package/dist/channels/slack/nudge-store.d.ts +61 -0
- package/dist/channels/slack/nudge-store.d.ts.map +1 -0
- package/dist/channels/slack/nudge-store.js +266 -0
- package/dist/channels/slack/post-update-delivery.js +318 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +74 -25
- package/dist/channels/slack/stop.d.ts +14 -0
- package/dist/channels/slack/stop.d.ts.map +1 -0
- package/dist/channels/slack/stop.js +22 -0
- package/dist/channels/slack/thread-context.d.ts +81 -0
- package/dist/channels/slack/thread-context.d.ts.map +1 -0
- package/dist/channels/slack/thread-context.js +470 -0
- package/dist/channels/slack/types.d.ts +27 -0
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.COTN7wgo.js → app.Ciosf24M.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.D154Detj.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.BW3TBdT0.js → VPLocalSearchBox.BcRDly7n.js} +1 -1
- package/dist/docs/assets/chunks/{theme.BEJW0vE7.js → theme.CMpbjdfx.js} +2 -2
- package/dist/docs/assets/{deployment.md.BtfEsc9S.js → deployment.md.DC5-ImcM.js} +1 -1
- package/dist/docs/assets/{example-agents_approval-buddy.md.8R5phXb5.js → example-agents_approval-buddy.md.DFGBYLcc.js} +2 -2
- package/dist/docs/assets/{example-agents_approval-buddy.md.8R5phXb5.lean.js → example-agents_approval-buddy.md.DFGBYLcc.lean.js} +1 -1
- package/dist/docs/assets/example-agents_index.md.BPXjUtRB.js +2 -0
- package/dist/docs/assets/example-agents_index.md.BPXjUtRB.lean.js +1 -0
- package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.js +10 -0
- package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.lean.js +1 -0
- package/dist/docs/assets/{example-agents_security-reviewer.md.D2rtwDTO.js → example-agents_security-reviewer.md.CE9v-aKm.js} +2 -2
- package/dist/docs/assets/{example-agents_security-reviewer.md.D2rtwDTO.lean.js → example-agents_security-reviewer.md.CE9v-aKm.lean.js} +1 -1
- package/dist/docs/assets/{guides_slack.md.CCwqHvSV.js → guides_slack.md.FWale6Ip.js} +4 -3
- package/dist/docs/assets/{guides_slack.md.CCwqHvSV.lean.js → guides_slack.md.FWale6Ip.lean.js} +1 -1
- package/dist/docs/assets/{reference_sessions.md.DdjWabnN.js → reference_sessions.md.DLd6mvbv.js} +2 -2
- package/dist/docs/assets/{reference_sessions.md.DdjWabnN.lean.js → reference_sessions.md.DLd6mvbv.lean.js} +1 -1
- package/dist/docs/assets/{storage.md.CVnInNiN.js → storage.md.DRTdnFvd.js} +9 -2
- package/dist/docs/assets/{storage.md.CVnInNiN.lean.js → storage.md.DRTdnFvd.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +6 -6
- package/dist/docs/evals.html +4 -4
- package/dist/docs/example-agents/approval-buddy.html +5 -5
- package/dist/docs/example-agents/benny.html +5 -5
- package/dist/docs/example-agents/bugbot.html +5 -5
- package/dist/docs/example-agents/codebase-wiki.html +4 -4
- package/dist/docs/example-agents/codeowners-review.html +4 -4
- package/dist/docs/example-agents/concierge.html +4 -4
- package/dist/docs/example-agents/fsd.html +4 -4
- package/dist/docs/example-agents/index.html +6 -6
- package/dist/docs/example-agents/knowledge-base.html +4 -4
- package/dist/docs/example-agents/oncall.html +34 -0
- package/dist/docs/example-agents/security-reviewer.html +6 -6
- package/dist/docs/example-agents/slack-agent.html +4 -4
- package/dist/docs/example-agents/weather-agent.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- package/dist/docs/guides/github.html +4 -4
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/slack.html +7 -6
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +4 -4
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +4 -4
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +5 -5
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +4 -4
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/storage.html +13 -6
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +45 -18
- package/dist/internal/cli-mcp.d.ts +64 -0
- package/dist/internal/cli-mcp.d.ts.map +1 -0
- package/dist/internal/cli-mcp.js +570 -0
- package/dist/internal/deploy-client.d.ts +7 -1
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +3 -0
- package/dist/internal/deploy-source.d.ts +3 -0
- package/dist/internal/deploy-source.d.ts.map +1 -1
- package/dist/internal/deploy-source.js +25 -7
- package/dist/internal/discovery.js +1 -1
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +3 -0
- package/dist/internal/host-kv.d.ts +17 -0
- package/dist/internal/host-kv.d.ts.map +1 -0
- package/dist/internal/host-kv.js +77 -0
- package/dist/internal/hosting.d.ts +12 -2
- package/dist/internal/hosting.d.ts.map +1 -1
- package/dist/internal/hosting.js +21 -4
- package/dist/internal/json-dir-store.js +100 -0
- package/dist/internal/logs-client.d.ts +16 -2
- package/dist/internal/logs-client.d.ts.map +1 -1
- package/dist/internal/logs-client.js +51 -16
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +8 -0
- package/dist/internal/session-engine.d.ts +42 -7
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +276 -66
- package/dist/internal/storage-coordinator.d.ts +18 -1
- package/dist/internal/storage-coordinator.d.ts.map +1 -1
- package/dist/internal/storage-coordinator.js +43 -0
- package/dist/internal/workspace.d.ts +19 -0
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +47 -13
- package/dist/playground/assets/{index-72vCOBWO.js → index-C0-QEG4O.js} +42 -42
- package/dist/playground/assets/index-z3JY9O2x.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted.d.ts +65 -0
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -0
- package/dist/storage-backends/cursor-hosted.js +203 -0
- package/dist/storage-backends/file-kv.d.ts +42 -0
- package/dist/storage-backends/file-kv.d.ts.map +1 -0
- package/dist/storage-backends/file-kv.js +190 -0
- package/dist/storage-backends/postgres-kv.d.ts +82 -0
- package/dist/storage-backends/postgres-kv.d.ts.map +1 -0
- package/dist/storage-backends/postgres-kv.js +185 -0
- package/dist/storage.d.ts +10 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +10 -1
- package/dist/types.d.ts +79 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/.vitepress/cache/deps/package.json +3 -0
- package/docs/deployment.md +7 -3
- package/docs/example-agents/approval-buddy.md +1 -1
- package/docs/example-agents/index.md +15 -10
- package/docs/example-agents/oncall.md +216 -0
- package/docs/example-agents/security-reviewer.md +5 -5
- package/docs/guides/slack.md +11 -2
- package/docs/reference/sessions.md +13 -4
- package/docs/storage.md +41 -0
- package/package.json +22 -1
- package/skills/framework-map/SKILL.md +6 -4
- package/src/bin/agent-serve.ts +32 -2
- package/src/channels/slack/channel-watch.ts +28 -1
- package/src/channels/slack/cursor-account.ts +20 -0
- package/src/channels/slack/defaults.ts +51 -20
- package/src/channels/slack/dispatch.ts +215 -40
- package/src/channels/slack/inbound.ts +30 -10
- package/src/channels/slack/index.ts +4 -0
- package/src/channels/slack/live-delivery.ts +1 -0
- package/src/channels/slack/manifest.ts +3 -0
- package/src/channels/slack/nudge-carrier.ts +182 -0
- package/src/channels/slack/nudge-store.ts +308 -0
- package/src/channels/slack/slack-channel.ts +106 -31
- package/src/channels/slack/stop.ts +25 -0
- package/src/channels/slack/thread-context.ts +572 -0
- package/src/channels/slack/types.ts +27 -0
- package/src/internal/cli-deploy.ts +58 -20
- package/src/internal/cli-mcp.ts +708 -0
- package/src/internal/deploy-client.ts +11 -0
- package/src/internal/deploy-source.ts +24 -6
- package/src/internal/discovery.ts +1 -1
- package/src/internal/distribution.ts +3 -0
- package/src/internal/host-kv.ts +85 -0
- package/src/internal/hosting.ts +24 -4
- package/src/internal/logs-client.ts +67 -21
- package/src/internal/server.ts +31 -0
- package/src/internal/session-engine.ts +379 -61
- package/src/internal/storage-coordinator.ts +41 -0
- package/src/internal/workspace.ts +48 -11
- package/src/storage-backends/cursor-hosted.ts +246 -0
- package/src/storage-backends/file-kv.ts +221 -0
- package/src/storage-backends/postgres-kv.ts +253 -0
- package/src/storage.ts +11 -1
- package/src/types.ts +83 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.B7UcKvIn.js +0 -1
- package/dist/docs/assets/example-agents_index.md.CRqJlnIf.js +0 -2
- package/dist/docs/assets/example-agents_index.md.CRqJlnIf.lean.js +0 -1
- package/dist/internal/persistence-coordinator.d.ts +0 -127
- package/dist/internal/persistence-coordinator.d.ts.map +0 -1
- package/dist/persistence.d.ts +0 -184
- package/dist/persistence.d.ts.map +0 -1
- package/dist/playground/assets/index-BjnMwYoR.css +0 -1
- /package/dist/docs/assets/{deployment.md.BtfEsc9S.lean.js → deployment.md.DC5-ImcM.lean.js} +0 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Investigate every alert in its own Slack thread"
|
|
3
|
+
description: "Watch a bot-fed alerts channel, react when the agent locks in, coalesce thread chatter behind a quiet window, and let the agent schedule its own re-checks."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Investigate every alert in its own Slack thread
|
|
7
|
+
|
|
8
|
+
This agent is an on-call teammate. Alert feeds post into an alerts channel
|
|
9
|
+
as bots. Each new alert dispatches an investigation session pinned to that
|
|
10
|
+
post's thread: the agent reacts 👀 the moment it locks in, investigates
|
|
11
|
+
immediately, and posts brief findings backed by evidence it observed.
|
|
12
|
+
Replies in the thread reach it only after the thread has been quiet for
|
|
13
|
+
about a minute, and reminder tools let it wake itself later to re-check a
|
|
14
|
+
baseline or confirm an alert cleared.
|
|
15
|
+
|
|
16
|
+
Use this example when alerts land in Slack and you want one thread-scoped
|
|
17
|
+
investigation per alert, with an agent that paces its own engagement
|
|
18
|
+
instead of answering every message.
|
|
19
|
+
|
|
20
|
+
[Browse the current alert-investigator source.](../../examples/oncall/)
|
|
21
|
+
|
|
22
|
+
## Follow an alert
|
|
23
|
+
|
|
24
|
+
1. An alert feed (Alertmanager, PagerDuty, Datadog) posts a new top-level
|
|
25
|
+
message in the watched alerts channel.
|
|
26
|
+
2. The channel watch accepts it. `includeBotPosts` lets bot authors
|
|
27
|
+
through; the agent's own posts always stay dropped.
|
|
28
|
+
3. The handler reacts 👀 on the alert post and sets "Investigating…"
|
|
29
|
+
typing. The reaction is the lock-in signal: this alert has an owner.
|
|
30
|
+
4. Agentkit creates a session keyed to the alert's thread and dispatches
|
|
31
|
+
immediately. New alerts get no debounce.
|
|
32
|
+
5. The agent reads the alert, gathers evidence, and posts findings to the
|
|
33
|
+
thread once it has a hypothesis.
|
|
34
|
+
6. People discuss in the thread. Replies buffer per thread and dispatch as
|
|
35
|
+
one coalesced follow-up after roughly a minute of quiet.
|
|
36
|
+
7. The agent arms reminders for anything that needs time and posts interim
|
|
37
|
+
updates when new evidence changes the picture.
|
|
38
|
+
|
|
39
|
+
Mentions and DMs skip the watch entirely and behave like ordinary chat.
|
|
40
|
+
|
|
41
|
+
## Map the files
|
|
42
|
+
|
|
43
|
+
| File | Purpose |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| [`agent/agent.ts`](../../examples/oncall/agent/agent.ts) | Names the agent and keeps harness workspaces outside any monorepo checkout. |
|
|
46
|
+
| [`agent/instructions.md`](../../examples/oncall/agent/instructions.md) | Engagement rules, the investigation loop, and the message discipline. |
|
|
47
|
+
| [`agent/channels/slack-app.ts`](../../examples/oncall/agent/channels/slack-app.ts) | Dedicated Socket Mode app: watch configuration and handler wiring. |
|
|
48
|
+
| [`agent/lib/alert-watch.ts`](../../examples/oncall/agent/lib/alert-watch.ts) | The engagement policy: lock in on new alerts, coalesce replies. |
|
|
49
|
+
| [`agent/lib/thread-debounce.ts`](../../examples/oncall/agent/lib/thread-debounce.ts) | Per-thread quiet window. |
|
|
50
|
+
| [`agent/lib/alerts.ts`](../../examples/oncall/agent/lib/alerts.ts) | Dispatch classification, prompt building, and thread addressing. |
|
|
51
|
+
| [`agent/lib/slack-api.ts`](../../examples/oncall/agent/lib/slack-api.ts) | Reactions and thread posts on this agent's own token pair. |
|
|
52
|
+
| [`agent/tools/reminders_create.ts`](../../examples/oncall/agent/tools/reminders_create.ts) | Self-scheduled wakes bound to the thread (plus `reminders_list` and `reminders_cancel`). |
|
|
53
|
+
| [`agent/tools/post_thread_update.ts`](../../examples/oncall/agent/tools/post_thread_update.ts) | Interim updates to the thread mid-turn. |
|
|
54
|
+
| [`evals/smoke.eval.ts`](../../examples/oncall/evals/smoke.eval.ts) | Checks identity and the reminder-tool route. |
|
|
55
|
+
|
|
56
|
+
## Let bot posts through the watch
|
|
57
|
+
|
|
58
|
+
Channel watching drops bot-authored posts by default so two agents can
|
|
59
|
+
never feed each other. Alert channels invert the assumption: the posts
|
|
60
|
+
worth watching come from bots. `channelPosts.includeBotPosts` opts in per
|
|
61
|
+
channel:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
engagement: {
|
|
65
|
+
channelPosts: {
|
|
66
|
+
allow: ["#alerts"],
|
|
67
|
+
posts: "all",
|
|
68
|
+
includeBotPosts: true,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Loop safety survives the opt-in. The pack matches the watching app's own
|
|
74
|
+
posts by the `bot_id` and bot user id from `auth.test` and drops them, so
|
|
75
|
+
the agent's findings never re-dispatch it. Posts that mention the bot stay
|
|
76
|
+
on the mention path.
|
|
77
|
+
|
|
78
|
+
`posts: "all"` also delivers thread replies. The handler, not the pack,
|
|
79
|
+
decides their pace.
|
|
80
|
+
|
|
81
|
+
## Pace the engagement
|
|
82
|
+
|
|
83
|
+
The example runs two rhythms:
|
|
84
|
+
|
|
85
|
+
- A new alert dispatches immediately.
|
|
86
|
+
- Thread replies produce one engagement per lull.
|
|
87
|
+
|
|
88
|
+
The pack's `debounceMs` is per message; it exists to let edits settle. This
|
|
89
|
+
agent needs a per-thread window instead, so the handler owns it
|
|
90
|
+
([`lib/thread-debounce.ts`](../../examples/oncall/agent/lib/thread-debounce.ts)).
|
|
91
|
+
Every reply restarts a 60-second timer keyed by thread. Superseded waiters
|
|
92
|
+
resolve `null` and the handler returns `null` for them. When the thread
|
|
93
|
+
goes quiet, the newest waiter receives the whole batch and dispatches one
|
|
94
|
+
follow-up that lists every message with mentionable attribution.
|
|
95
|
+
|
|
96
|
+
Two details make the window matter. A follow-up that arrives while a turn
|
|
97
|
+
runs preempts that turn (latest message wins), so engaging per message
|
|
98
|
+
would keep cancelling the investigation. And @mentions bypass the window
|
|
99
|
+
through Slack's mention path, so a person who needs the agent now still
|
|
100
|
+
gets it now.
|
|
101
|
+
|
|
102
|
+
## Schedule your own re-checks
|
|
103
|
+
|
|
104
|
+
Investigations rarely finish in one pass. A baseline comparison needs 20
|
|
105
|
+
minutes of data. An alert that cleared may re-fire. The example hands the
|
|
106
|
+
model three tools over `host.reminders`:
|
|
107
|
+
|
|
108
|
+
- `reminders_create` arms a one-shot (`delay: "20m"`) or recurring
|
|
109
|
+
(`every: "30m"` with a plain-language stop condition) wake bound to the
|
|
110
|
+
thread's conversation.
|
|
111
|
+
- `reminders_list` shows the thread's standing watches.
|
|
112
|
+
- `reminders_cancel` disarms one, and refuses ids that belong to another
|
|
113
|
+
thread's conversation.
|
|
114
|
+
|
|
115
|
+
When a reminder fires, its prompt returns to the same session as a
|
|
116
|
+
follow-up turn, and the reply lands in the alert thread. The instructions
|
|
117
|
+
keep wake prompts generic (re-read live state instead of replaying stale
|
|
118
|
+
numbers) and wake replies to one line, for example "re-checked p99 on
|
|
119
|
+
api-gateway: 120ms, back at baseline, cancelling the watch."
|
|
120
|
+
|
|
121
|
+
Keep these tool filenames if you copy the design: the framework's reminder
|
|
122
|
+
fire prompt tells the model to call `reminders_cancel` by name when a stop
|
|
123
|
+
condition is set.
|
|
124
|
+
|
|
125
|
+
## Alert people mid-investigation
|
|
126
|
+
|
|
127
|
+
The final reply of each turn posts to the thread on its own.
|
|
128
|
+
`post_thread_update` covers evidence that shouldn't wait for the turn to
|
|
129
|
+
finish: it posts a one-or-two-sentence update through the agent's token,
|
|
130
|
+
with `<@USERID>` mentions for the people who need to act. The instructions
|
|
131
|
+
restrict it to changes in hypothesis, severity, or blast radius. Progress
|
|
132
|
+
narration doesn't qualify.
|
|
133
|
+
|
|
134
|
+
## Connect the Slack app
|
|
135
|
+
|
|
136
|
+
Channel watching is Socket Mode only, so this example uses a dedicated
|
|
137
|
+
app:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
agentkit slack init --dir examples/oncall --name "Oncall" --channel-posts
|
|
141
|
+
agentkit slack doctor --prefix ONCALL
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The generated manifest subscribes to `message.channels` and
|
|
145
|
+
`message.groups` and includes `reactions:write` for the lock-in reaction.
|
|
146
|
+
Install the app, export `ONCALL_SLACK_BOT_TOKEN` and
|
|
147
|
+
`ONCALL_SLACK_APP_TOKEN`, and invite the bot to each watched channel.
|
|
148
|
+
|
|
149
|
+
`ONCALL_ALERTS_CHANNELS` sets the watch list as comma-separated ids or
|
|
150
|
+
`#names`. It defaults to `#alerts`.
|
|
151
|
+
|
|
152
|
+
Wire observability MCP servers under `agent/mcp-connections/` so evidence
|
|
153
|
+
gathering reaches your logs, metrics, and dashboards. The example ships
|
|
154
|
+
none; without them the agent works from the alert text, its links, and the
|
|
155
|
+
thread.
|
|
156
|
+
|
|
157
|
+
## Validate and start the server
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
agentkit validate --dir examples/oncall
|
|
161
|
+
agentkit info --dir examples/oncall --json
|
|
162
|
+
agentkit dev examples/oncall
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The info output lists four server tools and the watched channel on the
|
|
166
|
+
`slack-app` channel. Missing tokens leave that channel idle without
|
|
167
|
+
stopping the server.
|
|
168
|
+
|
|
169
|
+
In dev mode, reminder timers don't auto-fire. List and fire them by hand
|
|
170
|
+
through the dev routes described in
|
|
171
|
+
[Schedules and reminders](../reference/schedules.md#dispatch-and-dev-mode).
|
|
172
|
+
|
|
173
|
+
## Test the policy without Slack
|
|
174
|
+
|
|
175
|
+
The engagement policy is plain code with unit tests:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
pnpm exec vitest run examples/oncall
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The integration test drives a synthetic Events API delivery through the
|
|
182
|
+
real parse, watch, and dispatch plumbing. It asserts a bot alert
|
|
183
|
+
dispatches pinned to its thread after the lock-in reaction, the agent's
|
|
184
|
+
own posts never loop, and replies coalesce behind the quiet window.
|
|
185
|
+
|
|
186
|
+
The smoke eval spends a model turn:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
agentkit eval --dir examples/oncall smoke --json
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
It checks identity and the reminder-tool route lexically. It doesn't prove
|
|
193
|
+
Slack delivery or reaction behavior; the unit tests cover the dispatch
|
|
194
|
+
side, and a live check needs the dedicated app connected.
|
|
195
|
+
|
|
196
|
+
## Build an alert investigator
|
|
197
|
+
|
|
198
|
+
Use this structure when a bot feed should drive thread-scoped work:
|
|
199
|
+
|
|
200
|
+
1. Watch the feed channel with `includeBotPosts: true` and a narrow
|
|
201
|
+
allowlist.
|
|
202
|
+
2. Acknowledge on the triggering post before dispatching, so people see
|
|
203
|
+
ownership without opening the thread.
|
|
204
|
+
3. Dispatch new items immediately; coalesce thread chatter behind a
|
|
205
|
+
per-thread quiet window.
|
|
206
|
+
4. Give the agent reminder tools for anything that needs time, and make
|
|
207
|
+
cancel discipline part of the instructions.
|
|
208
|
+
5. Keep every posted message brief and tied to evidence the agent saw.
|
|
209
|
+
|
|
210
|
+
## Where to go next
|
|
211
|
+
|
|
212
|
+
- [Slack](../guides/slack.md)
|
|
213
|
+
- [Schedules and reminders](../reference/schedules.md)
|
|
214
|
+
- [Tools](../reference/tools.md)
|
|
215
|
+
- [Playbook router](./benny.md) for the human-post variant of channel
|
|
216
|
+
watching
|
|
@@ -71,7 +71,7 @@ status from the finding count even when the review posting result says
|
|
|
71
71
|
| Progress channel | [`agent/channels/asr-progress.ts`](../../examples/security-reviewer/agent/channels/asr-progress.ts) | Serve live reviewer and triage state by `runId`. |
|
|
72
72
|
| Playground renderer | [`agent/playground/tools/run_reviewers.tsx`](../../examples/security-reviewer/agent/playground/tools/run_reviewers.tsx) | Replace the generic tool chip with live module rows. |
|
|
73
73
|
| SDK review pipeline | [`agent/lib/asr/`](../../examples/security-reviewer/agent/lib/asr/), [`review-stages.ts`](../../examples/security-reviewer/agent/lib/review-stages.ts) | Select modules, call model workers, triage, deduplicate, and write artifacts. |
|
|
74
|
-
| Storage | [`agent/storage.ts`](../../examples/security-reviewer/agent/storage.ts) | Persist framework sessions
|
|
74
|
+
| Storage | [`agent/storage.ts`](../../examples/security-reviewer/agent/storage.ts) | Persist framework sessions with `cursorHostedStorage` (lazy restore). |
|
|
75
75
|
| A/B | [`agent/ab.ts`](../../examples/security-reviewer/agent/ab.ts), [`agent/ab.config.ts`](../../examples/security-reviewer/agent/ab.config.ts) | Compare all-severity versus high-only GitHub comments. |
|
|
76
76
|
| Eval | [`evals/`](../../examples/security-reviewer/evals/) | Check stage-tool presence against a pinned sample. |
|
|
77
77
|
|
|
@@ -190,10 +190,10 @@ findings, accounting, and audit events.
|
|
|
190
190
|
|
|
191
191
|
## Separate session storage from review artifacts
|
|
192
192
|
|
|
193
|
-
`defineStorage` sends agentkit session and event records
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
session.
|
|
193
|
+
`defineStorage` + `cursorHostedStorage` sends agentkit session and event records
|
|
194
|
+
to Cursor-hosted Bugbot storage through the control-plane proxy. Security
|
|
195
|
+
Reviewer sets `restore: "off"` so startup doesn't load old review sessions in
|
|
196
|
+
bulk. A continuation lookup can still fetch a needed session.
|
|
197
197
|
|
|
198
198
|
The staged review files are separate from session storage. Session-store
|
|
199
199
|
durability doesn't preserve those files. All stages for one `runId` must see
|
package/docs/guides/slack.md
CHANGED
|
@@ -146,6 +146,7 @@ export default slackChannel({
|
|
|
146
146
|
allow: ["#triage-alerts"], // explicit allowlist; no wildcard exists
|
|
147
147
|
posts: "top-level", // default: thread replies never dispatch
|
|
148
148
|
debounceMs: 15_000, // optional: let rapid edits settle
|
|
149
|
+
includeBotPosts: false, // default: bot-authored posts never dispatch
|
|
149
150
|
},
|
|
150
151
|
},
|
|
151
152
|
onChannelPost: async (ctx, message) => {
|
|
@@ -160,12 +161,20 @@ on the Slack app (Socket Mode only — not available with
|
|
|
160
161
|
`cursorAccount: true`). Generate manifests with `slack init --channel-posts`,
|
|
161
162
|
or add the events and re-import. The bot must also be a member of each
|
|
162
163
|
watched channel. Posts that mention the bot stay on the `app_mention`
|
|
163
|
-
path, bot-authored posts never dispatch, and deleting a post
|
|
164
|
-
debounce window cancels its dispatch. Watched posts reuse the
|
|
164
|
+
path, bot-authored posts never dispatch by default, and deleting a post
|
|
165
|
+
inside the debounce window cancels its dispatch. Watched posts reuse the
|
|
165
166
|
thread-scoped principal, so a later @mention continues the same session.
|
|
166
167
|
Set `engagement.mentions` or `engagement.directMessages` to `false` to
|
|
167
168
|
turn those surfaces off.
|
|
168
169
|
|
|
170
|
+
Set `includeBotPosts: true` when the posts worth watching come from bots:
|
|
171
|
+
alert feeds, webhook integrations, or other agents posting notes. The
|
|
172
|
+
watching app's own posts stay dropped either way, matched by the `bot_id`
|
|
173
|
+
and bot user id from `auth.test`, so an agent can never dispatch on its
|
|
174
|
+
own replies. The
|
|
175
|
+
[alert investigator example](../example-agents/oncall.md) watches a
|
|
176
|
+
bot-fed alerts channel this way.
|
|
177
|
+
|
|
169
178
|
## Prepare work on the host
|
|
170
179
|
|
|
171
180
|
Mention and DM handlers may return a prepared `message`,
|
|
@@ -50,13 +50,22 @@ Task sessions don't accept follow-ups. Trying one returns `409`.
|
|
|
50
50
|
|
|
51
51
|
## What happens when I send a follow-up?
|
|
52
52
|
|
|
53
|
-
A follow-up to an idle chat session starts another turn.
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
A follow-up to an idle chat session starts another turn. Admission when
|
|
54
|
+
the session is already busy depends on the channel:
|
|
55
|
+
|
|
56
|
+
| Path | Busy-session policy |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| HTTP playground / `POST /v1/session/:id` / MCP `ask` | **Preempt** (default): interrupt the in-flight turn, wait for it to settle, then run the new message |
|
|
59
|
+
| Slack mentions / DMs / alert-watch | **Coalesce**: leave the active turn running, enqueue the follow-up, and drain queued asks into one follow-up turn when the active turn finishes (no mid-turn tool/hook inject) |
|
|
60
|
+
|
|
61
|
+
Pass `admission: "coalesce"` on `send()` to opt into the Slack policy from
|
|
62
|
+
other callers. Omit it (or pass `"preempt"`) to keep interrupt semantics.
|
|
56
63
|
|
|
57
64
|
`POST /v1/session/:id/stop` interrupts a turn without sending a new
|
|
58
65
|
message. Interrupted turns record `turn.failed` with
|
|
59
|
-
`"turn interrupted"`. This means the turn was preempted.
|
|
66
|
+
`"turn interrupted"`. This means the turn was preempted. A whole-message
|
|
67
|
+
Slack `stop` / `@agent stop` does the same for that thread and clears
|
|
68
|
+
pending coalesced nudges.
|
|
60
69
|
|
|
61
70
|
Session-bound deterministic tool calls share the same execution lock.
|
|
62
71
|
They return `409 session_busy` while a model turn is running.
|
package/docs/storage.md
CHANGED
|
@@ -96,3 +96,44 @@ With `get` and `list`, serve can rebuild local state from your store:
|
|
|
96
96
|
|
|
97
97
|
A turn in flight at crash time is not replayed. The next follow-up
|
|
98
98
|
resumes from the last flushed state.
|
|
99
|
+
|
|
100
|
+
## Author KV (`ctx.host.kv`)
|
|
101
|
+
|
|
102
|
+
Handlers can store their own JSON under the same sink without minting
|
|
103
|
+
framework keys:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
await ctx.host.kv.put("alert-memory/abc", { updated: "…" });
|
|
107
|
+
const prior = await ctx.host.kv.get("alert-memory/abc");
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
agentkit prefixes author keys as `agentkit/v1/{agent}/kv/{key}` (same
|
|
111
|
+
bounded encoding as continuation tokens). Writes **await** the sink and
|
|
112
|
+
propagate errors — unlike session mirrors, which are at-most-once.
|
|
113
|
+
|
|
114
|
+
Without `agent/storage.ts`, `host.kv` falls back to files under
|
|
115
|
+
`--state-root/kv`. That is fine for local dogfood; it does **not**
|
|
116
|
+
survive replacing the host. For Cursor-managed hosting, prefer
|
|
117
|
+
`@cursor/july/storage/cursor-hosted` so sessions and author KV share the
|
|
118
|
+
platform Bugbot tables through a control-plane HTTP proxy (authenticated
|
|
119
|
+
as the deployment pod credential — engines never receive a database URL).
|
|
120
|
+
For self-hosted Postgres you own, use `@cursor/july/storage/postgres-kv`
|
|
121
|
+
and supply `BUGBOTDB_URL` / `AGENT_SERVE_DEPLOYMENT_ID` yourself.
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
// agent/storage.ts — Cursor-managed hosting
|
|
125
|
+
import { defineStorage } from "@cursor/july/storage";
|
|
126
|
+
import { cursorHostedStorage } from "@cursor/july/storage/cursor-hosted";
|
|
127
|
+
|
|
128
|
+
export default defineStorage({
|
|
129
|
+
...cursorHostedStorage(),
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Built-in helpers:
|
|
134
|
+
|
|
135
|
+
| Import | Backend |
|
|
136
|
+
| --- | --- |
|
|
137
|
+
| `@cursor/july/storage/file-kv` | File-per-key under `.agent-serve/kv` |
|
|
138
|
+
| `@cursor/july/storage/cursor-hosted` | Platform Bugbot `agent_serve_*` via control-plane proxy |
|
|
139
|
+
| `@cursor/july/storage/postgres-kv` | Same tables; you supply `BUGBOTDB_URL` / `AGENT_SERVE_DEPLOYMENT_ID` |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cursor/july",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "(early alpha) Filesystem-first framework for defining Cursor agents as markdown and TypeScript and serving them over channels with the Cursor SDK.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"repository": {
|
|
@@ -124,6 +124,27 @@
|
|
|
124
124
|
"import": "./dist/storage.js",
|
|
125
125
|
"default": "./dist/storage.js"
|
|
126
126
|
},
|
|
127
|
+
"./storage/file-kv": {
|
|
128
|
+
"anysphere-source": "./src/storage-backends/file-kv.ts",
|
|
129
|
+
"bun": "./src/storage-backends/file-kv.ts",
|
|
130
|
+
"types": "./dist/storage-backends/file-kv.d.ts",
|
|
131
|
+
"import": "./dist/storage-backends/file-kv.js",
|
|
132
|
+
"default": "./dist/storage-backends/file-kv.js"
|
|
133
|
+
},
|
|
134
|
+
"./storage/postgres-kv": {
|
|
135
|
+
"anysphere-source": "./src/storage-backends/postgres-kv.ts",
|
|
136
|
+
"bun": "./src/storage-backends/postgres-kv.ts",
|
|
137
|
+
"types": "./dist/storage-backends/postgres-kv.d.ts",
|
|
138
|
+
"import": "./dist/storage-backends/postgres-kv.js",
|
|
139
|
+
"default": "./dist/storage-backends/postgres-kv.js"
|
|
140
|
+
},
|
|
141
|
+
"./storage/cursor-hosted": {
|
|
142
|
+
"anysphere-source": "./src/storage-backends/cursor-hosted.ts",
|
|
143
|
+
"bun": "./src/storage-backends/cursor-hosted.ts",
|
|
144
|
+
"types": "./dist/storage-backends/cursor-hosted.d.ts",
|
|
145
|
+
"import": "./dist/storage-backends/cursor-hosted.js",
|
|
146
|
+
"default": "./dist/storage-backends/cursor-hosted.js"
|
|
147
|
+
},
|
|
127
148
|
"./package.json": {
|
|
128
149
|
"anysphere-source": "./package.json",
|
|
129
150
|
"bun": "./package.json",
|
|
@@ -97,10 +97,12 @@ Two handles, two owners:
|
|
|
97
97
|
- **sessionId** — runtime-owned handle for streaming/inspection
|
|
98
98
|
(`GET /v1/session/:id/stream?startIndex=N`, durable NDJSON replay).
|
|
99
99
|
|
|
100
|
-
A follow-up to a busy session **interrupts the in-flight turn**
|
|
101
|
-
for it to settle before the new turn sends.
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
A follow-up to a busy HTTP/MCP session **interrupts the in-flight turn**
|
|
101
|
+
and waits for it to settle before the new turn sends. Slack uses
|
|
102
|
+
**coalesce** instead (enqueue + boundary drain; see
|
|
103
|
+
`docs/reference/sessions.md`). `POST /v1/session/:id/stop` interrupts
|
|
104
|
+
without sending. Session-bound deterministic tool calls are rejected with
|
|
105
|
+
`409 session_busy` while a turn runs.
|
|
104
106
|
|
|
105
107
|
Key built-in routes (each agent, under `/<slug>` in multi-agent mode):
|
|
106
108
|
`POST /v1/session`, `POST /v1/session/:id`, `GET /v1/session/:id/stream`,
|
package/src/bin/agent-serve.ts
CHANGED
|
@@ -89,6 +89,10 @@ Usage:
|
|
|
89
89
|
${CLI} call <tool> [--input <json>] [--dir <path> | --url <baseUrl> | --prod]
|
|
90
90
|
[--session <id>] [--slug <slug>] [--team <id>]
|
|
91
91
|
${CLI} init [dir] [--json]
|
|
92
|
+
${CLI} mcp --prod [--slug <slug>] [--team <id>] [--dir <path>]
|
|
93
|
+
| --url <baseUrl> [--bearer-token <t>]
|
|
94
|
+
${CLI} mcp install [--name <serverName>] [--print] [--json]
|
|
95
|
+
(same target flags as mcp)
|
|
92
96
|
${CLI} eval [--dir <path>] [evalId...] [--list] [--json]
|
|
93
97
|
[--tag <tag>]... [--verbose] [--timeout-ms <n>]
|
|
94
98
|
[--no-stream] [--no-wait]
|
|
@@ -158,6 +162,15 @@ Commands:
|
|
|
158
162
|
run login when unsigned, then print next steps. --json is
|
|
159
163
|
machine-readable and never blocks on login. No directory:
|
|
160
164
|
print the setup guide.
|
|
165
|
+
mcp Serve this agent's MCP endpoint over stdio for MCP clients
|
|
166
|
+
that spawn local servers (e.g. Cursor). Proxies
|
|
167
|
+
newline-delimited JSON-RPC to <target>/v1/mcp. --prod resolves
|
|
168
|
+
the hosted deployment with the signed-in Cursor account and
|
|
169
|
+
re-mints short-lived engine credentials as they expire;
|
|
170
|
+
--url targets a running server. stdout is reserved for the
|
|
171
|
+
MCP wire. \`mcp install\` writes the matching entry into
|
|
172
|
+
~/.cursor/mcp.json (--print to preview; --name to override
|
|
173
|
+
the server name).
|
|
161
174
|
eval Run filesystem evals under evals/**/*.eval.ts (streams on a TTY).
|
|
162
175
|
Default: ephemeral local harness against evals on disk. Pass
|
|
163
176
|
--prod to kick off the batch on the team's hosted deployment
|
|
@@ -418,8 +431,9 @@ async function main(argv: string[]): Promise<number> {
|
|
|
418
431
|
const dir = values.dir ?? ".";
|
|
419
432
|
|
|
420
433
|
// Best-effort, cached, and silent on failure. `update` does its own fresh
|
|
421
|
-
// lookup,
|
|
422
|
-
|
|
434
|
+
// lookup, --json runs stay quiet for machine consumers, and `mcp` skips it
|
|
435
|
+
// so MCP client spawns stay fast (stdout there is the protocol wire).
|
|
436
|
+
if (command !== "update" && command !== "mcp" && values.json !== true) {
|
|
423
437
|
await warnIfOutdated();
|
|
424
438
|
}
|
|
425
439
|
|
|
@@ -486,6 +500,22 @@ async function main(argv: string[]): Promise<number> {
|
|
|
486
500
|
});
|
|
487
501
|
}
|
|
488
502
|
|
|
503
|
+
case "mcp": {
|
|
504
|
+
const { cmdMcp } = await import("../internal/cli-mcp.js");
|
|
505
|
+
return cmdMcp(positionals[0], {
|
|
506
|
+
dir,
|
|
507
|
+
slug: values.slug,
|
|
508
|
+
team: values.team,
|
|
509
|
+
apiKey: values["api-key"],
|
|
510
|
+
url: values.url,
|
|
511
|
+
prod: values.prod,
|
|
512
|
+
bearerToken: values["bearer-token"],
|
|
513
|
+
name: values.name,
|
|
514
|
+
print: values.print,
|
|
515
|
+
json: values.json,
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
|
|
489
519
|
case "login": {
|
|
490
520
|
const { cmdLogin } = await import("../internal/cli-cursor.js");
|
|
491
521
|
return cmdLogin({
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* - Top-level posts only by default; thread replies never dispatch unless
|
|
7
7
|
* `posts: "all"` is configured.
|
|
8
8
|
* - Posts that @mention the bot are skipped here (the `app_mention` path
|
|
9
|
-
* owns them)
|
|
9
|
+
* owns them).
|
|
10
|
+
* - Bot-authored posts are dropped unless `includeBotPosts` is configured
|
|
11
|
+
* (alert feeds / webhook integrations); the watching bot's own posts are
|
|
12
|
+
* always dropped either way (loop safety).
|
|
10
13
|
* - An optional debounce window lets rapid edits settle: edits update the
|
|
11
14
|
* pending text, deleting the post cancels the dispatch.
|
|
12
15
|
*/
|
|
@@ -114,11 +117,14 @@ export function createChannelPostWatcher(args: {
|
|
|
114
117
|
allowlist: SlackChannelAllowlist;
|
|
115
118
|
/** Bot user id (from `auth.test`) so bot mentions route via app_mention. */
|
|
116
119
|
getBotUserId: () => string | undefined;
|
|
120
|
+
/** This app's own `bot_id` (from `auth.test`) for self-post loop safety. */
|
|
121
|
+
getBotId?: () => string | undefined;
|
|
117
122
|
dispatch: (message: SlackMessage, eventId?: string) => void;
|
|
118
123
|
log?: SlackLog;
|
|
119
124
|
}): ChannelPostWatcher {
|
|
120
125
|
const log = args.log ?? defaultSlackLog;
|
|
121
126
|
const includeReplies = args.options.posts === "all";
|
|
127
|
+
const includeBotPosts = args.options.includeBotPosts === true;
|
|
122
128
|
const debounceMs = args.options.debounceMs ?? 0;
|
|
123
129
|
const pending = new Map<
|
|
124
130
|
string,
|
|
@@ -185,6 +191,27 @@ export function createChannelPostWatcher(args: {
|
|
|
185
191
|
return skip("thread reply (posts: top-level)", message);
|
|
186
192
|
}
|
|
187
193
|
const botUserId = args.getBotUserId();
|
|
194
|
+
const author = message.author;
|
|
195
|
+
if (author?.isBot === true) {
|
|
196
|
+
if (!includeBotPosts) {
|
|
197
|
+
return skip("bot-authored post (includeBotPosts off)", message);
|
|
198
|
+
}
|
|
199
|
+
const selfBotId = args.getBotId?.();
|
|
200
|
+
if (selfBotId === undefined && botUserId === undefined) {
|
|
201
|
+
return skip(
|
|
202
|
+
"bot-authored post (bot identity unknown; loop safety)",
|
|
203
|
+
message
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
if (
|
|
207
|
+
(selfBotId !== undefined && author.botId === selfBotId) ||
|
|
208
|
+
(botUserId !== undefined &&
|
|
209
|
+
author.userId !== "" &&
|
|
210
|
+
author.userId === botUserId)
|
|
211
|
+
) {
|
|
212
|
+
return skip("own post (loop safety)", message);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
188
215
|
if (botUserId !== undefined && message.text.includes(`<@${botUserId}>`)) {
|
|
189
216
|
return skip("bot mention (app_mention path owns it)", message);
|
|
190
217
|
}
|
|
@@ -114,6 +114,12 @@ export async function dispatchCursorSlackEnvelope(args: {
|
|
|
114
114
|
envelope: CursorSlackEnvelope;
|
|
115
115
|
send: SendMessageFn;
|
|
116
116
|
host: HostContext;
|
|
117
|
+
hasContinuationSession?: (continuationToken: string) => Promise<boolean>;
|
|
118
|
+
isContinuationBusy?: (continuationToken: string) => Promise<boolean>;
|
|
119
|
+
getContinuationLastBotMessageTs?: (
|
|
120
|
+
continuationToken: string
|
|
121
|
+
) => Promise<string | undefined>;
|
|
122
|
+
interruptContinuation?: (continuationToken: string) => Promise<boolean>;
|
|
117
123
|
}): Promise<void> {
|
|
118
124
|
const { runtime, envelope } = args;
|
|
119
125
|
const log = runtime.getLog();
|
|
@@ -161,6 +167,20 @@ export async function dispatchCursorSlackEnvelope(args: {
|
|
|
161
167
|
credentials: runtime.credentials,
|
|
162
168
|
host: args.host,
|
|
163
169
|
log,
|
|
170
|
+
...(args.hasContinuationSession === undefined
|
|
171
|
+
? {}
|
|
172
|
+
: { hasContinuationSession: args.hasContinuationSession }),
|
|
173
|
+
...(args.isContinuationBusy === undefined
|
|
174
|
+
? {}
|
|
175
|
+
: { isContinuationBusy: args.isContinuationBusy }),
|
|
176
|
+
...(args.getContinuationLastBotMessageTs === undefined
|
|
177
|
+
? {}
|
|
178
|
+
: {
|
|
179
|
+
getContinuationLastBotMessageTs: args.getContinuationLastBotMessageTs,
|
|
180
|
+
}),
|
|
181
|
+
...(args.interruptContinuation === undefined
|
|
182
|
+
? {}
|
|
183
|
+
: { interruptContinuation: args.interruptContinuation }),
|
|
164
184
|
...(envelope.eventId === undefined ? {} : { eventId: envelope.eventId }),
|
|
165
185
|
});
|
|
166
186
|
}
|