Skip to main content

Event-driven notify layer for agentic CLIs. Turn passive AI assistants into tapped-on-the-shoulder agents.

Project description

waitless

Turn your AI assistant into a tapped-on-the-shoulder agent.

When something finishes in the background - a build, a test suite, a deploy, a scrape, an email arriving, a long-running Codex/Claude task - waitless makes sure the active session finds out on its very next tool call. No polling. No HTTP server. No middleware. Just a file and a hook.

Born out of Pileless (the agent-to-agent workflow platform) and spun off free under the -less brand.


Why

Claude Code, Codex CLI, and similar agentic tools are passive by default - they wait for you to type. If a background job finishes while you're not looking, neither of you notices. waitless flips that: the moment a thing happens, the next tool call surfaces it in the model's attention. You stop being the middleman.

The pattern is deliberately small:

  • The file IS the bus. Every event is one JSON line appended to ~/.waitless/events.jsonl.
  • The hook IS the tap. A PostToolUse hook reads the file on every tool call and surfaces undelivered events as a system-reminder.
  • No server. No port, no auth surface, no subscriber registry, no process to crash.

Install

pip install pileless-waitless

Or vendor the three files in waitless/ directly into your project - no dependencies beyond Python 3.9+.

Wire it into Claude Code

Add to .claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash|Edit|Write|MultiEdit",
        "hooks": [
          { "type": "command", "command": "python -m waitless.tap" }
        ]
      }
    ]
  }
}

That's it. Now anything that writes to ~/.waitless/events.jsonl will tap your session.

Emit events

From Python:

from waitless import emit

emit(
    source="my-build",
    lane="default",
    severity="attention",
    title="deploy finished",
    body="shipped v1.2.3 to prod",
    action_hint="verify the rollout",
)

From the CLI:

python -m waitless.core \
  --source my-build \
  --lane default \
  --severity attention \
  --title "deploy finished" \
  --action-hint "verify the rollout"

Wrap any command so it taps on completion:

# (use waitless.wrap - ships with the package)
python -m waitless.wrap --lane default --source build --title "tests" -- pytest

Severity ladder

You decide how urgent each event is. The hook decides what to do with it:

Severity What happens
info Queue silently; drain at session start
attention Surface to the active session - the model acts on it
emergency Surface + the model acts NOW
operator-needed The only rung that pages the human (via a Telegram bot, webhook relay, or any URL you point it at)

The whole point: the AI handles everything it can. The human only hears about things that genuinely need a human.

Lanes

Events are tagged with a lane (e.g. default, scrape, money, pile). The hook only surfaces events on the active session's lane, so multiple sessions don't cross-talk. Set WAITLESS_LANE=your-lane per session, or write ~/.waitless/lane.json keyed by session id.

Security model

The events file is writable by your user. That means any process running as you can emit events - including, in theory, a malicious one trying to prompt-inject the model via a crafted event title. waitless defends against this:

  • Event titles/bodies are sanitized (brackets + newlines stripped, length-capped).
  • Every surfaced line is labeled (untrusted source=X) so the model knows not to treat event content as a system instruction.

If you want stronger isolation, restrict the events file's permissions to a dedicated user, or run waitless under a separate account.

Self-monitoring

waitless ships with a deadman (waitless.deadman) that checks the tap is actually draining. If a live session ran and still failed to clear a stale attention+ event, or the events file grows unbounded, the deadman emits an operator-needed event that pages the human. It won't page just because nobody's been using their agent - it only fires when a consumer demonstrably ran and the backlog still didn't clear. Wire it as a cron job / scheduled task every 30 minutes.

What's NOT in waitless

  • No HTTP server. (Use ntfy, ifttt, or any webhook relay if you need cross-machine paging.)
  • No real-time interrupt. The tap lands at the next tool boundary, not mid-call. Reviewers agreed this is the right tradeoff.
  • No subscriber registry. Lane env-var is the only per-session state.

License

MIT. Free as in beer and speech. Use it, fork it, sell it, just don't blame us.

Acknowledgements

Spun off from Pileless - if waitless is useful, you'll probably love Pileless for the full agent-to-agent workflow (structured handoffs, approvals, real-time piles). Check it out.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pileless_waitless-0.1.0.tar.gz (11.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pileless_waitless-0.1.0-py3-none-any.whl (14.5 kB view details)

Uploaded Python 3

File details

Details for the file pileless_waitless-0.1.0.tar.gz.

File metadata

  • Download URL: pileless_waitless-0.1.0.tar.gz
  • Upload date:
  • Size: 11.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for pileless_waitless-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b1c59c7aee8e8ad200ceb24ca36d535ba7424bb89c826b6b2494700ac50f664f
MD5 0677f07a7361ec3ccd412393101cc4ad
BLAKE2b-256 568451b5726ff4044a6c2ca70804fe185af84422fbe820d49355718433e934c0

See more details on using hashes here.

File details

Details for the file pileless_waitless-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pileless_waitless-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 45a8ce2bf50b1075e5ab2ecde769d6e71bd23d0b72852a4a79081e5c93a6a37e
MD5 cdf4f4b292147328adf599439930e538
BLAKE2b-256 31cb5727755600bddbc6a70be3a5225b935b87305495b5e33eb0b875754067a3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page