Skip to content

Conversation

@Saurabhkmr98
Copy link
Member

@Saurabhkmr98 Saurabhkmr98 commented Dec 17, 2025

Description

  • Added docs for Building Agents in Plane

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Feature (non-breaking change which adds functionality)
  • Improvement (change that would cause existing functionality to not work as expected)
  • Code refactoring
  • Performance improvements
  • Documentation update

Screenshots and Media (if applicable)

Test Scenarios

References

Summary by CodeRabbit

  • Documentation
    • Added comprehensive agent documentation covering concepts, lifecycle, activity types, and workflows
    • Added step-by-step guides for building and integrating agents with OAuth onboarding, webhook handling, and SDK examples
    • Added best practices for acknowledgements, stop signals, progress reporting, error handling, and UX guidance
    • Added detailed reference for agent signals, content payloads, and ephemeral behavior
    • Integrated agent docs into site navigation for easy access

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Dec 17, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
developer-docs Error Error Jan 27, 2026 1:29pm

Request Review

@makeplane
Copy link

makeplane bot commented Dec 17, 2025

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

@coderabbitai
Copy link

coderabbitai bot commented Dec 17, 2025

📝 Walkthrough

Walkthrough

Adds a new suite of Plane Agents documentation (overview, building guide, best practices, signals/content payload), updates the Build Plane App OAuth onboarding content, and exposes the agent docs via a new "Agents" navigation group in mint.json.

Changes

Cohort / File(s) Summary
Agent Documentation
dev-tools/agents/overview.mdx, dev-tools/agents/building-an-agent.mdx, dev-tools/agents/best-practices.mdx, dev-tools/agents/signals-content-payload.mdx
Four new comprehensive MDX docs introducing Plane Agents: overview and lifecycle (with sequence diagram), step-by-step agent building (OAuth, webhooks, AgentRun/Activity lifecycle, SDK examples), best practices (acknowledgement, stop signals, progress, errors, UX), and signals/content payload reference (content vs signals, payload schemas, examples in TS/Python).
OAuth / App Onboarding
dev-tools/build-plane-app.mdx
Major overhaul of the onboarding guide to an OAuth-centric flow: expanded prerequisites, distinct User/Bot token flows, new OAuth helpers and code examples (Node/Python), webhook verification, token exchange/storage, deployment notes, and an end-to-end example.
Navigation Configuration
mint.json
Added a new nested navigation group "Agents" under the "Build and extend Plane" section, listing the four new agent documentation pages.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User
participant Plane as Plane (Server)
participant Agent as Agent (Webhook)
participant DB as Storage/SDK
User->>Plane: Trigger (mention / comment / API)
Plane->>Plane: Create AgentRun & AgentRunActivity (prompt)
Plane->>Agent: POST webhook (agent_run_create / agent_run_user_prompt)
Agent->>Agent: Acknowledge immediately (thought activity)
Agent->>DB: Retrieve context / credentials
Agent->>Plane: Create activities (thoughts, progress, actions)
Plane->>Plane: Persist activities, update AgentRun state
Agent->>Plane: Create response activity (final response)
Plane->>User: Deliver response via UI / webhook callbacks

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • Areas requiring attention:
    • dev-tools/agents/building-an-agent.mdx — Verify OAuth flows, webhook samples, SDK usage, and activity payload examples
    • dev-tools/agents/signals-content-payload.mdx — Validate signal semantics and JSON payload examples against API contracts
    • Cross-doc consistency — Terminology, status/state names, and example code coherence across all agent docs
    • dev-tools/build-plane-app.mdx — Confirm OAuth details, token exchange examples, and webhook verification snippets
    • mint.json — Ensure navigation structure and paths are correct

"I hopped through docs with a twitch of my nose,
New agent paths where curiosity grows.
Webhooks acknowledged, signals held tight,
Thoughts and actions dance into the light.
Bravo, dear devs — let the agents take flight! 🐇"

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding comprehensive documentation for Agents in Plane, which is reflected across all modified files (four new agent docs and updates to build-plane-app.mdx).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af15bab and dcdcd76.

