-
Notifications
You must be signed in to change notification settings - Fork 7
[SILO-788] feat: add docs for Agents in Plane #176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: preview
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Linked to Plane Work Item(s) This comment was auto-generated by Plane |
📝 WalkthroughWalkthroughAdds 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
Sequence Diagram(s)mermaid Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ 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. Comment |
There was a problem hiding this 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
📒 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_createandagent_run_user_promptevents with sample payloads.
173-176: No action needed. The packageplane-sdkis published on PyPI and installable viapip install plane-sdk.
101-104: SDK package name is correct and available.The npm package
@makeplane/plane-node-sdkis 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.
| ```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... | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| ### 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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`).
| ### Automatically Ephemeral Types | ||
|
|
||
| The following activity types are automatically marked as ephemeral: | ||
| - `thought` | ||
| - `action` | ||
| - `error` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this 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.
| ```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, | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| ```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"], | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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'); | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.mdxRepository: 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", 403Also 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).
| // 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(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.mdxRepository: 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.mdxRepository: 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 2Repository: 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.mdxRepository: 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 -nRepository: 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 -nRepository: 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.
Description
Type of Change
Screenshots and Media (if applicable)
Test Scenarios
References
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.