<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: nestor quiroga</title>
    <description>The latest articles on DEV Community by nestor quiroga (@nestor_quiroga_d0a1aef9d7).</description>
    <link>https://dev.to/nestor_quiroga_d0a1aef9d7</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4090683%2F90cb84df-4924-4e2b-9529-6f8775377014.jpg</url>
      <title>DEV Community: nestor quiroga</title>
      <link>https://dev.to/nestor_quiroga_d0a1aef9d7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nestor_quiroga_d0a1aef9d7"/>
    <language>en</language>
    <item>
      <title>Four ways to overcount what your coding agent costs</title>
      <dc:creator>nestor quiroga</dc:creator>
      <pubDate>Mon, 24 Aug 2026 11:17:18 +0000</pubDate>
      <link>https://dev.to/nestor_quiroga_d0a1aef9d7/four-ways-to-overcount-what-your-coding-agent-costs-2acl</link>
      <guid>https://dev.to/nestor_quiroga_d0a1aef9d7/four-ways-to-overcount-what-your-coding-agent-costs-2acl</guid>
      <description>&lt;p&gt;I spent a week building a cost tracker for Claude Code and Codex, and the whole time I could not make my numbers agree with anybody else's. Mine were always lower. A lot lower.&lt;/p&gt;

&lt;p&gt;It turned out my numbers were the right ones, and the reason is worth writing down, because every mistake below is the &lt;em&gt;obvious&lt;/em&gt; reading of the data.&lt;/p&gt;

&lt;p&gt;All the figures here come from one real history on one machine: &lt;strong&gt;2.52 GB of Claude Code transcripts and 1.72 GB of Codex rollouts&lt;/strong&gt;, 824 files. Nothing simulated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the data lives
&lt;/h2&gt;

&lt;p&gt;Both agents already write everything you need, locally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code: &lt;code&gt;~/.claude/projects/**/*.jsonl&lt;/code&gt;, one JSON object per line. Assistant lines carry &lt;code&gt;message.usage&lt;/code&gt; with &lt;code&gt;input_tokens&lt;/code&gt;, &lt;code&gt;output_tokens&lt;/code&gt;, &lt;code&gt;cache_read_input_tokens&lt;/code&gt; and a &lt;code&gt;cache_creation&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;Codex: &lt;code&gt;~/.codex/sessions/**/rollout-*.jsonl&lt;/code&gt;, with &lt;code&gt;token_count&lt;/code&gt; events and — nice touch — the real rate limits of your plan.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No API call, no account. The whole thing is a file read. Which is exactly why it feels easy, and why it is easy to get wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overcount #1: one line is not one charge
&lt;/h2&gt;

&lt;p&gt;This is the big one.&lt;/p&gt;

&lt;p&gt;An assistant message with several content blocks — say a bit of text and two tool calls — gets written to the transcript &lt;strong&gt;several times&lt;/strong&gt;, and every one of those lines carries the &lt;strong&gt;complete&lt;/strong&gt; &lt;code&gt;usage&lt;/code&gt; object. Not a slice of it. The whole thing.&lt;/p&gt;

&lt;p&gt;If you sum &lt;code&gt;usage&lt;/code&gt; per line, you count that message three or four times.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Lines with a usage object:  192,262
Actual messages:             92,666
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;99,141 of those lines are repeats inside a single file.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overcount #2: the same message lives in several files
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;claude --resume&lt;/code&gt;, forks, and git worktrees all copy conversation history into new transcript files. The same &lt;code&gt;message.id&lt;/code&gt; shows up in two files that have different names, different sessions, sometimes different project paths.&lt;/p&gt;

&lt;p&gt;In my history that is 440 messages. Small — half a percent — but it means deduplication has to be &lt;strong&gt;global&lt;/strong&gt;, not per file. If you dedupe within each file and then add the files up, you still overcount.&lt;/p&gt;

&lt;p&gt;Put #1 and #2 together and here is what it does to the bill:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cost if you count lines&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$66,006&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost if you count messages&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$29,087&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That is &lt;strong&gt;127% too much&lt;/strong&gt;. Not a rounding error. More than double.&lt;/p&gt;

&lt;p&gt;The fix is four lines. Key by &lt;code&gt;message.id&lt;/code&gt;, and when the same id shows up with different usage numbers — 7,326 of mine did, because the record gets rewritten while the message is still being generated — keep the one with the highest &lt;code&gt;output_tokens&lt;/code&gt;, which is the message's final state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;previous&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;previous&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;previous&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Overcount #3: reasoning tokens are already in the output
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;output_tokens_details.thinking_tokens&lt;/code&gt; and Codex's &lt;code&gt;reasoning_output_tokens&lt;/code&gt; are a &lt;strong&gt;breakdown&lt;/strong&gt; of &lt;code&gt;output_tokens&lt;/code&gt;, not an addition to it. They are informative — you may want to know how much of your spend is thinking — but adding them to the total charges you twice for the most expensive kind of token there is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overcount #4: Codex totals are cumulative
&lt;/h2&gt;

&lt;p&gt;Codex emits a &lt;code&gt;token_count&lt;/code&gt; event per turn, and &lt;code&gt;info.total_token_usage&lt;/code&gt; is the running total &lt;strong&gt;for the whole session&lt;/strong&gt;, not for that turn.&lt;/p&gt;

&lt;p&gt;One ordinary session of mine had 119 of those events. Sum them and you report roughly sixty times the real consumption.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;First event total:      15,574
Last event total:   15,927,387
Sum of per-turn deltas: 15,927,387   ← equals the last total, as it should
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So: take the last total, not the sum. And watch for the total going &lt;em&gt;down&lt;/em&gt;, which means the context was reset — close that stretch, add it to the base, and start accumulating again.&lt;/p&gt;

&lt;h2&gt;
  
  
  And one way to undercount
&lt;/h2&gt;

&lt;p&gt;Cache writes do not all cost the same. Anthropic bills a &lt;strong&gt;1-hour&lt;/strong&gt; cache write at 2× the input rate and a &lt;strong&gt;5-minute&lt;/strong&gt; one at 1.25×. Recent transcripts split them for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"cache_creation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ephemeral_5m_input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4210&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ephemeral_1h_input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;51160&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Older ones only have the lumped &lt;code&gt;cache_creation_input_tokens&lt;/code&gt;. If you treat every cache write as 5-minute, you understate the total — by &lt;strong&gt;10.3%&lt;/strong&gt; on my history, where 722M tokens were written at 1 hour against 66M at 5 minutes. Long agent sessions lean heavily on the 1-hour cache, so this grows with exactly the usage pattern you care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two numbers that must never be mixed
&lt;/h2&gt;

&lt;p&gt;The dollar figure any of these tools shows you is &lt;strong&gt;API-equivalent cost&lt;/strong&gt;: what those tokens would have cost on pay-as-you-go. On a Max or a Plus plan, it is not your invoice and it never will be.&lt;/p&gt;

&lt;p&gt;What you actually consume is quota, and there the two agents differ: Codex publishes its own limits in the transcript (percent used, window, reset time, plan), while Claude Code does not record them at all. You can reconstruct Claude's 5-hour window from timestamps — it opens with the first message after a 5-hour gap — but you cannot know how much of it you have burned. So don't print a percentage. Print the time left and label the thing as derived.&lt;/p&gt;

&lt;p&gt;Guessing there is how a tool loses the user's trust the first time they compare it with reality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does it perform?
&lt;/h2&gt;

&lt;p&gt;Two things mattered more than I expected:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filter on bytes before decoding.&lt;/strong&gt; Transcripts contain enormous lines — base64 images, big tool outputs. Converting those to strings just to discard them cost 7 seconds of a 12-second pass. Check for &lt;code&gt;"usage"&lt;/code&gt; in the raw &lt;code&gt;Buffer&lt;/code&gt; first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never &lt;code&gt;Buffer.concat&lt;/code&gt; per chunk.&lt;/strong&gt; My first line splitter concatenated the pending buffer with each new chunk, which is quadratic when a single line is tens of megabytes. Codex's 1.72 GB took &lt;strong&gt;37 seconds&lt;/strong&gt;. Collecting the chunks in an array and joining once, when the line is actually complete, brought it to &lt;strong&gt;2.2 seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Full pass over 4.25 GB: about 7 seconds. Incremental updates after that, using a &lt;code&gt;(path, size, mtime, byte offset)&lt;/code&gt; watermark per file: 28 milliseconds. The whole index is 3.4 MB gzipped, so there is no database anywhere in this — a compressed NDJSON file loads in 114 ms.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I am least sure about
&lt;/h2&gt;

&lt;p&gt;Everything above is measured. One thing is inferred: &lt;strong&gt;that a repeated &lt;code&gt;message.id&lt;/code&gt; means a single charge.&lt;/strong&gt; It follows from how the API bills, and it fits the fact that repeats carry identical or growing usage numbers. But there is no official document saying so, and if I'm wrong, my numbers are too low rather than too high.&lt;/p&gt;

&lt;p&gt;The check that would settle it is comparing a month of usage against a real pay-as-you-go invoice. I don't have a metered account with enough traffic. If you do and you're curious, I'd genuinely like to know the answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tool
&lt;/h2&gt;

&lt;p&gt;All of this is in &lt;strong&gt;CostKeeper&lt;/strong&gt;, a free VS Code extension: both agents in one table, cost by project, model, day, branch and session, client tagging and CSV export for invoicing, real Codex quota. Local only, no account, no telemetry, and the index stores no free text at all — no prompts, no code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://marketplace.visualstudio.com/items?itemName=argalla.costkeeper" rel="noopener noreferrer"&gt;Marketplace&lt;/a&gt; · &lt;a href="https://open-vsx.org/extension/argalla/costkeeper" rel="noopener noreferrer"&gt;Open VSX&lt;/a&gt; · &lt;a href="https://github.com/TecniartGalicia/costkeeper" rel="noopener noreferrer"&gt;source, MIT&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But honestly, the reading of the format matters more than the tool. If you maintain one of the other trackers, take the four fixes — they're worth more to your users than anything I could add.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>opensource</category>
      <category>vscode</category>
    </item>
    <item>
      <title>I gave my AI coding agents a night shift</title>
      <dc:creator>nestor quiroga</dc:creator>
      <pubDate>Sun, 23 Aug 2026 10:34:52 +0000</pubDate>
      <link>https://dev.to/nestor_quiroga_d0a1aef9d7/i-gave-my-ai-coding-agents-a-night-shift-f60</link>
      <guid>https://dev.to/nestor_quiroga_d0a1aef9d7/i-gave-my-ai-coding-agents-a-night-shift-f60</guid>
      <description>&lt;p&gt;I use Claude Code and Codex every day. And I kept running into the same want: hand the agent a task at night — bump dependencies, keep the changelog current, fix the flaky test — and just review it in the morning over coffee.&lt;/p&gt;

&lt;p&gt;You can already schedule the agents. The catch is &lt;em&gt;where&lt;/em&gt; they run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scheduling a prompt isn't the hard part
&lt;/h2&gt;

&lt;p&gt;Every scheduler I tried runs the agent on your working directory. That's fine at 2pm when you're watching. It's not fine at 3am when you're asleep: if the agent gets something wrong, it gets it wrong &lt;strong&gt;on your code&lt;/strong&gt;, on the branch you'll open tomorrow.&lt;/p&gt;

&lt;p&gt;I didn't want "an agent that runs unattended." I wanted "an agent whose overnight work is &lt;strong&gt;isolated and reviewable&lt;/strong&gt;."&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea: a worktree per run, and a morning inbox
&lt;/h2&gt;

&lt;p&gt;So I built a small VS Code extension called &lt;strong&gt;TaskKeeper&lt;/strong&gt;. Each scheduled run happens inside a fresh &lt;strong&gt;Git worktree&lt;/strong&gt; created from the exact base commit. The agent works on a throwaway copy; your checkout is never touched. When it finishes, the result lands in a review inbox: the diff, the files it changed, what it cost, and two buttons — &lt;strong&gt;accept&lt;/strong&gt; (a local merge, never a push) or &lt;strong&gt;discard&lt;/strong&gt; (worktree and branch gone, nothing left behind).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqbfs3bdvzhn5tszp9xm8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqbfs3bdvzhn5tszp9xm8.gif" alt="A task runs overnight in an isolated worktree, then waits in the morning inbox — accept or discard" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The whole bet is that one line: &lt;em&gt;if it gets it wrong at 3am, it gets it wrong on a copy — and you decide in the morning.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What a finished run looks like
&lt;/h2&gt;

&lt;p&gt;You don't get a wall of JSON. You get a readable transcript with a summary header — cost, turns, files changed, which worktree — and the actions right there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj4399rf07djhlx4a7fmw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj4399rf07djhlx4a7fmw.png" alt="The morning inbox: a finished run with a readable transcript and accept/reject" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Accept merges into your branch locally. Reject deletes the worktree and its branch. Either way, nothing ever leaves your machine on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a task starts from intent
&lt;/h2&gt;

&lt;p&gt;A task is really two independent choices: which conversation (new / resume / fork) and where it works (isolated worktree, or directly in the repo). Instead of making you think about that, the panel opens on the intent — &lt;em&gt;in a conversation&lt;/em&gt; or &lt;em&gt;isolated task in a repo&lt;/em&gt; — and fills in the rest. There's a small catalog of templates (bump dependencies, write the changelog, lint sweep, fix failing tests) to start from.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9p51nxv6108uyyajdqdr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9p51nxv6108uyyajdqdr.png" alt="Creating a task: templates, the two intents, repository and agent" width="800" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  It runs with VS Code closed
&lt;/h2&gt;

&lt;p&gt;There's no daemon of mine sitting in your tray. Each task is one entry in the &lt;strong&gt;operating system's own scheduler&lt;/strong&gt; — Windows Task Scheduler or macOS launchd. At the scheduled time a short-lived worker starts, takes a machine-wide slot, creates the worktree, launches the agent, and records everything in a local SQLite database. The extension only &lt;em&gt;reads&lt;/em&gt; that database. If your machine is on, the task runs; on Windows it can even wake the machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping it safe
&lt;/h2&gt;

&lt;p&gt;Two things I wasn't willing to hand-wave:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Permission profiles per run.&lt;/strong&gt; Every run gets one of two explicit profiles — &lt;em&gt;audit (read-only)&lt;/em&gt; or &lt;em&gt;isolated changes&lt;/em&gt; — applied through a controlled settings profile that your personal Claude config can't widen. A read-only task stays read-only even if your interactive setup is permissive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local-first.&lt;/strong&gt; No telemetry, no account, no network call from the extension itself. Secrets that look like tokens are redacted from stored logs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Catching the failure schedulers hide
&lt;/h2&gt;

&lt;p&gt;The scary failure mode of any scheduler isn't a task that errors — you see those. It's a task that &lt;strong&gt;silently stops running&lt;/strong&gt; because its OS trigger got unregistered. So there's a &lt;em&gt;Last night&lt;/em&gt; digest: what ran while you were away, and a health check that turns a task &lt;strong&gt;red&lt;/strong&gt; if its trigger went missing, instead of it quietly never running again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg4dbcu5zvw6bx6fiyu36.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg4dbcu5zvw6bx6fiyu36.png" alt="The Last night digest with scheduler health, flagging a missing trigger" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching the spend
&lt;/h2&gt;

&lt;p&gt;Overnight agents cost money, so there's a monthly cap the runner itself enforces — once the month hits the cap, the next task is skipped before it spends. A panel breaks the month down by day and by task.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffrj94f9rfwzwwdy6gvq6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffrj94f9rfwzwwdy6gvq6.png" alt="The Spend panel: month vs. cap, by day and by task" width="800" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Cost is reported by Claude; Codex doesn't report it, and the panel says so.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things I actually run it for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dependencies:&lt;/strong&gt; weekly, bump to latest compatible and run the tests. I review the diff, I don't babysit the upgrade.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Changelog:&lt;/strong&gt; read-only, summarise the week's commits into a draft entry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flaky tests:&lt;/strong&gt; overnight, find the failing test and make it pass without changing intent — then I decide if the fix is real.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;It's free, local-first, no account. Windows and macOS (the macOS binaries are signed).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Marketplace: &lt;a href="https://marketplace.visualstudio.com/items?itemName=argalla.taskkeeper" rel="noopener noreferrer"&gt;https://marketplace.visualstudio.com/items?itemName=argalla.taskkeeper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Open VSX: &lt;a href="https://open-vsx.org/extension/argalla/taskkeeper" rel="noopener noreferrer"&gt;https://open-vsx.org/extension/argalla/taskkeeper&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not affiliated with Anthropic or OpenAI — it launches the same CLIs you already use.&lt;/p&gt;

&lt;p&gt;I'm most interested in how other people handle unattended agent runs: worktrees, containers, throwaway branches, something else? What's held up for you?&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>ai</category>
      <category>claude</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
