Skip to content

feat: add Asana bot integration#1

Open
Will Smith (wesmith4) wants to merge 1 commit into
mainfrom
feat/asana-bot
Open

feat: add Asana bot integration#1
Will Smith (wesmith4) wants to merge 1 commit into
mainfrom
feat/asana-bot

Conversation

@wesmith4

Copy link
Copy Markdown
Collaborator

Summary

Adds a new @open-inspect/asana-bot package — a Cloudflare Worker that turns Asana task activity into Open-Inspect coding sessions, mirroring the linear-bot architecture. Assigning a task to a dedicated Asana bot user creates a session; comments on active tasks forward as follow-up prompts; unassigning or completing the task stops the session. The bot reports back via task comments (working status, session link, PR link on completion).

How it works

  • Identity: a dedicated Asana bot user + PAT (no OAuth needed for single-tenant). Bot user GID and workspace GID are worker config.
  • Webhooks: created programmatically via POST /admin/webhooks/setup (HMAC-authed), which registers a webhook on the bot's user task list — one webhook covers assignments across the whole workspace. Per-task story webhooks are registered when a session starts (follow-up comments) and cleaned up on stop. Optional per-project webhooks enable @mention triggers.
  • Handshake/signature: echoes X-Hook-Secret only for hook IDs we minted (unique UUID target paths), stores per-webhook secrets in KV, verifies X-Hook-Signature (HMAC-SHA256) with timing-safe comparison, dedupes batched events.
  • Repo resolution cascade: project→repo KV mapping → repo custom field (owner/name) → LLM classifier fallback.
  • Prompt safety: all task/comment text is wrapped in <user_content> untrusted-content blocks, same as linear-bot.
  • Sessions: created via the control-plane service binding with spawnSource: "asana-bot"; completion callbacks route through the new ASANA_BOT binding and post a task comment with the PR link. Tool-call callbacks are acknowledged but not surfaced (Asana has no ephemeral activity primitive — each would be a real comment).

Cross-package changes

  • shared: AsanaCallbackContext, "asana-bot" spawn source, "asana" message source, AsanaBotSettings + integration definition. Also fixes a pre-existing bug where the plain SpawnSource TS union was missing entries its zod schema had.
  • control-plane: ASANA_BOT service binding, callback routing (case "asana"), identity resolution (asana: author IDs), analytics spawn sources, integration-settings validation.
  • web: Settings → Integrations → Asana (model, reasoning effort, user-preference override, task session instructions).
  • terraform: workers-asana.tf, asana_kv namespace, enable_asana_bot + asana_pat/asana_bot_user_gid/asana_workspace_gid variables with validation, control-plane service binding, outputs.
  • CI: asana-bot test step.

Deployment

  1. Create the bot Asana account, generate a PAT, get its user GID and the workspace GID (GET /users/me).
  2. Set enable_asana_bot = true, asana_pat, asana_bot_user_gid, asana_workspace_gid in terraform.tfvars; apply.
  3. curl -X POST https://<asana-bot-worker>/admin/webhooks/setup with an internal HMAC bearer token.

Docs: packages/asana-bot/README.md and docs/integrations/ASANA.md.

Testing

  • 106 new asana-bot tests (handshake, signature rejection, dedup, assignment→session end-to-end, follow-up routing, stop semantics, self-comment guard, prompt-injection escaping, callbacks, KV store, client).
  • Full TS suite green: 2,893 tests across all 7 packages; typecheck and lint clean repo-wide.

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: Will Smith (@wesmith4), Action: pull_request

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3474e65d72

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

Comment on lines +39 to +41
response = await env.CONTROL_PLANE.fetch(
`https://internal/integration-settings/asana/resolved/${owner}/${name}`,
{ headers }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Return Asana resolved settings from control-plane

In the checked control-plane route, handleGetResolvedConfig only handles github, linear, code-server, and sandbox before returning Unsupported integration, so this new Asana fetch always gets a non-OK response and falls back to DEFAULT_CONFIG. In any deployment using the Asana settings page, the bot therefore ignores enabledRepos, configured model/effort, user-override policy, and task instructions, which can let tasks run against repos that admins intended to disable.

Useful? React with 👍 / 👎.

model,
createdAt: Date.now(),
};
await storeTaskSession(env, taskGid, taskSession);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Clear task session state after prompt enqueue failure

When session creation succeeds but the subsequent /prompt call returns non-2xx, this task mapping has already been persisted (and a story webhook may have been created) before the function returns on the error path. For transient control-plane or DO failures, reassigning or mentioning the same Asana task later will hit the existing-session check and skip creating a usable session, while follow-up comments get sent to a session that never received the initial task prompt; either store this only after the prompt succeeds or delete the session/webhook state on failure.

Useful? React with 👍 / 👎.

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.

1 participant