Skip to content

Commit 8d6179a

Browse files
add detailed step-by-step docs for oauth app and agent flows
1 parent dcdcd76 commit 8d6179a

4 files changed

Lines changed: 1555 additions & 781 deletions

File tree

dev-tools/agents/best-practices.mdx

Lines changed: 87 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,26 @@ Send a `thought` activity within the first few seconds of receiving a webhook:
2929
<Tab title="TypeScript">
3030

3131
```typescript
32-
async function handleWebhook(webhook: AgentRunActivityWebhook) {
32+
import { PlaneClient } from '@makeplane/plane-node-sdk';
33+
34+
async function handleWebhook(webhook: AgentRunActivityWebhook, credentials: { bot_token: string; workspace_slug: string }) {
35+
const planeClient = new PlaneClient({
36+
baseUrl: process.env.PLANE_API_URL || 'https://api.plane.so',
37+
accessToken: credentials.bot_token,
38+
});
39+
3340
const agentRunId = webhook.agent_run.id;
34-
41+
3542
// IMMEDIATELY acknowledge receipt
36-
await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, {
37-
type: "thought",
38-
content: {
39-
type: "thought",
40-
body: "Received your request. Analyzing...",
41-
},
43+
await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, {
44+
type: 'thought',
45+
content: { type: 'thought', body: 'Received your request. Analyzing...' },
4246
});
4347

4448
// Now proceed with actual processing
4549
// This can take longer since user knows agent is working
4650
const result = await processRequest(webhook);
47-
51+
4852
// ... rest of the logic
4953
}
5054
```
@@ -53,23 +57,30 @@ async function handleWebhook(webhook: AgentRunActivityWebhook) {
5357
<Tab title="Python">
5458

5559
```python
56-
async def handle_webhook(webhook: dict):
60+
from plane import PlaneClient
61+
from plane.models.agent_runs import CreateAgentRunActivity
62+
63+
def handle_webhook(webhook: dict, credentials: dict):
64+
plane_client = PlaneClient(
65+
base_url=os.getenv("PLANE_API_URL", "https://api.plane.so"),
66+
access_token=credentials["bot_token"],
67+
)
68+
5769
agent_run_id = webhook["agent_run"]["id"]
58-
70+
5971
# IMMEDIATELY acknowledge receipt
6072
plane_client.agent_runs.activities.create(
61-
workspace_slug=workspace_slug,
62-
agent_run_id=agent_run_id,
63-
type="thought",
64-
content={
65-
"type": "thought",
66-
"body": "Received your request. Analyzing...",
67-
},
73+
workspace_slug=credentials["workspace_slug"],
74+
run_id=agent_run_id,
75+
data=CreateAgentRunActivity(
76+
type="thought",
77+
content={"type": "thought", "body": "Received your request. Analyzing..."},
78+
),
6879
)
6980

7081
# Now proceed with actual processing
71-
result = await process_request(webhook)
72-
82+
result = process_request(webhook)
83+
7384
# ... rest of the logic
7485
```
7586

@@ -108,24 +119,29 @@ When a user wants to stop an agent run, Plane sends a `stop` signal with the act
108119
<Tab title="TypeScript">
109120

110121
```typescript
111-
async function handleWebhook(webhook: AgentRunActivityWebhook) {
122+
async function handleWebhook(webhook: AgentRunActivityWebhook, credentials: { bot_token: string; workspace_slug: string }) {
123+
const planeClient = new PlaneClient({
124+
baseUrl: process.env.PLANE_API_URL || 'https://api.plane.so',
125+
accessToken: credentials.bot_token,
126+
});
127+
112128
const signal = webhook.agent_run_activity.signal;
113129
const agentRunId = webhook.agent_run.id;
114130

115131
// ALWAYS check for stop signal first
116-
if (signal === "stop") {
132+
if (signal === 'stop') {
117133
// Cancel any ongoing work
118134
cancelOngoingTasks(agentRunId);
119-
135+
120136
// Acknowledge the stop
121-
await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, {
122-
type: "response",
137+
await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, {
138+
type: 'response',
123139
content: {
124-
type: "response",
140+
type: 'response',
125141
body: "Understood. I've stopped processing your previous request.",
126142
},
127143
});
128-
144+
129145
return; // Exit early
130146
}
131147

@@ -137,26 +153,33 @@ async function handleWebhook(webhook: AgentRunActivityWebhook) {
137153
<Tab title="Python">
138154

139155
```python
140-
async def handle_webhook(webhook: dict):
156+
def handle_webhook(webhook: dict, credentials: dict):
157+
plane_client = PlaneClient(
158+
base_url=os.getenv("PLANE_API_URL", "https://api.plane.so"),
159+
access_token=credentials["bot_token"],
160+
)
161+
141162
signal = webhook["agent_run_activity"]["signal"]
142163
agent_run_id = webhook["agent_run"]["id"]
143164

144165
# ALWAYS check for stop signal first
145166
if signal == "stop":
146167
# Cancel any ongoing work
147168
cancel_ongoing_tasks(agent_run_id)
148-
169+
149170
# Acknowledge the stop
150171
plane_client.agent_runs.activities.create(
151-
workspace_slug=workspace_slug,
152-
agent_run_id=agent_run_id,
153-
type="response",
154-
content={
155-
"type": "response",
156-
"body": "Understood. I've stopped processing your previous request.",
157-
},
172+
workspace_slug=credentials["workspace_slug"],
173+
run_id=agent_run_id,
174+
data=CreateAgentRunActivity(
175+
type="response",
176+
content={
177+
"type": "response",
178+
"body": "Understood. I've stopped processing your previous request.",
179+
},
180+
),
158181
)
159-
182+
160183
return # Exit early
161184

162185
# Continue with normal processing...
@@ -215,23 +238,34 @@ Graceful error handling is crucial for a good user experience.
215238
### Always Catch and Report Errors
216239

217240
```typescript
218-
async function handleWebhook(webhook: AgentRunActivityWebhook) {
241+
async function handleWebhook(webhook: AgentRunActivityWebhook, credentials: { bot_token: string; workspace_slug: string }) {
242+
const planeClient = new PlaneClient({
243+
baseUrl: process.env.PLANE_API_URL || 'https://api.plane.so',
244+
accessToken: credentials.bot_token,
245+
});
246+
219247
const agentRunId = webhook.agent_run.id;
220248

221249
try {
222-
await createThought("Processing your request...");
223-
250+
await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, {
251+
type: 'thought',
252+
content: { type: 'thought', body: 'Processing your request...' },
253+
});
254+
224255
// Your logic here...
225256
const result = await processRequest(webhook);
226-
227-
await createResponse(result);
228-
257+
258+
await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, {
259+
type: 'response',
260+
content: { type: 'response', body: result },
261+
});
262+
229263
} catch (error) {
230264
// ALWAYS inform the user about errors
231-
await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, {
232-
type: "error",
265+
await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, {
266+
type: 'error',
233267
content: {
234-
type: "error",
268+
type: 'error',
235269
body: getUserFriendlyErrorMessage(error),
236270
},
237271
});
@@ -240,14 +274,14 @@ async function handleWebhook(webhook: AgentRunActivityWebhook) {
240274

241275
function getUserFriendlyErrorMessage(error: Error): string {
242276
// Map technical errors to user-friendly messages
243-
if (error.message.includes("rate limit")) {
277+
if (error.message.includes('rate limit')) {
244278
return "I'm receiving too many requests right now. Please try again in a few minutes.";
245279
}
246-
if (error.message.includes("timeout")) {
247-
return "The operation took too long. Please try a simpler request or try again later.";
280+
if (error.message.includes('timeout')) {
281+
return 'The operation took too long. Please try a simpler request or try again later.';
248282
}
249283
// Generic fallback
250-
return "I encountered an unexpected error. Please try again or contact support if the issue persists.";
284+
return 'I encountered an unexpected error. Please try again or contact support if the issue persists.';
251285
}
252286
```
253287

@@ -272,15 +306,15 @@ For multi-turn conversations, maintain context from previous activities.
272306
```typescript
273307
// Get all activities for context
274308
const activities = await planeClient.agentRuns.activities.list(
275-
workspaceSlug,
309+
credentials.workspace_slug,
276310
agentRunId
277311
);
278312

279313
// Build conversation history
280314
const history = activities.results
281-
.filter(a => a.type === "prompt" || a.type === "response")
315+
.filter(a => a.type === 'prompt' || a.type === 'response')
282316
.map(a => ({
283-
role: a.type === "prompt" ? "user" : "assistant",
317+
role: a.type === 'prompt' ? 'user' : 'assistant',
284318
content: a.content.body,
285319
}));
286320

0 commit comments

Comments
 (0)