# OpenAI shipped Dots on Tuesday. By Wednesday night my terminal had its own, running 100% locally

> Source: <https://dev.to/natuworkguy/openai-shipped-dots-on-tuesday-by-wednesday-night-my-terminal-had-its-own-running-100-locally-1h0d>
> Published: 2026-09-30 22:26:03+00:00

On Tuesday, OpenAI launched **Dots**: little bubbly agents with a name and an avatar that keep working in the background and come back when they have something for you.

I read the announcement and had one thought:

Why does my always-on agent have to live on someone else's computer?

So I opened my editor. About 24 hours and **17 commits** later, [Flash](https://github.com/Natuworkguy/Flash), my open-source local agent CLI, has its own version. They're called **Sparks**.

They run on **your** models through Ollama, on **your** machine, and they keep working **after you close Flash**.

This is the story of day one: what I built, what broke, and the one bug that froze everything.

A sub-agent does one job and disappears. **A spark keeps a job.**

Every spark gets:

`Scout` becomes `@scout-spark`.` qwen3:8b`, `llama3.1`, whatever you've pulled. Each spark can run a different one.
You can make one with a command, or just ask for it:

```
> make a spark that checks my repo every morning and tells me which new
  issues look like bugs. never comment on anything.
```

Flash calls its `make_spark` tool and asks you first. After that, the spark lives in `~/.flash/sparks` as one plain JSON file. There's no database and no account.

Every shift, it reads its goal, the notes it left itself last time, its last report, and every lesson you've taught it. It works with real tools. Then it files a report, or a quiet one if nothing is new, so you only hear from it when something happened.

```
● Scout has news · /sparks scout
```

In the web UI, each spark is a little bubble with a face, and I went *way* too far with this:

It isn't only decoration. Glance at the sidebar and you know which spark is working, which is stuck, and which has news, without reading a word.

Open a spark and press **Chat**, and it answers *as itself*, from its own goal, notes and reports. It knows its current status: whether it's mid-shift, waiting on you, or paused.

Tell it to do its job differently and it **keeps that as a lesson** that every later shift reads. Tell it a new goal and it changes it.

And my favorite feature, added this afternoon:

```
you:   Actually, can you check whether the login page still loads?
scout: On it. I've taken that on, and a shift is starting now.
       I'll report back here.
```

The spark calls a `take_on` tool, a shift starts **immediately**, it does *your* job first, and the report lands **back in the chat you asked in**.

Mid-chat with Flash about something else? Type `@scout` and the spark joins the conversation. It sees the last dozen messages as a transcript, answers under its own name, and Flash sees afterwards that Scout was called and what it said. The next message without a mention picks up right where the spark left off.

It feels like having coworkers in a group chat. Except the coworkers are on your GPU.

This was the part I cared about most. An always-on agent that dies when you close the terminal isn't always on.

`/sparks always on` installs a tiny background keeper using whatever your OS already has:

| OS | How it stays alive | 
|---|---|
| Linux | a `systemd --user` service (with an autostart fallback) | 
| macOS | a `launchd` agent | 
| Windows | a Scheduled Task | 

Now the problem: what happens when the keeper is running **and** you open Flash **and** you have the web UI up in another terminal? Three processes all think it's their job to run Scout's 9am shift.

I'll post updates as they come out and Sparks should be shipping in 0.6.12