📒 Files selected for processing (5)
  • dev-tools/agents/best-practices.mdx (1 hunks)
  • dev-tools/agents/building-an-agent.mdx (1 hunks)
  • dev-tools/agents/overview.mdx (1 hunks)
  • dev-tools/agents/signals-content-payload.mdx (1 hunks)
  • mint.json (1 hunks)
🔇 Additional comments (8)
dev-tools/agents/overview.mdx (1)

1-138: LGTM! Well-structured overview documentation.

The overview provides a comprehensive introduction to Plane Agents, covering key concepts like Agent Runs, Activity types, and the lifecycle flow. The Mermaid sequence diagram clearly illustrates the webhook interaction pattern, and the Agent Run states table is thorough.

dev-tools/agents/best-practices.mdx (1)

79-94: Good guidance on thought activity messaging.

The examples of good vs. bad thought messages are helpful for developers to understand the expected user experience. The guidance to avoid exposing technical implementation details is particularly valuable.

dev-tools/agents/building-an-agent.mdx (3)

350-425: Comprehensive webhook documentation.

The webhook payload structures and handling examples are thorough and will help developers understand the integration flow. Good inclusion of both agent_run_create and agent_run_user_prompt events with sample payloads.


173-176: No action needed. The package plane-sdk is published on PyPI and installable via pip install plane-sdk.


101-104: SDK package name is correct and available.

The npm package @makeplane/plane-node-sdk is published with latest version 0.1.4. The installation command in the documentation is accurate and ready for publication.

dev-tools/agents/signals-content-payload.mdx (2)

563-579: Helpful visual example of ephemeral vs permanent activities.

This visual representation clearly demonstrates how ephemeral activities (thoughts, actions) behave compared to permanent responses. This will help developers understand the user experience impact of different activity types.


1-7: Well-structured reference documentation.

The signals and content payload reference is comprehensive, with clear TypeScript interfaces, JSON examples, and SDK usage patterns for both TypeScript and Python. The section organization makes it easy to find specific information.

mint.json (1)

510-519: Navigation structure looks good.

The new Agents subgroup is correctly added under "Build and extend Plane" with a logical page ordering: Overview → Building an Agent → Best Practices → Signals & Content Payload. The paths match the new documentation files added in this PR.

Comment on lines 139 to 163
```python
async def handle_webhook(webhook: dict):
signal = webhook["agent_run_activity"]["signal"]
agent_run_id = webhook["agent_run"]["id"]

# ALWAYS check for stop signal first
if signal == "stop":
# Cancel any ongoing work
cancel_ongoing_tasks(agent_run_id)

# Acknowledge the stop
plane_client.agent_runs.activities.create(
workspace_slug=workspace_slug,
agent_run_id=agent_run_id,
type="response",
content={
"type": "response",
"body": "Understood. I've stopped processing your previous request.",
},
)

return # Exit early

# Continue with normal processing...
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Same async/await inconsistency as previous example.

The Python example at lines 140-163 has the same issue: async def handle_webhook with a synchronous plane_client.agent_runs.activities.create(...) call on line 150. Apply the same fix for consistency.

🤖 Prompt for AI Agents
In dev-tools/agents/best-practices.mdx around lines 139 to 163, the async
handler calls the synchronous plane_client.agent_runs.activities.create(...) on
line 150; change this to an awaited call so the async function properly awaits
the I/O (i.e., use await plane_client.agent_runs.activities.create(...)); if the
client method is not async, wrap the call in asyncio.to_thread(...) and await
that instead; keep the early return and error handling unchanged.

Comment on lines +339 to +348
### Ephemeral Activities

Activities with `ephemeral: true` are temporary and don't create comments. They're useful for showing agent progress without cluttering the conversation.

The following activity types are automatically marked as ephemeral:
- `thought`
- `action`
- `error`

Ephemeral activities are displayed temporarily in the UI and replaced when the next activity arrives.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Inconsistency: error activity type described as both ephemeral and creating a comment.

Lines 343-346 state that error is automatically marked as ephemeral. However, lines 315-324 state that error "Creates a comment and sets run to failed." These are contradictory—ephemeral activities don't create comments.

Please clarify the actual behavior. Based on the context (error needing to inform users), it seems error should create a comment and likely shouldn't be ephemeral.

🤖 Prompt for AI Agents
In dev-tools/agents/building-an-agent.mdx around lines 339 to 348, the docs
contradict earlier lines (315–324) by listing `error` as an ephemeral activity;
update this section to remove `error` from the ephemeral list and instead state
that `error` activities create a comment and set the run to `failed`, and ensure
the earlier description and the ephemeral-activity list are consistent about
which types are ephemeral (keep `thought` and `action` as ephemeral, remove
`error`).

Comment on lines +549 to +554
### Automatically Ephemeral Types

The following activity types are automatically marked as ephemeral:
- `thought`
- `action`
- `error`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Same inconsistency: error listed as ephemeral but also creates a comment.

Lines 551-554 list error as automatically ephemeral, but lines 454-456 state that error "Creates a comment and sets the Agent Run status to failed." This is the same inconsistency noted in building-an-agent.mdx.

Please resolve this across all documentation files to clarify the actual behavior of error activities.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@dev-tools/agents/building-an-agent.mdx`:
- Around line 531-544: The example webhook handler is calling os.getenv() but
never imports the os module; add an import for os at the top of the snippet so
the handle_webhook function and PlaneClient initialization (which uses
os.getenv) can resolve os. Locate the snippet containing handle_webhook and
PlaneClient and insert "import os" with the other imports.
- Around line 200-208: The example uses os.getenv() but misses importing the os
module; add an "import os" statement near the top of the snippet so the
PlaneClient initialization (PlaneClient and os.getenv(...) call) can run without
NameError.

In `@dev-tools/build-plane-app.mdx`:
- Around line 678-701: The verifyWebhookSignature/timingSafeEqual usage can
throw when the provided signature is missing or lengths differ; update
verifyWebhookSignature (and each webhook route like the app.post('/webhook')
handler) to first check that signature is present and that
Buffer.byteLength(signature) === Buffer.byteLength(expectedSignature) (or
compare lengths of Buffer.from(...)) before calling crypto.timingSafeEqual, and
if the checks fail return false so the route returns 403; ensure you normalize
missing header to an empty string only for guards and do this same guard pattern
wherever timingSafeEqual is used (e.g., other webhook handlers at the other
locations mentioned).
- Around line 1099-1112: Add an explicit input validation for
app_installation_id in the OAuth callback handlers: in the JavaScript route
app.get('/oauth/callback') and the Python OAuth callback function, check that
req.query.app_installation_id (JS) / the corresponding request param (Python) is
present and non-empty before calling the Plane API; if missing, return an early
400 response with a clear error message (e.g., "Missing app_installation_id") to
avoid opaque downstream errors and stop the token exchange logic from executing.

Comment on lines +200 to +208
```python
from plane import PlaneClient
from plane.models.agent_runs import CreateAgentRunActivity

# Initialize the client with your bot token
plane_client = PlaneClient(
base_url=os.getenv("PLANE_API_URL", "https://api.plane.so"),
access_token=bot_token,
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing import os in Python example.

The code uses os.getenv() on line 206 but doesn't include the os import, which will cause a NameError at runtime.

Proposed fix
+import os
 from plane import PlaneClient
 from plane.models.agent_runs import CreateAgentRunActivity

 # Initialize the client with your bot token
 plane_client = PlaneClient(
     base_url=os.getenv("PLANE_API_URL", "https://api.plane.so"),
     access_token=bot_token,
 )
🤖 Prompt for AI Agents
In `@dev-tools/agents/building-an-agent.mdx` around lines 200 - 208, The example
uses os.getenv() but misses importing the os module; add an "import os"
statement near the top of the snippet so the PlaneClient initialization
(PlaneClient and os.getenv(...) call) can run without NameError.

Comment on lines +531 to +544
```python
from plane import PlaneClient
from plane.models.agent_runs import CreateAgentRunActivity

def handle_webhook(webhook: dict, credentials: dict):
"""Handle incoming agent webhook."""
# Only handle agent_run_activity webhooks
if webhook.get("type") != "agent_run_activity":
return

plane_client = PlaneClient(
base_url=os.getenv("PLANE_API_URL", "https://api.plane.so"),
access_token=credentials["bot_token"],
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing import os in Python webhook handler example.

Similar to the earlier Python example, this code uses os.getenv() on line 542 without importing os.

Proposed fix
+import os
 from plane import PlaneClient
 from plane.models.agent_runs import CreateAgentRunActivity

 def handle_webhook(webhook: dict, credentials: dict):
🤖 Prompt for AI Agents
In `@dev-tools/agents/building-an-agent.mdx` around lines 531 - 544, The example
webhook handler is calling os.getenv() but never imports the os module; add an
import for os at the top of the snippet so the handle_webhook function and
PlaneClient initialization (which uses os.getenv) can resolve os. Locate the
snippet containing handle_webhook and PlaneClient and insert "import os" with
the other imports.

Comment on lines +678 to +701
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');

return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-plane-signature'] as string;
const payload = req.body.toString();

if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET)) {
return res.status(403).send('Invalid signature');
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

git ls-files | grep -E "build-plane-app.mdx|build-plane"

Repository: makeplane/developer-docs

Length of output: 97


🏁 Script executed:

wc -l dev-tools/build-plane-app.mdx

Repository: makeplane/developer-docs

Length of output: 102


🏁 Script executed:

cat -n dev-tools/build-plane-app.mdx | sed -n '670,710p'

Repository: makeplane/developer-docs

Length of output: 1365


🏁 Script executed:

cat -n dev-tools/build-plane-app.mdx | sed -n '710,740p'

Repository: makeplane/developer-docs

Length of output: 1000


🏁 Script executed:

cat -n dev-tools/build-plane-app.mdx | sed -n '1135,1160p'

Repository: makeplane/developer-docs

Length of output: 1138


🏁 Script executed:

cat -n dev-tools/build-plane-app.mdx | sed -n '1245,1270p'

Repository: makeplane/developer-docs

Length of output: 971


Add guards for missing/invalid webhook signatures before comparison.

timingSafeEqual throws when buffers differ in length; missing headers cause uncaught exceptions returning 500 instead of 403. Add explicit header presence and length checks so all failures return 403 consistently. The complete Python example at lines 1252–1265 has a partial guard (signature or ""); apply similar safeguards to all locations.

🛡️ Suggested guards
 function verifyWebhookSignature(
   payload: string,
   signature: string,
   secret: string
 ): boolean {
   const expectedSignature = crypto
     .createHmac('sha256', secret)
     .update(payload)
     .digest('hex');
 
-  return crypto.timingSafeEqual(
-    Buffer.from(signature),
-    Buffer.from(expectedSignature)
-  );
+  if (!signature) return false;
+  const sigBuf = Buffer.from(signature);
+  const expBuf = Buffer.from(expectedSignature);
+  if (sigBuf.length !== expBuf.length) return false;
+  return crypto.timingSafeEqual(sigBuf, expBuf);
 }
 
 app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
-  const signature = req.headers['x-plane-signature'] as string;
+  const signature = req.headers['x-plane-signature'];
+  if (typeof signature !== 'string' || !signature) {
+    return res.status(403).send('Invalid signature');
+  }
   const payload = req.body.toString();
 def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
+    if not signature:
+        return False
     expected_signature = hmac.new(
         secret.encode('utf-8'),
         msg=payload,
         digestmod=hashlib.sha256
     ).hexdigest()
 
     return hmac.compare_digest(expected_signature, signature)
 
 `@app.route`("/webhook", methods=["POST"])
 def handle_webhook():
     signature = request.headers.get("X-Plane-Signature")
+    if not signature:
+        return "Invalid signature", 403

Also applies to: 716-731, 1142-1150, 1252-1265

🤖 Prompt for AI Agents
In `@dev-tools/build-plane-app.mdx` around lines 678 - 701, The
verifyWebhookSignature/timingSafeEqual usage can throw when the provided
signature is missing or lengths differ; update verifyWebhookSignature (and each
webhook route like the app.post('/webhook') handler) to first check that
signature is present and that Buffer.byteLength(signature) ===
Buffer.byteLength(expectedSignature) (or compare lengths of Buffer.from(...))
before calling crypto.timingSafeEqual, and if the checks fail return false so
the route returns 403; ensure you normalize missing header to an empty string
only for guards and do this same guard pattern wherever timingSafeEqual is used
(e.g., other webhook handlers at the other locations mentioned).

Comment on lines +1099 to +1112
// OAuth callback - exchanges app_installation_id for bot token
app.get('/oauth/callback', async (req, res) => {
const appInstallationId = req.query.app_installation_id as string;

try {
const basicAuth = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64');

// Get bot token
const tokenRes = await axios.post(
`${PLANE_API_URL}/auth/o/token/`,
new URLSearchParams({
grant_type: 'client_credentials',
app_installation_id: appInstallationId,
}).toString(),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and examine the structure
git ls-files | grep -i "build-plane-app.mdx"

Repository: makeplane/developer-docs

Length of output: 97


🏁 Script executed:

# Read the relevant sections to verify the concern
# Check lines around 1099-1112 first
sed -n '1090,1130p' dev-tools/build-plane-app.mdx

Repository: makeplane/developer-docs

Length of output: 1379


🏁 Script executed:

# Check the second location: lines 1205-1224
sed -n '1200,1230p' dev-tools/build-plane-app.mdx

Repository: makeplane/developer-docs

Length of output: 1105


🏁 Script executed:

# Search for earlier examples that might have the guard mentioned
# Look for the pattern earlier in the file
rg "if.*app_installation_id|if not app_installation_id" dev-tools/build-plane-app.mdx -B 2 -A 2

Repository: makeplane/developer-docs

Length of output: 217


🏁 Script executed:

# Find the exact line numbers where the guard appears
rg -n "if.*app_installation_id|if not app_installation_id" dev-tools/build-plane-app.mdx

Repository: makeplane/developer-docs

Length of output: 103


🏁 Script executed:

# Get broader context around the Python OAuth callback to see if it has the guard
sed -n '1205,1235p' dev-tools/build-plane-app.mdx | cat -n

Repository: makeplane/developer-docs

Length of output: 1418


🏁 Script executed:

# Check around line 1100 for JavaScript to confirm no guard
sed -n '1099,1120p' dev-tools/build-plane-app.mdx | cat -n

Repository: makeplane/developer-docs

Length of output: 905


Add input validation for app_installation_id in both OAuth callback examples.

Both the JavaScript (lines 1099–1112) and Python (lines 1205–1224) callback examples proceed without validating app_installation_id, causing opaque API failures if the parameter is missing. Add the guard that already appears earlier in the documentation.

✅ Suggested guard
 app.get('/oauth/callback', async (req, res) => {
   const appInstallationId = req.query.app_installation_id as string;
+  if (!appInstallationId) {
+    return res.status(400).send('Missing app_installation_id');
+  }
 
   try {
 def oauth_callback():
     app_installation_id = request.args.get("app_installation_id")
+    if not app_installation_id:
+        return "Missing app_installation_id", 400
 
     try:
🤖 Prompt for AI Agents
In `@dev-tools/build-plane-app.mdx` around lines 1099 - 1112, Add an explicit
input validation for app_installation_id in the OAuth callback handlers: in the
JavaScript route app.get('/oauth/callback') and the Python OAuth callback
function, check that req.query.app_installation_id (JS) / the corresponding
request param (Python) is present and non-empty before calling the Plane API; if
missing, return an early 400 response with a clear error message (e.g., "Missing
app_installation_id") to avoid opaque downstream errors and stop the token
exchange logic from executing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants