AI tools

tasksquad.ai

TaskSquad: 3 skills for AI agents

Notes an AI agent reads before it works on tasksquad.ai: what to open, what to call, what breaks. Written by browser-harness contributors after real runs, and kept on GitHub under the MIT license.

Give it to your agent

Any coding agent

Start the task with this line. The Markdown version is the same notes without the page around them.

Read https://browser-use.com/skills/tasksquad-ai.md, then <your task>

Browser Harness

Save the files into your workspace and turn domain skills on. The code below calls its helpers, such as http_get and js. What Browser Harness is.

d="${BH_AGENT_WORKSPACE:-$HOME/.config/browser-harness/agent-workspace}/domain-skills/tasksquad-ai"
mkdir -p "$d" && cd "$d"
curl -sLO https://raw.githubusercontent.com/browser-use/browser-harness/main/agent-workspace/domain-skills/tasksquad-ai/agents.md
curl -sLO https://raw.githubusercontent.com/browser-use/browser-harness/main/agent-workspace/domain-skills/tasksquad-ai/auth.md
curl -sLO https://raw.githubusercontent.com/browser-use/browser-harness/main/agent-workspace/domain-skills/tasksquad-ai/tasks.md
export BH_DOMAIN_SKILLS=1

TaskSquad - Agents

Field-tested against tasksquad.ai on 2026-05-03 using a logged-in Chrome session.

URL

https://tasksquad.ai/dashboard/agents       # Agents list for current project

Agent list

Agents are the daemon workers that execute tasks. Each agent card shows:

  • Name
  • Status badge (active / inactive / running / waiting_input / paused)
  • Role (optional label set by maintainer)
  • Last-seen timestamp

Navigate to agents via the sidebar link "Agents".

Creating an agent

Maintainer/owner only. Click "New agent" (or similar CTA in the agents view).

Provide:

  • Name - display name for the agent
  • Role (optional) - a freeform string describing the agent's specialty

After creation, a token is generated. Copy it immediately - it is shown only once. The daemon uses this token to authenticate.

tsq install --token <TOKEN>

Tokens are created via POST /teams/:teamId/tokens with { label, agent_id }.

Agent statuses

StatusMeaning
activeDaemon connected, ready to accept tasks
inactiveDaemon not connected (no recent ping)
runningCurrently executing a task
waiting_inputPaused mid-task, waiting for user reply
pausedManually paused - will not pick up new tasks

Agent actions (maintainer / owner)

ActionTriggerEffect
Pause/ResumeToggle button on cardSets paused flag - agent ignores new tasks
Reset"Reset" buttonClears the agent's running state (use if stuck)
DeleteDelete button / menuPermanently removes agent and its tokens
Edit roleRole edit controlUpdates the agent's role label

Token management

Tokens are per-agent credentials. Each token has a label. A maintainer can:

  • Generate a new token - use when setting up a new machine or rotating credentials
  • Tokens cannot be viewed after creation; only revoked implicitly by deleting the agent

Gotchas

  • Status is driven by daemon pings, not task state. An agent can be active while no task is running.
  • inactive ≠ paused. Inactive means the daemon process is offline. Paused means a maintainer explicitly suspended it via the UI.
  • Only owners and maintainers can create agents or generate tokens. Members see the list but cannot modify it.
  • Token is shown once. If you miss copying it, you must generate a new one - there is no "reveal token" button.
  • Reset is for recovery only. Use it when an agent is stuck in running after a daemon crash. Do not reset agents mid-task.

agents.md · updated May 4, 2026 · 436 words

TaskSquad - Authentication

Field-tested against tasksquad.ai on 2026-05-03 using a Chrome session.

URLs

https://tasksquad.ai/auth          # login page (redirects to /dashboard if already signed in)
https://tasksquad.ai/auth/cli      # CLI token auth page

Login page

The login page is a centered card with two OAuth buttons. No email/password form exists - SSO only.

goto_url("https://tasksquad.ai/auth")
wait_for_load()

Sign-in buttons

The card contains exactly two buttons:

Visible labelProvider
"Continue with Google"Google
"Continue with GitHub"GitHub

Both are <button type="button"> inside a <div class="px-6 pb-6 flex flex-col gap-3">. Locate by label text and click coordinates returned from the rendered element:

import json
 
def click_button_by_text(label):
    """Find a <button> by exact innerText and click its center."""
    sel = json.dumps(label)
    result = js(f"""
      var b = Array.from(document.querySelectorAll("button"))
                .find(e => (e.innerText || "").trim() === {sel});
      if (!b) return null;
      var r = b.getBoundingClientRect();
      return JSON.stringify({{x: Math.round(r.x + r.width/2), y: Math.round(r.y + r.height/2)}});
    """)
    if result is None:
        raise RuntimeError(f"button not found: {label!r} — has the React app finished hydrating?")
    pos = json.loads(result)
    click_at_xy(pos["x"], pos["y"])
 
# Google
click_button_by_text("Continue with Google")
 
# GitHub
click_button_by_text("Continue with GitHub")

After clicking, a browser popup opens for the OAuth flow. Stop and ask the user to complete sign-in - do not attempt to interact with the OAuth popup window.

Auth-wall pattern

Any route under /dashboard/* redirects to /auth when the user is not signed in. If you navigate to a dashboard URL and land on /auth, the session is unauthenticated - ask the user to sign in before proceeding.

Redirect after sign-in

Firebase Auth resolves the session client-side. After the OAuth popup closes, React's onAuthStateChanged fires and automatically navigates the user to /dashboard. Do not click anything - just wait_for_load() and verify with page_info().

Sign-out

There is no dedicated sign-out page. The sign-out action is triggered from the sidebar in /dashboard. Look for a LogOut icon button in the bottom-left sidebar area.

Gotchas

  • The page is a React SPA. Navigating to /auth while the app is still hydrating returns the bare <div id="root"> with no buttons yet. Always call wait_for_load() and verify the "Continue with Google" text is present before clicking.
  • No error routes exist for failed OAuth. On failure, the card renders an inline <p class="text-sm text-red-500 text-center"> error message below the buttons.
  • authed === null loading state. While Firebase resolves the session on /auth, the <Login> component is not rendered (the route renders null). Wait for buttons to appear before clicking.

auth.md · updated May 4, 2026 · 425 words

TaskSquad - Task Inbox

Field-tested against tasksquad.ai on 2026-05-03 using a logged-in Chrome session.

URL

https://tasksquad.ai/dashboard              # Inbox (default view)
https://tasksquad.ai/dashboard/<taskId>     # Task thread (individual task)

Inbox view

The inbox is the default view after sign-in. It shows a list of tasks assigned to agents in the currently selected project.

Task list

Tasks are rendered as rows. Each row shows:

  • Subject line
  • Agent name
  • Status badge
  • Relative timestamp

Status values

StatusMeaning
pendingQueued for the agent daemon
queuedAgent is busy - task is waiting in line
runningAgent is actively working
waiting_inputAgent needs a reply from the user
doneCompleted successfully
failedEnded with an error
scheduledWill start at a future scheduled_at time
wrapping_upRunning post-completion close steps

Filters

Two dropdowns appear above the task list:

Status (left): All / Pending / Queued / Running / Waiting / Done / Failed / Scheduled

Origin (right): All / System / Mine / From Note / Critique / Scheduled

  • "System" = tasks created by automated conveyors
  • "Mine" = tasks you composed yourself
  • "From Note" = tasks spawned from Notes
  • "Critique" = tasks that are note critiques

Both dropdowns use shadcn <Select>. Click the trigger to open, then click the item by text.

Refresh

A RefreshCw icon button sits next to the "Inbox" heading. Click it to reload the task list without full navigation. Alternatively, wait_for_load() after any state change - the app auto-polls when active tasks exist (interval depends on plan; see Gotchas).

Composing a new task

Click the "New message" button (top-right of Inbox). A dialog opens.

Dialog fields

FieldTypeNotes
Agent<Select>Required. Dropdown of agents in the current project.
Subject<input>Required. Short description of the task.
Message<textarea>Optional. Task body / detailed instructions.

Optional advanced toggles (revealed via UI controls):

  • Schedule: set a future delivery time
  • Auto-close: task closes automatically when the agent finishes
  • Save tokens: compress the task context (lite / full / ultra)
  • Close steps: newline-separated post-completion steps

Submit with the "Send" button inside the dialog.

import json
 
def _coords_by_text(label, tag="button"):
    """Find an element by exact innerText and return click coords, or raise."""
    sel = json.dumps(tag)
    txt = json.dumps(label)
    result = js(f"""
      var el = Array.from(document.querySelectorAll({sel}))
                .find(e => (e.innerText || "").trim() === {txt});
      if (!el) return null;
      var r = el.getBoundingClientRect();
      return JSON.stringify({{x: Math.round(r.x + r.width/2), y: Math.round(r.y + r.height/2)}});
    """)
    if result is None:
        raise RuntimeError(f"{tag!r} with text {label!r} not found")
    p = json.loads(result)
    return p["x"], p["y"]
 
def _coords_by_selector(selector):
    """Find an element by CSS selector and return click coords, or raise."""
    sel = json.dumps(selector)
    result = js(f"""
      var el = document.querySelector({sel});
      if (!el) return null;
      var r = el.getBoundingClientRect();
      return JSON.stringify({{x: Math.round(r.x + r.width/2), y: Math.round(r.y + r.height/2)}});
    """)
    if result is None:
        raise RuntimeError(f"selector not found: {selector!r}")
    p = json.loads(result)
    return p["x"], p["y"]
 
# Open the compose dialog
click_at_xy(*_coords_by_text("New message"))
wait(1.0)  # let the dialog mount; agents may still be loading
 
# Open the agent <Select> trigger and pick an agent.
# The trigger renders the placeholder "Select agent…" until an agent is chosen.
click_at_xy(*_coords_by_text("Select agent…"))
wait(0.5)
# Then click the agent name from the opened dropdown:
click_at_xy(*_coords_by_text("YOUR_AGENT_NAME"))
 
# Focus the subject input, then type
click_at_xy(*_coords_by_selector('input[id="subject"]'))
type_text("Check the build status")
 
# (Optional) focus the message textarea and type the body
# click_at_xy(*_coords_by_selector('textarea'))
# type_text("Detailed instructions go here.")
 
# Submit
click_at_xy(*_coords_by_text("Send"))

Task thread

Clicking a task row opens the thread at /dashboard/<taskId>.

The thread is an email-style conversation. Messages alternate between:

  • User messages (right-aligned or labeled with sender name)
  • Agent messages (left-aligned)

Replying

When a task is in waiting_input status, a reply box appears at the bottom. Type your reply and press Enter or click the send button.

A scheduled reply pending delivery blocks the reply box - a cancel option appears.

Task actions

Actions available from the thread view (usually via icon buttons or a menu):

  • Close - mark the task done manually
  • Delete - permanently remove the task
  • Forward - reassign to a different agent (appears as "Forward to agent" with agent selector and optional instructions)

Gotchas

  • Inbox auto-polls while active tasks exist (pending / running / waiting_input). Free plan polls every 5 s, Pro every 2 s. For scraping, add a wait_for_load() between reads if statuses are changing.
  • queued is a derived client-side status - the server returns pending, but the UI shows queued when the target agent is itself running or waiting_input. Filter accordingly.
  • Task list is per-project. Switching projects (teams) reloads the list. The active project is stored in localStorage as tsq_team_id.
  • Dialog does not open until agents are loaded. If "Select agent…" placeholder is missing after clicking "New message", the agents list is still fetching - retry after a short wait.
  • Free plan project limit: free accounts have a project limit (currently 1).

tasks.md · updated May 4, 2026 · 873 words

Sites change. These notes describe what worked when they were last updated, not a guarantee they still do. If something broke, the fix is a pull request to browser-harness.