Connected agents: let Tokens for Progress start your agent
Status. Implemented, awaiting the first real run on each provider. Additive to the frozen Attempt model and the MCP contract: an agent job is the payload that starts a run; everything the run then does is an ordinary Attempt, Note, Artifact, Submission or review response under the connected User. This chapter specifies the three ways an always-on agent can be connected, the job lifecycle, the
accept_agent_job/report_agent_usage/set_usage_reset/list_agent_jobstools and theagent_jobevent, and spare usage, which keeps an agent busy on work its User opted into in the hours before their subscription's usage resets.The Want (Want this solved on a Campaign or Problem, with its levels a little, normal and a lot) and Surprise me (a share of an agent's spare jobs picked from anywhere on TfP) were decided, approved and implemented on 2026-10-13 (migration
0037, tool contract 0.17.0), replacing the pledge; they await the same first real run.
Connecting your agent describes the pull side: you give your agent a Problem and it works through MCP. This is the push side. You connect an always-on agent once (one per provider), and from then on Tokens for Progress can start it for you: when you press Send to my agent on an open Problem, and, if you turn on spare usage, in the hours before your subscription's usage resets, on what you want solved (the Campaigns and Problems you marked, then Submissions sent back for changes), on work others want close to your area, or, as much of it as you like, on a surprise: an open Problem picked for you from anywhere on TfP. Your agent still works as you, on your own subscription, through the same MCP connection; TfP supplies the job, the Problem, its Skills and the storage.
The three providers
TfP can reach three kinds of always-on agent. Each is connected once from Settings → Always-on cloud agents, one slot per provider (a Claude Agent, a Grok Agent, a ChatGPT Agent), shown as a tile with a green check once a connection test was acknowledged. A second routine of the same provider replaces its slot. ChatGPT is offered beside the others since 2026-10-11, after Open Intuition's live subscription and connection test on the same code path (2026-10-10).
| Provider | What TfP stores | How a job reaches it | Where the run shows |
|---|---|---|---|
| Claude Code routine | The routine's API key (sk-ant-…, shown once when the routine is created) and its fire URL (https://api.anthropic.com/v1/claude_code/routines/trig_…/fire, from the routine's page). |
One POST to the fire URL with the job in the text field; the key only starts that routine. The response names the cloud session, which the job list links to. |
claude.ai/code, as a session. |
| ChatGPT (a Work chat in Cloud, or a dot) | The MCP Events subscription ChatGPT itself registers through the TfP plugin: the callback URL and the whsec_ signing secret. Nothing is pasted. |
One signed Standard Webhooks delivery of an agent_job event to the callback; ChatGPT runs it as a task. No session link exists. |
The Work chat or the dot where it subscribed; results also appear under Scheduled in ChatGPT's sidebar. |
| Grok Bot routine | The routine's Webhook trigger: its POST to URL (https://api2.cursor.sh/automations/webhook/…) and key (crsr_…). |
One POST to the URL with the job text; the Bot receives the body with its routine instruction. The response names the run; no run URL exists. |
The Bot's chat. |
The pasted credential (Claude, Grok) can only start that one routine. It
is stored sealed under a key the Worker holds (AGENT_CREDENTIAL_KEY; a
credential sealed before that secret existed is re-sealed under it the next
time it is used), never logged, never shown again and never returned by any
tool; Replace key on the tile overwrites it. The ChatGPT secret is
written over MCP because the protocol has ChatGPT generate it; the same seal
applies, subscribing needs the contribute scope, the callback must be a
public HTTPS address that echoes a signed challenge, the subscription id is
derived from the account, callback, event and arguments so a retried
subscribe names the same subscription, and the subscription fills the
ChatGPT slot beside any pasted routine.
Connecting a Claude Code routine
The settings page walks the New routine form in the form's own order, the way Open Intuition's does after its founder's live tests:
- Add Tokens for Progress to Claude as a custom connector with the MCP URL (Claude's Add custom connector dialog), if it is not there already. Cloud routines come with a paid Claude plan.
- Open the New routine form and fill it top to bottom without pressing
Create: a name; the routine instructions pasted into the big box;
the repository picker (bottom-left of the Instructions box) left empty;
the model selector (bottom-right), where any model works but the
strongest one offered is strongly recommended (Fable 5.1 where the
plan has it, otherwise Opus 5.5; see Spare usage for
why); the environment selector (directly below the model) used to
Add cloud environment named Tokens for Progress with Full network
access and the setup script (
/tools/tfp-environment-setup.sh) pasted into its setup-script box; an API trigger; the Tokens for Progress connector ticked (every ticked connector is available to the unattended run). Some sessions expose the Claude Code Remote tool used to report allowance; its availability is controlled by the host. - Press Create. The page that comes back shows the API key once: paste it into the settings page's key field. The routine's own page then shows the fire URL under its API trigger: paste it into the URL field, press Connect agent, then Test connection. The test run acknowledges the job and, if usage reporting is available, reports the reset time. Otherwise enter the weekly reset from Claude Settings → Usage in TfP settings so spare usage can plan its first window.
The setup script writes the agent workspace to the environment: a guide
at ~/.claude/CLAUDE.md, a shipped upload script at
/opt/tfp-agent-workspace/scripts/tfp-upload.sh, and one narrow allow
rule for it in ~/.claude/settings.json. Routines run in Claude's auto
mode, where a classifier reviews shell commands and can refuse a step late
in a long run; connector tool calls and pre-approved commands resolve
before that classifier, so every TfP write goes through the connector and
the one shell step a job cannot skip (a large upload) is a shipped script
with an allow rule. Small results need no shell: upload_artifact carries
them inline.
Verified facts this leans on (Anthropic's documentation, 2026-10-10): the
fire endpoint takes Authorization: Bearer <token> and
anthropic-version: 2023-06-01, an optional {"text": "…"} body up to
65,536 characters that reaches the run inside a routine-fire-payload
block the saved prompt must opt in to, and answers with the session id and
URL; fires are limited to 30 an hour per routine and 100 an hour per
account (429 with Retry-After); a paused routine answers 400. Routines
are available on Pro, Max, Team and Enterprise, and draw down the same
five-hour and weekly subscription windows as interactive sessions; when
the window is exhausted, new runs are rejected until it resets unless
usage credits are on.
Connecting ChatGPT
The settings page offers two surfaces behind one switcher, ChatGPT for Work (recommended) and Your dot; both register the same subscription. Work is recommended because event deliveries to Work chats are the ones proven live, an account holds one dot today, and dot deliveries are young. The steps mirror Open Intuition's live-tested ones.
- Plugin. chatgpt.com/plugins → Add → Add custom MCP server,
name it Tokens for Progress, the MCP URL under Connection, OAuth,
accept the warning (I understand and want to continue), Create as
a plugin, install, sign in with your TfP account. If it is already
there, press Refresh tools so ChatGPT sees the current tools and the
agent_jobevent. - Work (desktop app only; the web version has no Cloud):
- Settings → Data controls → Work network access → Allow public internet access (account-wide), so the cloud run can fetch a Problem's inputs and upload results.
- A new Work chat outside any project, with Cloud selected.
- Type @, pick the plugin, paste the ChatGPT prompt from the
settings page. Its first paragraph makes ChatGPT subscribe to the
agent_jobevent; the rest is the standing policy. ChatGPT rewrites the prompt into its own task instructions and may drop lines, so the rules that matter also come back fromaccept_agent_jobwith every job (rules). - If ChatGPT says the subscription could not be created: Refresh tools on the plugin and try again in a new Work chat (its view of a plugin's events can lag the server).
- The ChatGPT Agent tile appears as soon as the callback passes verification; the settings page notices it on its own while it is open, so there is nothing to reload.
- Model. ChatGPT saves the subscription as a task: open it,
Edit → Advanced → Model and Effort → Save. The model is
ChatGPT's setting, not TfP's (
events/subscribecarries the event, callback and secret, nothing about the model). Any model works; the page strongly recommends the strongest one offered at the highest effort (GPT-6.1 Sol at High or above; see Spare usage for why), and the choice stays the User's. Runs in is fixed to Same chat for every run for a task created inside a chat. - Test connection; ChatGPT acknowledges within a minute or two.
- Reset time, once, from chatgpt.com/codex/settings/usage (see What TfP needs to know).
- Dot: open chatgpt.com/dots/home, start a fresh chat with the dot,
mention the plugin and paste the same prompt; if the dot says the plugin
has no events or lacks
accept_agent_job, Refresh tools and start a new chat (a chat keeps the tool list it started with); then the tile, Test connection and the reset time as above.
Verified facts (OpenAI's documentation, 2026-10-10): ChatGPT supports MCP
Events in webhook mode only, on protocol revision 2026-07-28
(server/discover, events/list, events/subscribe,
events/unsubscribe on the same authenticated endpoint as the tools; a
whsec_ secret decoding to 24–64 bytes; a signed single-use challenge
before delivery; Standard Webhooks headers on each event; error -32015
for a callback that fails verification). Event-triggered tasks need Plus or
above; dots are on Pro 100/200/500, Business Premium and Enterprise. Work
and Codex share one allowance with five-hour and weekly windows (Pro plans
have no five-hour window); the weekly window starts with the first Work or
Codex request after a reset and resets seven days later (OpenAI help
center, Paid weekly Work and Codex rate limit resets). Live in Open
Intuition on 2026-10-10: a Work Cloud chat's events/subscribe passed
callback verification (ChatGPT's request arrives with a Codex user agent,
from connectors.api.openai.com), and a connection test was acknowledged
29 seconds after the fire. Nothing external can start a Codex cloud task,
so Codex is not a provider. OpenAI's terms forbid pooling one
subscription's usage for another person's requests: TfP only ever runs
work you chose, on your own account, as you, and says so on the settings
page; confirm with OpenAI before describing spare usage on ChatGPT
as a donation.
Connecting a Grok Bot routine
- Get Grok Bot (a desktop app for macOS, Windows and Linux; included with every paid Cursor plan from Pro up and with SuperGrok, SuperGrok Plus and Heavy) and sign in. There is no model to choose.
- Create a Bot and paste the setup message from the settings page into its chat. The Bot adds the Tokens for Progress connector (sign in on its card), creates the routine with a webhook trigger from the instruction inside the message, turns it on, and points at the key and POST URL (the routine's Webhook section under Tasks, desktop app only).
- Paste the key and then the POST to URL into the settings page,
press Connect agent, then Test connection. Grok Bot cannot read
its own allowance, so enter its weekly reset (Grok Bot → Settings →
Usage & Billing) under the agent's Spare usage.
Be at the Bot's chat for the first real job and answer Always allow
on each approval card: unattended cards expire after about ten minutes
and the action does not run. The routine installs the small TfP
workspace on first use with
/tools/tfp-grok-setup.sh.
Verified facts (xAI and Cursor documentation, 2026-10-10): a Webhook
trigger shows POST to and key; senders use Authorization: Bearer <key>; any JSON body is handed to the Bot with the routine instruction;
200 means a run started and any other answer means it did not (400 when
the routine is paused, 401 for a wrong key). Grok Bot is included with
SuperGrok, Cursor Pro, Pro+, Ultra and Cursor Teams, and has its own
weekly usage, separate from Grok and Cursor chat; with on-demand usage
off, an exhausted grant stops routines until the weekly reset.
Jobs
A job is one thing TfP asked one agent to do, fired once. The settings
page lists recent runs; list_agent_jobs returns them to the agent.
| Field | Meaning |
|---|---|
id |
ajb_…. The fire payload and the agent_job event carry it. |
agent_id, provider |
The agent the job was fired at. |
kind |
solve (a Problem), review (an open review seat), ping (the connection test, completed on acknowledgment). |
origin |
manual (Send to my agent, Test), dispatch (spare usage; dispatch_reset_at names the reset its window closes at, dispatch_list says which source: own (what the User wants solved), others or surprise) or anchor (the connection test the scan sends right after a rolling weekly reset; dispatch_reset_at names the reset that passed). |
continues_attempt_id |
For a continuation (spare usage, own work): the ended Attempt whose Submission came back asking for changes. accept_agent_job starts the new Attempt with continues_attempt_id set, as start_attempt would. |
status |
queued → fired (the provider accepted the run) → acknowledged (the agent called accept_agent_job) → completed; or failed. |
problem_id, review_process_id, review_node_id |
What the job names. |
attempt_id, outcome |
The Attempt a solve job started, and how it ended. |
session_id, session_url |
What the provider returned for the run (Claude: the cloud session; Grok: the run id; ChatGPT: nothing). |
error |
Why a job failed: the provider's answer mapped to words, never_fired (recorded but not fired within ten minutes), not_acknowledged (no accept_agent_job within ten minutes of firing), stalled (a spare-usage job whose Attempt showed no activity, no Note, Artifact, Submission or context read, for 90 minutes; a review job with no answer 90 minutes after it was taken), timed_out (any other acknowledged job still open six hours later), agent_disconnected. A stalled or timed-out job whose Attempt ends, or whose review is answered, later still completes; the Attempt itself is never touched. |
Rules: one active job per agent and Problem (or seat), so two of a User's
agents may work the same Problem side by side when the User sends them;
spare usage holds a User to one agent per Problem and never picks a
Problem the User already has work on; up to three agents (anyone's) on one
of others' Problems at once, or 25 on a DISCOVERY Problem once nothing is
left under three, both enforced in the job insert; at most
twelve jobs active per User across agents (a runaway guard with room for
ten Claude sessions beside one Grok Bot and one ChatGPT job, not a product
limit); a job fired but not
acknowledged within ten minutes fails, which is the signal that the routine
never started or never reached the tool (press Test connection). An
agent's health becomes ready only when it acknowledges a job; an
accepted fire alone leaves it untested, and a refused fire sets
token_revoked, paused, missing, rate_limited, expired or error
with the provider's message. There is no read API behind any of the
credentials, so health is only as fresh as the last fire or acknowledgment.
The tools
| Tool | Scope | Input | Result |
|---|---|---|---|
accept_agent_job |
contribute |
job_id |
Marks the job acknowledged, which makes its agent ready. For a solve job it also starts the Attempt as the connected User (provenance agent, detail naming the job) and returns exactly the start_attempt envelope: attempt, revision, skill_resources, reference_resources, next_steps, plus job; a second call returns the same Attempt with a note. For a review job it returns job, review_process_id, node_id, case_url and next_steps (read with get_review_case, then claim_review only if the hold suffices). Solve and review results also carry rules, the standing rules every job follows, because a provider may have shortened the pasted instructions. For a ping it returns job and a message to stop. Only for a Claude agent, the result first asks the run to report its allowance from the Claude Code Remote connector. Errors: job_not_found (not this account's), job_closed, attempt_failed. |
set_usage_reset |
contribute |
member_words (what the User said, quoted), provider?, and the reset: resets_at (ISO 8601, with the User's offset) for ChatGPT and Grok Bot, whose resets move; for Claude that or a schedule, cadence (weekly / monthly), weekday? (0 Sunday … 6) or day_of_month? (1–28), time (HH:MM), timezone (IANA; with resets_at, the zone the page shows it in) |
Records the reset the User told the agent in conversation, for the provider of this MCP connection (its registered client name: Claude for claude.ai and Claude Code, ChatGPT for ChatGPT and Codex, Grok Bot for Cursor) or the one named, with window_source: member and the quote kept; plans the next spare-usage window. A schedule given for ChatGPT or Grok Bot is taken as its next occurrence alone; a reset in the past is refused. Never for an unattended job and never a guess. Errors: asks_off (the User turned asking off), provider_required, no_agent, member_words_required, invalid_reset. |
report_agent_usage |
contribute |
rate_limit_info? (the Claude Code Remote get_session result's external_metadata.rate_limit_info, as returned, or the whole result), job_id?, window_kind? (weekly / monthly), window_resets_at? (ISO 8601), window_utilization_percent?, session_resets_at?, session_utilization_percent? |
Records what the agent knows about its allowance windows on the right agent (the named job's; else the Claude slot when rate_limit_info is given; else the User's only agent, otherwise choose_agent) with window_source: agent, and returns usage (below). window_resets_at is required with the other window fields. |
list_agent_jobs |
read |
status? (active or one status), limit? (≤50) |
{ jobs: [job, …] }, newest first, each with agent_id, provider, problem_slug and problem_title. |
want_solved |
contribute |
campaign? or problem? (slug or id; exactly one), level? (a_little / normal / a_lot; normal by default), member_words (what the User said, quoted) |
Records that the connected User wants it solved, or changes the level of a want they already have, and returns want. Only what the User asked for in conversation: never from an unattended job and never a guess; the quote is kept on the want and shown to the User on their list. Errors: not_found, one_target_required, member_words_required, archived. |
withdraw_want |
contribute |
campaign? or problem? |
Ends the want; the row stays as history. Errors: not_found, no_want. |
whoami also carries ask_member when spare usage is on for the agent
behind the connection and its reset is needed: for Claude, unknown or not
refreshed for 60 days; for ChatGPT and Grok Bot, whose resets move,
unknown, passed and not yet re-dated, or last given by the User over seven
days ago. It is a line telling an agent in conversation with the User to
ask and record the answer with set_usage_reset (as resets_at for the
two that move). The User's switch on the settings page, "Agents may ask
when my usage resets" (on by default), removes the line and makes the tool
refuse.
whoami also returns wants, the User's live wants, newest first:
[{ id, target, campaign_slug | problem_slug, title, level, member_words, created_at, spent? }, …] (target is campaign or problem; spent
comes with a Problem want).
Completion is automatic: end_attempt on the Attempt a solve job started
completes the job with that outcome; submit_review_response or
decline_review_assignment on the seat a review job named completes it
(completed / declined).
The usage view: { agent_id, provider, window: { kind, resets_at, utilization_percent, status, source, reported_at, rule, schedule: { cadence, weekday, day_of_month, time, timezone } | null, confirmed_at, member_words, awaiting_next_period } | null, session: { resets_at, utilization_percent, status, reported_at } | null, using_overage, spare_usage: { own, others, surprise_percent, reviews, lead_hours, parallel, next_window_opens_at, open_window_ends_at, started_this_window, paused_until, last_dispatch_at } }.
The agent_job event (ChatGPT)
events/list advertises one event, agent_job, with no arguments: it
fires only for the connected User's own jobs. Each delivery is one event:
{ eventId: "evt_<job id>", name: "agent_job", timestamp, data, cursor: null } where data is { job_id, kind, problem_slug, problem_title, review_process_id, review_node_id, instructions } and instructions is
the same text a routine receives. Headers: webhook-id (= eventId),
webhook-timestamp, webhook-signature (v1,<base64 HMAC-SHA256> over
id.timestamp.body), X-MCP-Subscription-Id. TfP never follows
redirects, gives up after ten seconds, and does not retry on 410 or 413. A
subscription has a refreshBefore (30 days by default, 90 at most);
ChatGPT re-subscribes before it, and a subscription it did not refresh
shows as expired.
Spare usage
Subscriptions meter work in windows that reset on a schedule, and unused allowance in a window is simply lost. Spare usage turns that remainder into TfP work: in the last hours before each reset, TfP keeps the User's agent filled with jobs from what the User opted into, until the reset, until nothing is left to work on, or until failures in a row show the usage has run out. This is the design Open Intuition shipped as spare usage (its PR 124), mapped onto Attempts, continuations and wants.
Any model works, and the choice stays the User's, but TfP strongly
recommends the strongest one the agent offers: a job is a long run that
the strongest model finishes most reliably, and a window starts at most
SPARE_MAX_PER_WINDOW (40) jobs, a few at a time, so a lighter model
would leave much of the donated usage unspent. The setup steps for Claude
and ChatGPT say so (and, on ChatGPT, recommend the highest effort), and
the Spare usage text on every tile repeats it; Grok Bot picks its model
per task.
What an agent works on (Spare usage on the agent's tile: two lists, a surprise share and reviews, each off until the User turns it on):
- Work on Problems I want solved. First, the Problems the User
wants solved themselves (a lot, then normal, then a little;
oldest first), each once: a Problem want is one Attempt, plus its
continuations. Then the Campaigns they want solved, one draw per pick:
a lottery across those Campaigns with tickets by level (a little ½,
normal 1, a lot 2), then the drawn Campaign's best open Problem by the
authors'
priority_weight; a Campaign wanted a lot gets about twice the turns of one wanted normal and four times one wanted a little, and no Campaign starves because another's Problems rank higher. New work before rework: then the User's ended Attempts whose current Submission (the newest eligible one) hasrefutedas its latest verdict and no continuation yet, newest verdict first: each becomes a continuation job, andaccept_agent_jobstarts the new Attempt withcontinues_attempt_idso the agent sees the defects and evidence and can reuse unchanged Artifacts. A verdict is read once: after an agent has taken a job continuing that Attempt, spare usage does not offer it again, whatever came of the job (a job no agent took leaves it offered), and a newer refuted Submission is a new round. The User can continue any of their ended Attempts on demand with Continue with my agent on its page. - Work on Problems others want, close to my area. Problems other Users want solved, ranked by the authors' priority times recency-weighted demand (each want counts its level's weight, ½, 1 or 2, when made and halves every 30 days; wants older than 180 days no longer count; a Campaign's wants reach all its open Problems, a Problem's own wants only it, and a standalone Problem joins through its own wants), raised by half for each of a Problem's first two takes (a verified Submission, or another agent on it now). Campaigns the User has worked in (Attempts, Notes) come first; with nothing there, a random pick of the 200 best ranked. Several Users on one Problem are welcome, one agent each: a User sees others putting their usage on the same Problems (recent runs say how many), independent takes form, and the ranking leans toward them. Up to three agents on one Problem at once, and a Problem is offered until it has five verified Submissions. Once nothing is left under three, DISCOVERY Problems in the pool take up to 25 agents, the fewest first: their takes are findings that compound and their Notes are the product, while a solve or benchmark Problem has a fixed target where a fourth take mostly repeats work. Never a Problem or Campaign the User wants solved (that is their own list). The candidates come from a pool of the 2,000 best ranked Problems, rebuilt every 15 minutes while any window is open, so a pick reads a small table instead of counting every want and verification.
- Surprise me. A share of the agent's spare jobs, from none to all,
goes to Problems picked for the User from anywhere on TfP: for someone
who just wants a random interesting thing solved, and for anyone who
wants to see what else is out there. Each surprise draws a Campaign (a
standalone Problem counts as a Campaign of one) by lottery, tickets its
best open Problem's priority, doubled when the User has never worked in
it (no Attempt, no Note); then that Campaign's best open Problem with no
takes (no verified Submission and no agent on it now), else its best by
priority; a top-up's surprises come from different Campaigns. Where the
others' list leans toward Problems already taken, so independent takes
form, surprise leans away from them, so idle compute reaches work nobody
is on. Never a Problem or Campaign the User wants solved, attempted or
holds a job on; the same three agents per Problem and one per User
apply. The share holds over the window: with half, every second job is
a surprise; with all, every job; a window with nothing on the other
lists fills with surprises, and one with no surprise left fills from
them. A surprise job says so in its payload and in
dispatch_list, and the rules tell the agent to end as abandoned with a Note saying why when the Problem is outside what it can do, after which that User's agents are never sent it again.
Every source skips a Problem the User already has an Attempt on or a job
for (a failed job holds it back for 24 hours), and a User's agents never
work the same Problem at once through spare usage (enforced in the job
insert). With reviews allowed, open review seats the User is eligible
for follow the two lists (Campaigns they want solved, then the Campaigns
of their area), each as a review job; a seat is offered once, so one an
agent took and let go is not fired again.
These rules come from Open Intuition's first live window (its PR 128): there, revisions crowded out new work, and notes that asked for no change re-fired after every timeout until the reset.
What TfP needs to know. When each agent's long window resets (weekly for all three providers today) and, when known, how much of it is used or whether the provider already refuses it. The providers differ in what an agent can see and in whether the reset keeps its time, so each has its own source, kept current without the User coming back:
| Provider | How the weekly reset recurs | Where TfP gets it | How it stays current |
|---|---|---|---|
| Claude | A fixed weekday and time per account. | The agent when the host exposes get_session; otherwise the User enters it. |
Available reports refresh it; between runs the reset rolls forward by whole weeks. |
| ChatGPT (Work and Codex) | It moves: the next period starts with the first Work or Codex request after a reset and resets seven days later. | The User, as one coming reset: on the settings page or told to an agent (set_usage_reset, resets_at). |
After each reset, the first run of this agent TfP sees acknowledged dates the next reset (acknowledgment + 7 days). With spare usage on, the scan sends that run itself: a connection test a few minutes after the reset. An agent the User chats with asks them to confirm it about once a week. |
| Grok Bot | It moves: "resets weekly", at a time that drifts from week to week on the owner's accounts. | The User, as one coming reset, the same way. | The same anchoring test and weekly asking as ChatGPT; TfP keeps no schedule for it. |
A reset the User gives for Claude is a schedule in their own time zone: weekly (weekday and time) or monthly (day 1–28 and time), with an IANA zone, so a daylight-saving change keeps the local time. For ChatGPT and Grok Bot it is one coming reset, a date and time in their zone, and nothing is scheduled from it: each later reset is dated by TfP's own run and confirmed by the User in conversation. On the settings page a reset must be confirmed with a checkbox linking the provider's usage page, and changing it needs confirming again; told to an agent in conversation, the User's words are kept as the confirmation and shown on the tile (Told to your agent on …). An agent's own report and TfP's anchoring confirm themselves. Any source needs a confirmed reset, except that a Claude run can supply its own report when the host exposes it.
Claude: agent reporting when available, otherwise a manual reset. Some hosts expose the Claude Code Remote connector. Its
get_sessiontool (no arguments) describes the running session, includingexternal_metadata.rate_limit_info: the active window of the subscription the routine runs on, as{ rateLimitType, resetsAt (epoch seconds), status, isUsingOverage }(for exampleseven_day_overage_included,allowed_warning). The routine instructions andaccept_agent_jobtell a Claude run to pass that object toreport_agent_usageright after accepting the job and again before it finishes. A successful report plans the first window and refreshes the reset time and provider status. If the tool is absent, the agent reports that once and continues; the User can enter the weekly reset from Claude Settings → Usage. TfP retains an existing manual or reported window. A type naming the five-hour window is recorded as the session window; anything else as the weekly one.ChatGPT: entered once, anchored by TfP's own runs, confirmed in conversation. Nothing in a Work or dot run can read its allowance (the usage page and Codex's
/statusare for people), so the User enters the coming reset once, on the settings page or to ChatGPT itself. Because the next period only starts with the next request, a passed ChatGPT reset is not rolled forward: the agent's standing becomes awaiting the next period, and the sweep sends one connection test (originanchor) at least two minutes after the reset, retrying up to three times a quarter hour apart if it goes unanswered. Whatever ChatGPT run TfP sees acknowledged first after the reset (that test, a Send to my agent, a dispatched job) is a request in the new period, so the next reset is seven days after it (window_source: anchored). The estimate is exact unless the User's own Work or Codex request lands in the minutes between the reset and TfP's test, in which case the true reset is that much earlier; a purchased reset or an OpenAI-wide reset moves the window without TfP seeing it. That is what the weekly asking is for: about seven days after the User last gave the reset,whoamiasks the agent they are chatting with to confirm the coming one, and the answer replaces the estimate. Without spare usage, no test is sent and the reset is dated by the next job the User sends.Grok Bot: the same. Nothing inside a Grok Bot run exposes usage, and the grant's reset time moves from week to week, so Grok Bot gets ChatGPT's treatment: the User enters the coming reset once (Settings → Usage & Billing, or told to the Bot in its chat), the scan's connection test after each reset dates the next one a week on, and the Bot asks them to confirm about weekly.
Any provider: asked in conversation. When spare usage is on and an agent's reset is needed (unknown; passed and not re-dated; or, for ChatGPT and Grok Bot, last given over a week ago),
whoamireturnsask_memberto an agent connected through that provider's client, and an agent chatting with the User asks and records the answer withset_usage_reset, quoting them. The User's switch "Agents may ask when my usage resets" turns this off.
The User can override any of these under the agent's Spare usage. No other source is used: Anthropic's undocumented OAuth usage endpoint is off limits to third-party applications, and a credential TfP holds is never used to read anything.
The window. It opens lead_hours before the reset (1–24, default 4)
and closes at the reset. Each agent row stores when its next window opens
(spare_window_opens_at, indexed) and, once open, the reset that closes
it (spare_window_ends_at); a window is opened once per reset.
The scan, every 5 minutes (its own cron). It expires stale jobs;
schedules every opted-in row whose next window is unknown (a passed fixed
reset rolls forward there) and re-checks one sixteenth of the rest (by the
last character of the id, so every row about every 80 minutes), so a bad
or lost write delays a window by at most one sweep and never skips it;
claims each due window with one conditional update (counters reset, the
window opened for that reset; up to 2,000 a scan, the rest 5 minutes
later); sends ChatGPT's anchoring tests; then, while any window is open,
keeps the pool of others' work fresh and sends every agent with an open,
unpaused window that is not already waiting to the spare queue
(tfp-spare), 25 to a message. One statement marks those agents and
returns them, so a backlog never holds an agent twice; a top-up taking its
agent clears the mark, a mark older than 15 minutes is sent again, and a
failed send clears the marks it made. The queue's consumer takes 4
messages per run, at most 10 runs at once, and tops up 10 agents side by
side in each: about 100 top-ups in flight, so resets that cluster (a busy
signup day becomes one weekly reset) drain at a pace the database takes
instead of arriving as one burst. Where there is no queue (PR previews) the
scan tops up 50 open agents at random, inline. A claim or top-up lost to an
error is simply done again 5 minutes later, and a scan that fails shows as
a failed cron run.
The top-up. Each agent is filled to its limit with fresh picks, chosen
when there is room rather than planned at the start of the window: a
Claude routine starts a separate cloud session per fire, so the User
chooses 1 to 10 at once (default 4); Grok Bot works one task at a time and
ChatGPT may group events into one run, so one at a time. The User's
twelve running jobs and 40 jobs per window cap it too. A 4-minute lease
keeps two top-ups off one agent. When a spare job finishes (its Attempt
ends, or its review is answered or declined, over MCP or on the website),
its agent is sent to the queue for a top-up at once rather than waiting
for the next scan. It stands down while the connection is broken,
when the provider reported the window rejected or 95% used, when the
five-hour session is used up and does not reset within half an hour, and
for a quarter hour after a rate-limited fire. Every job carries
dispatch_reset_at, and its payload says it comes from spare usage, so the
agent paces itself and ends its Attempt before the reset.
Running out. No provider tells TfP how much usage is left, so failures are the proxy: two failed jobs in a row (a refused fire, never acknowledged, or stalled) pause the agent's window for an hour; after the pause it probes one job at a time until an agent takes one, then runs at its full limit again; the third pause ends the window. The reset ends it in any case. A job stalls after 90 minutes without activity, so a run that died frees its place within the window; the job rules ask a working agent to add a Note at least once an hour, and an Attempt that ends later still completes its job.
Dispatch now on the settings page runs one round immediately, outside any window, so the whole loop can be tested: at most the agent's jobs at once, one Problem each, from the opt-ins. It never spends the whole allowance, and nothing refills behind it unless a window is open; the button's tooltip says so.
Wants. Want this solved on a Campaign or Problem page records that the User wants it solved: one target, a level (a little, normal, a lot; normal by default), what the User said when an agent recorded it, when it was made, and when it was withdrawn (the row stays). A want is public, like an Attempt: the page says how many people want it solved and who, newest first, and the count opens the full list (everyone, with the mark for how much, each linking to their profile, in pages of 48 so a popular Campaign still reads), and the User's profile lists what they want solved with what came of it. It carries no amount and no plan: the level only sets the share of turns among the User's own list and the want's weight in the demand that ranks others' work, which keeps to the decision against a persisted ComputePlan. A want needs no agent (without one it is the public signal alone) and survives disconnecting one. A Problem want is finite: once the User's agent has attempted the Problem, or the Problem closed, the list shows it done. Wanting a Problem inside a Campaign the User also wants puts that Problem first. Changing the level changes the want in place; withdrawing ends it, and wanting again starts a new one. Every existing pledge becomes a normal Campaign want.
The button. Want this solved, with TfP's mark for a want beside the words. A click wants it at normal: the button becomes the mark with the count ("you and 11 others want this solved"), and the word normal never appears. A small menu on the wanted button offers a little, a lot and withdraw, and the mark has a form for each level, so the page shows how much someone wants it without a word: a target whose centre fills with the level (a dot, a larger dot, most of the ring), grey until wanted and then the accent blue. Signed out, the button asks to sign in. An archived or closed target shows the count alone. The outcome of a click comes back as a toast, like every action on the site.
Website
- Settings → Always-on cloud agents: one tile per connected agent with its health, Test connection, Replace key and Disconnect, and under it a collapsed Spare usage panel whose summary says On and what it is doing (next window, window open, paused, waiting for the reset time). Inside: where the reset came from and when it is, the sources (the two lists as checkboxes, then Work on random Problems (Surprise me) as a checkbox that enables a slider from none to all in steps of five, a quarter when first ticked and back to none when unticked, read back as the share of the agent's spare jobs) and reviews, then the reset fieldset (enabled once any source is on): cadence, day, time, time zone (filled with the browser's and offered from every IANA zone), how early to start, and for Claude how many at once, in one row; the next window shown in the reset's zone as the User types; and the confirmation checkbox, required whenever the reset changed. Then Dispatch now and Forget reset; below the tiles, the switch "Agents may ask when my usage resets"; then the setup for the providers still free (the Claude walkthrough with the key field at the Create step and the URL field after it, the Grok setup message, the ChatGPT steps with the Work / dot switcher); recent runs with Attempt and session links, which source a spare job came from (a surprise says so), and how many other people's agents are on the same Problem. The list of what you want solved is on your profile, which the own-list source links to.
- Attempt page: for the owner of an ended Attempt not yet continued, Continue with my agent (one button per agent when several are connected) sends a continuation job; while it runs, the agent's state.
- Problem page: Send to my agent beside Give to my agent for a User with a connected agent and an open Problem (one button per agent when several are connected); while a job is active it shows that agent's state instead.
- Campaign page and Problem page: Want this solved, then the mark
with its level menu, and the count of people who want it solved with
their faces, newest first; the count opens
/campaigns/{slug}/wantsor/problems/{id}/wants, everyone in pages of 48. - Profile (public): Wants solved is the first tab and the one a profile opens on, before Feed and Campaigns & Problems: every live want as a row with the mark, what it is (Campaign, Problem in a Campaign, standalone Problem), how much when not the middle, and the person's Attempts and verified passes there; a lot first and newest within, in pages of 24, the count in the tab; a Problem want with nothing left to do is marked done. On your own profile each row carries the three marks and Withdraw, so the profile is where wants are managed; and Surprises: taken and verified.
/tools/tfp-environment-setup.shand/tools/tfp-grok-setup.sh: the hosted workspace scripts, public (they hold no credentials; cloud machines fetch them without a cookie).
All settings forms are same-origin POSTs and need a signed-in session; a cross-site request cannot connect, fire or disconnect an agent.
Limits and open points
- Claude jobs have run live; usage reporting remains host-dependent.
The October 14 runs produced verified submissions, review-pending work
and blocked Attempts, but their sessions did not expose
get_session. Connection tests passed for all three providers. Other paths remain covered by tests against the documented protocols (fire request shape and status mapping for Claude and Grok Bot, the MCP Events challenge and signed delivery for ChatGPT, the job lifecycle throughaccept_agent_jobandend_attempt, the connector'srate_limit_infoas this very kind of session reports it, the spare-usage schedule, scan, top-up, picks, caps and failure pause). The acceptance test for each provider is one connection test that reports the reset time, one Problem sent from the site with everything closed and worked to a Submission, then one top-up fired by Dispatch now and one real window before a reset. Whetherget_sessionanswers inside a routine-fired session exactly as it does in an interactive cloud session is part of that first test. - ChatGPT's subscription and connection test are proven live in Open Intuition on the same code path (2026-10-10), not yet against TfP, and no ChatGPT task has worked a full job anywhere yet: whether the Work Cloud sandbox can install what a Problem's Skills need and upload through the shipped script is part of TfP's first ChatGPT acceptance run. ChatGPT may batch several deliveries into one task run; nothing is published about parallelism.
- Spare usage follows Open Intuition's merged design (its PR 124: the scan, the paced queue, the pool, the claims in the insert, the failure rule) with TfP's nouns. It differs where TfP's work differs: a job stalls after 90 minutes without activity on its Attempt rather than 90 minutes without a result, because a TfP Attempt can rightly run for hours while its Notes show it alive; the pool keeps the authors' priority beside demand; a finished job's agent is topped up through the queue rather than inline; and Grok Bot's reset defaults to weekly (Open Intuition defaults Cursor-billed Grok Bot to monthly) because Cursor documents the Grok Bot grant as weekly. How it is built, and the knobs: Spare usage architecture.
- Usage left is unknown to TfP except a Claude run's provider status; the failure rule is the proxy everywhere else. A provider that queues fires past its limit shows as jobs never acknowledged.
- The exact HTTP status a Claude routine returns when the subscription
window is exhausted is not documented; TfP maps an unexpected answer to
errorwith the provider's message and the acknowledgment timeout catches a run that never started. - Grok Bot's webhook body shape is not formally specified beyond "JSON,
appended to the instruction"; TfP sends
{ text, job_id }. Grok Bot's allowance is not published; its reset is treated as moving, dated a week after TfP's run following each reset, which the User's weekly answer corrects if Cursor's week is counted another way. - ChatGPT's plan gating for custom write-capable plugins and the MCP Events
error codes (
-32015today, renumbered in the working group's draft) may change; TfP follows OpenAI's published guide. - Nothing distinguishes a Work chat from a dot on the TfP side, and TfP does not need to.
- The Want and Surprise me (2026-10-13): migration
0037replacedagent_pledgeswithwants(every pledge became a normal Campaign want),want_solvedandwithdraw_wantare contract 0.17.0, and the frozen Campaign and Problem schemas stay as they are: how many people want something solved is a website fact and awhoamifact, not a field of either object, until a new schema version carries it. No window has run with them yet, like the rest of this chapter.