Skip to content

feat: add Taskmarket tool - #3016

Open
nicolasesanchez50 wants to merge 5 commits into
Chainlit:mainfrom
nicolasesanchez50:feat/taskmarket-tool
Open

feat: add Taskmarket tool#3016
nicolasesanchez50 wants to merge 5 commits into
Chainlit:mainfrom
nicolasesanchez50:feat/taskmarket-tool

Conversation

@nicolasesanchez50

@nicolasesanchez50 nicolasesanchez50 commented Aug 22, 2026

Copy link
Copy Markdown

Summary

Adds TaskmarketTool, a chat-native Taskmarket requester tool for Chainlit apps. Taskmarket is an onchain agent labor marketplace on Base; this lets a user run the full requester flow from a Chainlit conversation.

What it does

  • list_tasks — discover open tasks via public REST (no wallet)
  • get_task — live status: reward USDC, phase, expiry, submission window
  • list_submissions — read-only review view (never accepts/rejects)
  • create_task — funded requester flow:
    • requires a fresh, exact authorization string (e.g. authorize 5 USDC for taskmarket task)
    • enforces reward > 0 and reward <= max_spend (default 5 USDC, TASKMARKET_MAX_SPEND to override)
    • delegates the actual USDC transfer to the official taskmarket CLI (wallet keys, X402 payment, legal acceptance, idempotency all handled by first-party tooling — no private keys, seeds, or tokens touched here)
    • returns the created task id + live status; never blindly retries an unknown-settlement payment

Every method surfaces as Step(type="tool") so tool activity is visible in the chat UI. Exported as cl.TaskmarketTool via the lazy __getattr__ pattern used by the other integrations.

Checklist

  • Sample app: backend/chainlit/sample/taskmarket.py
  • Tests: 8 passing (backend/tests/test_taskmarket.py) — contract + live read path + all refunds
  • ruff check + ruff format --check clean
  • mypy clean
  • Commit GPG-signed (verified: true, reason valid)
  • Taskmarket has no existing code in this repo (GitHub code search: 0)
  • Target not previously submitted to Taskmarket integration bounties (checked all open/sibling bounties)

Summary by cubic

Adds TaskmarketTool to chainlit to run the Taskmarket requester flow from chat via the official taskmarket CLI. Creation requires a server-generated, single-use authorization token bound to the conversation thread and exact reward; CLI timeouts are killed and return explicit unknown-settlement results.

  • Methods: request_authorization (server-generated token; TTL > 0; per-thread cap 10; global cap 100), list_tasks/get_task/list_submissions (async public REST, read-only), create_task (requires token; reward > 0 and <= TASKMARKET_MAX_SPEND [default 5 USDC]; duration > 0; delegates funded transfer to taskmarket CLI; never handles private keys). All surface as Step(type="tool").
  • Behavior: tokens are scoped to the conversation thread id (works across HTTP requests); cross-thread tokens are refused; registry pruned on use/expiry. Timeouts/unrecognized output/status failures return explicit {"created":"unknown"} and never blindly retry; timed-out subprocesses are terminated.
  • Packaging: exported as cl.TaskmarketTool via lazy import; sample at backend/chainlit/sample/taskmarket.py; 13 offline tests at backend/tests/test_taskmarket.py.

Written for commit 491db25. Summary will update on new commits.

Review in cubic

Add TaskmarketTool, a chat-native Taskmarket requester tool for
Chainlit apps: discover open tasks, track live status, review
submissions (read-only), and create funded tasks through the official
Taskmarket CLI with a hard spend cap and a mandatory fresh
authorization string.

- list_tasks/get_task/list_submissions: public REST reads, no wallet
- create_task: safety checks (authorization, max_spend, duration) then
  delegates the funded transfer to the official taskmarket CLI
- never stores private keys, seeds, or tokens; never blindly retries
- every method surfaces as Step(type='tool') for chat visibility
- sample app + 8 tests covering the contract and refunds

Co-Authored-By: Hermes Agent <nicolasesanchez50@gmail.com>
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. backend Pertains to the Python backend. enhancement New feature or request unit-tests Has unit tests. labels Aug 22, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 4 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread backend/chainlit/taskmarket.py Outdated
Comment thread backend/chainlit/taskmarket.py Outdated
Comment thread backend/chainlit/taskmarket.py Outdated
Comment thread backend/chainlit/taskmarket.py
Comment thread backend/tests/test_taskmarket.py Outdated
Comment thread backend/chainlit/sample/taskmarket.py Outdated
Comment thread backend/chainlit/taskmarket.py
Comment thread backend/chainlit/taskmarket.py Outdated
…lement

Address all 8 findings from cubic-dev-ai review on PR Chainlit#3016.

Safety:
- authorization gate is now a server-generated, time-limited, single-use
  token (request_authorization), bound to the exact reward — a caller can
  no longer construct the gate from the reward parameter
- CLI timeout / unrecognized output / failed status lookup now return
  explicit unknown-settlement results with the task id whenever known,
  never raise, never blind-retry a payment

Async:
- HTTP reads via async httpx client (no event-loop block up to 45s)
- CLI write via asyncio subprocess with 120s wait_for (no block up to 120s)

Quality:
- tests now mock _get_json with fixtures (no live network; 13 tests,
  all offline and deterministic)
- sample app guards refusal strings against JSON parse

Co-Authored-By: Hermes Agent <nicolasesanchez50@gmail.com>
@nicolasesanchez50

Copy link
Copy Markdown
Author

Addressed all 8 findings in commit 1b77e4b68e (GPG-verified, PR head updated; 13 tests pass, ruff/format/mypy clean).

P1 — authorization gate (nonce): now request_authorization() issues a server-generated, random, time-limited (default 300s), single-use token bound to the exact reward. create_task refuses unknown/expired/used/reward-mismatched tokens; the token is burned before the CLI runs. A caller can no longer construct the gate from the reward parameter.

P1 — event-loop blocking: HTTP reads now run through an async httpx.AsyncClient (Chainlit already depends on httpx) instead of sync urllib; the CLI write runs through asyncio.create_subprocess_exec + wait_for(..., 120) instead of sync subprocess.run. No 45s/120s event-loop stalls.

P1 — unknown settlement: CLI timeout, exit-0-with-unrecognized-output, and status-lookup failure all return explicit created: "unknown" / statusUnavailable results with the task id whenever known — never raise, never blind-retry a payment.

P2 — tests: live-internet tests replaced with mocked _get_json fixtures (deterministic, offline); suite now covers the token lifecycle, timeout, unrecognized-output, and status-lookup-failure paths.

P2 — sample: refusal strings are guarded with json.JSONDecodeError before parsing.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 3 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread backend/chainlit/taskmarket.py
Comment thread backend/tests/test_taskmarket.py Outdated
Comment thread backend/chainlit/taskmarket.py Outdated
Comment thread backend/chainlit/taskmarket.py
Comment thread backend/chainlit/taskmarket.py Outdated
…egistry

- P1: terminate and reap the subprocess on CLI timeout instead of leaking
  a hung process after the create command was issued.
- P2: bind authorization tokens to the issuing chat session and reject
  cross-session use.
- P2: reject nonpositive TTLs instead of silently falling back to the
  default 300s window.
- P2: prune expired/used tokens and cap the authorization registry.
- tests: enter the Chainlit context fixture (repo pattern), patch
  shutil.which so the settlement path runs on machines/CI without the
  real CLI binary, and cover the timeout-kill, session-binding and
  nonpositive-TTL paths.

Co-Authored-By: Hermes Agent jobs <hermes@nicosanchez.com.ar>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread backend/chainlit/taskmarket.py
Comment thread backend/chainlit/taskmarket.py Outdated
…orking

Address cubic review (2 P2):
- One conversation can no longer exhaust the shared token cap: add a
  per-conversation cap (MAX_AUTH_TOKENS_PER_SCOPE) in front of the
  global backstop, so a single chat holding its max (10) cannot starve
  the other conversations of the 100-token pool.
- Token binding now follows the conversation thread id instead of the
  raw session id. Thread ids survive across HTTP API requests (where
  each request gets a fresh session id), so a token issued in one
  request is usable by create_task in a later request of the same
  thread, while cross-conversation tokens are still refused.

Tests: add per-conversation cap regression test (cap applies to one
thread only, another thread still issues) and HTTP-flow regression
test (token survives a session id change within the same thread).

Co-Authored-By: Hermes Agent jobs <hermes@nicosanchez.com.ar>
@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend Pertains to the Python backend. enhancement New feature or request size:XL This PR changes 500-999 lines, ignoring generated files. unit-tests Has unit tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants