superwait 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- superwait-0.2.0/.gitignore +6 -0
- superwait-0.2.0/CONTRIBUTING.md +21 -0
- superwait-0.2.0/LICENSE +21 -0
- superwait-0.2.0/PKG-INFO +146 -0
- superwait-0.2.0/README.md +132 -0
- superwait-0.2.0/docs/hosts.md +104 -0
- superwait-0.2.0/pyproject.toml +29 -0
- superwait-0.2.0/src/superwait/SKILL.md +95 -0
- superwait-0.2.0/src/superwait/__init__.py +1 -0
- superwait-0.2.0/src/superwait/__main__.py +3 -0
- superwait-0.2.0/src/superwait/cli.py +109 -0
- superwait-0.2.0/src/superwait/engine.py +244 -0
- superwait-0.2.0/src/superwait/hooks.py +102 -0
- superwait-0.2.0/src/superwait/models.py +128 -0
- superwait-0.2.0/src/superwait/server.py +64 -0
- superwait-0.2.0/src/superwait/setup.py +105 -0
- superwait-0.2.0/src/superwait/store.py +113 -0
- superwait-0.2.0/tests/test_ergonomics.py +233 -0
- superwait-0.2.0/tests/test_hooks_setup.py +91 -0
- superwait-0.2.0/tests/test_transport.py +97 -0
- superwait-0.2.0/tests/test_wait.py +212 -0
- superwait-0.2.0/uv.lock +817 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Use Python 3.11+ and uv. From a checkout:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
uv sync --locked
|
|
7
|
+
uv run --locked pytest
|
|
8
|
+
uv build
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The tests use temporary databases, synthetic lifecycle events, local HTTP
|
|
12
|
+
endpoints, subprocesses, and a real stdio MCP client. Keep them independent of
|
|
13
|
+
personal agent accounts, configuration, and conversation history.
|
|
14
|
+
|
|
15
|
+
For integration changes, include a focused regression and state which host
|
|
16
|
+
versions were actually exercised. Keep timeout, cancellation, and partial-result
|
|
17
|
+
behavior explicit. Adapter tests alone do not establish live host compatibility.
|
|
18
|
+
|
|
19
|
+
Submit changes through pull requests. Describe the observable behavior and how
|
|
20
|
+
you checked it. Avoid adding a service or framework when the existing wait
|
|
21
|
+
engine, host feature, or standard library is sufficient.
|
superwait-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Superwait contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
superwait-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: superwait
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Conditional waiting for coding agents
|
|
5
|
+
Project-URL: Repository, https://github.com/ctxrs/superwait
|
|
6
|
+
Project-URL: Issues, https://github.com/ctxrs/superwait/issues
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Requires-Dist: httpx2<3,>=2.13
|
|
11
|
+
Requires-Dist: mcp<3,>=2.2
|
|
12
|
+
Requires-Dist: pydantic<3,>=2.12
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# Superwait
|
|
16
|
+
|
|
17
|
+
Conditional waiting for coding agents. Wait for any, all, or a count of workers
|
|
18
|
+
and other conditions, wake early on a blocker, and keep one deadline across
|
|
19
|
+
interruptions.
|
|
20
|
+
|
|
21
|
+
Superwait provides an MCP tool and CLI, with setup for Codex, Claude Code, and
|
|
22
|
+
Cursor. The agent chooses the workflow; local code checks the conditions.
|
|
23
|
+
There are no model calls inside the wait.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
Requires Python 3.11+ and [uv](https://docs.astral.sh/uv/).
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
uv tool install superwait
|
|
31
|
+
superwait setup codex --project /path/to/project
|
|
32
|
+
# Other hosts:
|
|
33
|
+
superwait setup claude --project /path/to/project
|
|
34
|
+
superwait setup cursor --project /path/to/project
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Setup installs project-local MCP settings, lifecycle hooks, and agent
|
|
38
|
+
instructions. It preserves unrelated settings and backs up changed files as
|
|
39
|
+
`.superwait-backup`. Restart the host and accept its normal trust prompts.
|
|
40
|
+
Install hooks before spawning workers, and keep the Python tool environment
|
|
41
|
+
installed. See [host integration](https://github.com/ctxrs/superwait/blob/main/docs/hosts.md) for details.
|
|
42
|
+
|
|
43
|
+
To install the current source instead, use
|
|
44
|
+
`uv tool install 'git+https://github.com/ctxrs/superwait'`.
|
|
45
|
+
|
|
46
|
+
Linux is tested. Codex has live integration coverage; Claude Code and Cursor
|
|
47
|
+
have adapter tests and still need full live workflow qualification. Other
|
|
48
|
+
operating systems and multi-hour host waits remain unqualified.
|
|
49
|
+
|
|
50
|
+
## Express the wait
|
|
51
|
+
|
|
52
|
+
Call the MCP tool `wait_for` with a `request`. For example, wait for two of three
|
|
53
|
+
reviewers, or return early when an explicit blocker signal arrives:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"request": {
|
|
58
|
+
"agents": ["reviewer-a-id", "reviewer-b-id", "reviewer-c-id"],
|
|
59
|
+
"mode": "quorum",
|
|
60
|
+
"quorum": 2,
|
|
61
|
+
"wake_on": [{"kind": "signal", "key": "review-42/blocker", "state": "blocked"}],
|
|
62
|
+
"timeout": "2h"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Use the native worker IDs from your host. The provider defaults to the host
|
|
68
|
+
selected during setup. Codex task paths such as `/root/reviewer_a` also work
|
|
69
|
+
when accompanied by the parent `session` supplied in its startup context.
|
|
70
|
+
`list_agents` is available for discovery and troubleshooting.
|
|
71
|
+
|
|
72
|
+
`mode` is `all` by default, or `any`, or `quorum` with a count. Add `targets` for
|
|
73
|
+
other conditions; they count toward the same threshold as `agents`.
|
|
74
|
+
|
|
75
|
+
| Condition | Matches when |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `agent` | A lifecycle hook observes a requested worker state |
|
|
78
|
+
| `signal` | A task-specific event is published |
|
|
79
|
+
| `file` | A path exists, is missing, changes, or contains literal text |
|
|
80
|
+
| `http` | A GET returns the requested status code |
|
|
81
|
+
| `command` | An observational command returns the requested exit code |
|
|
82
|
+
|
|
83
|
+
Checks run concurrently. Command probes take an argument array and run without
|
|
84
|
+
a shell; use an observational check because it repeats. `interval` controls
|
|
85
|
+
polling, with a default of one second. Use absolute paths when the MCP server's
|
|
86
|
+
working directory may differ from yours.
|
|
87
|
+
|
|
88
|
+
Publish an explicit checkpoint or blocker through the `signal` MCP tool or CLI:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
superwait signal review-42/blocker --state blocked --data '{"reason":"missing fixture"}'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Act on the result
|
|
95
|
+
|
|
96
|
+
The result includes `status`, `reason`, completed reports in `ready`, remaining
|
|
97
|
+
work in `pending`, and early wake conditions in `triggered`.
|
|
98
|
+
|
|
99
|
+
Pass the returned `continue_wait` object back as the next request to continue.
|
|
100
|
+
It preserves the deadline, remaining count, and file-change baselines, and
|
|
101
|
+
advances past delivered event signals. Persistent conditions such as an existing
|
|
102
|
+
blocker file must clear or be removed before continuing. After a quorum has
|
|
103
|
+
already been reached, continuation waits for all remaining work.
|
|
104
|
+
|
|
105
|
+
A stopped response does not prove an assignment succeeded. Read its report;
|
|
106
|
+
another host hook may continue that worker. For a resumed worker, use an `agent`
|
|
107
|
+
target with `after` set to its last returned `cursor`. Unknown workers stay
|
|
108
|
+
pending. `details: true` adds raw observations for troubleshooting.
|
|
109
|
+
|
|
110
|
+
## Long waits and the CLI
|
|
111
|
+
|
|
112
|
+
Setup configures a per-server MCP timeout of 24 hours plus 30 seconds for Codex
|
|
113
|
+
and Claude Code. `setup --max-wait 3d` changes that ceiling. Each request still
|
|
114
|
+
has its own timeout or timezone-qualified `deadline`. An absolute deadline
|
|
115
|
+
survives continuation; a timed-out continuation remains expired.
|
|
116
|
+
|
|
117
|
+
For long Cursor waits, or waits beyond the host's configured MCP timeout, run
|
|
118
|
+
the same engine through the host's background terminal:
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
superwait wait --request wait.json --output result.json
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`wait.json` contains the request itself, without the MCP `request` wrapper.
|
|
125
|
+
The CLI prints one final JSON result and atomically saves the optional output.
|
|
126
|
+
It exits 0 for matched/early wake, 124 for timeout, 130 for Ctrl-C, and 2 for an
|
|
127
|
+
invalid request or target ambiguity. Native completion notifications or terminal
|
|
128
|
+
collection retrieve the result; the CLI does not background itself.
|
|
129
|
+
|
|
130
|
+
Cancellation stops the wait and any probes it launched. It never cancels the
|
|
131
|
+
workers being observed. The machine and process must remain alive.
|
|
132
|
+
|
|
133
|
+
## Local state
|
|
134
|
+
|
|
135
|
+
Hooks and waiters share a local SQLite database, defaulting to
|
|
136
|
+
`$XDG_STATE_HOME/superwait/events.sqlite3` or
|
|
137
|
+
`~/.local/state/superwait/events.sqlite3`. Set `SUPERWAIT_DB` or `--db` to use
|
|
138
|
+
another store. Stored observations include worker IDs, report excerpts, and
|
|
139
|
+
available transcript paths. `superwait prune --days 30` removes old observations.
|
|
140
|
+
|
|
141
|
+
The Codex task-name adapter reads only the metadata header of the exact worker
|
|
142
|
+
transcript supplied by its hook. It does not scan conversation history.
|
|
143
|
+
Network requests occur only for requested HTTP or command checks.
|
|
144
|
+
|
|
145
|
+
See [contributing](https://github.com/ctxrs/superwait/blob/main/CONTRIBUTING.md)
|
|
146
|
+
for local development. Licensed under [MIT](https://github.com/ctxrs/superwait/blob/main/LICENSE).
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Superwait
|
|
2
|
+
|
|
3
|
+
Conditional waiting for coding agents. Wait for any, all, or a count of workers
|
|
4
|
+
and other conditions, wake early on a blocker, and keep one deadline across
|
|
5
|
+
interruptions.
|
|
6
|
+
|
|
7
|
+
Superwait provides an MCP tool and CLI, with setup for Codex, Claude Code, and
|
|
8
|
+
Cursor. The agent chooses the workflow; local code checks the conditions.
|
|
9
|
+
There are no model calls inside the wait.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
Requires Python 3.11+ and [uv](https://docs.astral.sh/uv/).
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
uv tool install superwait
|
|
17
|
+
superwait setup codex --project /path/to/project
|
|
18
|
+
# Other hosts:
|
|
19
|
+
superwait setup claude --project /path/to/project
|
|
20
|
+
superwait setup cursor --project /path/to/project
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Setup installs project-local MCP settings, lifecycle hooks, and agent
|
|
24
|
+
instructions. It preserves unrelated settings and backs up changed files as
|
|
25
|
+
`.superwait-backup`. Restart the host and accept its normal trust prompts.
|
|
26
|
+
Install hooks before spawning workers, and keep the Python tool environment
|
|
27
|
+
installed. See [host integration](https://github.com/ctxrs/superwait/blob/main/docs/hosts.md) for details.
|
|
28
|
+
|
|
29
|
+
To install the current source instead, use
|
|
30
|
+
`uv tool install 'git+https://github.com/ctxrs/superwait'`.
|
|
31
|
+
|
|
32
|
+
Linux is tested. Codex has live integration coverage; Claude Code and Cursor
|
|
33
|
+
have adapter tests and still need full live workflow qualification. Other
|
|
34
|
+
operating systems and multi-hour host waits remain unqualified.
|
|
35
|
+
|
|
36
|
+
## Express the wait
|
|
37
|
+
|
|
38
|
+
Call the MCP tool `wait_for` with a `request`. For example, wait for two of three
|
|
39
|
+
reviewers, or return early when an explicit blocker signal arrives:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"request": {
|
|
44
|
+
"agents": ["reviewer-a-id", "reviewer-b-id", "reviewer-c-id"],
|
|
45
|
+
"mode": "quorum",
|
|
46
|
+
"quorum": 2,
|
|
47
|
+
"wake_on": [{"kind": "signal", "key": "review-42/blocker", "state": "blocked"}],
|
|
48
|
+
"timeout": "2h"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Use the native worker IDs from your host. The provider defaults to the host
|
|
54
|
+
selected during setup. Codex task paths such as `/root/reviewer_a` also work
|
|
55
|
+
when accompanied by the parent `session` supplied in its startup context.
|
|
56
|
+
`list_agents` is available for discovery and troubleshooting.
|
|
57
|
+
|
|
58
|
+
`mode` is `all` by default, or `any`, or `quorum` with a count. Add `targets` for
|
|
59
|
+
other conditions; they count toward the same threshold as `agents`.
|
|
60
|
+
|
|
61
|
+
| Condition | Matches when |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `agent` | A lifecycle hook observes a requested worker state |
|
|
64
|
+
| `signal` | A task-specific event is published |
|
|
65
|
+
| `file` | A path exists, is missing, changes, or contains literal text |
|
|
66
|
+
| `http` | A GET returns the requested status code |
|
|
67
|
+
| `command` | An observational command returns the requested exit code |
|
|
68
|
+
|
|
69
|
+
Checks run concurrently. Command probes take an argument array and run without
|
|
70
|
+
a shell; use an observational check because it repeats. `interval` controls
|
|
71
|
+
polling, with a default of one second. Use absolute paths when the MCP server's
|
|
72
|
+
working directory may differ from yours.
|
|
73
|
+
|
|
74
|
+
Publish an explicit checkpoint or blocker through the `signal` MCP tool or CLI:
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
superwait signal review-42/blocker --state blocked --data '{"reason":"missing fixture"}'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Act on the result
|
|
81
|
+
|
|
82
|
+
The result includes `status`, `reason`, completed reports in `ready`, remaining
|
|
83
|
+
work in `pending`, and early wake conditions in `triggered`.
|
|
84
|
+
|
|
85
|
+
Pass the returned `continue_wait` object back as the next request to continue.
|
|
86
|
+
It preserves the deadline, remaining count, and file-change baselines, and
|
|
87
|
+
advances past delivered event signals. Persistent conditions such as an existing
|
|
88
|
+
blocker file must clear or be removed before continuing. After a quorum has
|
|
89
|
+
already been reached, continuation waits for all remaining work.
|
|
90
|
+
|
|
91
|
+
A stopped response does not prove an assignment succeeded. Read its report;
|
|
92
|
+
another host hook may continue that worker. For a resumed worker, use an `agent`
|
|
93
|
+
target with `after` set to its last returned `cursor`. Unknown workers stay
|
|
94
|
+
pending. `details: true` adds raw observations for troubleshooting.
|
|
95
|
+
|
|
96
|
+
## Long waits and the CLI
|
|
97
|
+
|
|
98
|
+
Setup configures a per-server MCP timeout of 24 hours plus 30 seconds for Codex
|
|
99
|
+
and Claude Code. `setup --max-wait 3d` changes that ceiling. Each request still
|
|
100
|
+
has its own timeout or timezone-qualified `deadline`. An absolute deadline
|
|
101
|
+
survives continuation; a timed-out continuation remains expired.
|
|
102
|
+
|
|
103
|
+
For long Cursor waits, or waits beyond the host's configured MCP timeout, run
|
|
104
|
+
the same engine through the host's background terminal:
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
superwait wait --request wait.json --output result.json
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`wait.json` contains the request itself, without the MCP `request` wrapper.
|
|
111
|
+
The CLI prints one final JSON result and atomically saves the optional output.
|
|
112
|
+
It exits 0 for matched/early wake, 124 for timeout, 130 for Ctrl-C, and 2 for an
|
|
113
|
+
invalid request or target ambiguity. Native completion notifications or terminal
|
|
114
|
+
collection retrieve the result; the CLI does not background itself.
|
|
115
|
+
|
|
116
|
+
Cancellation stops the wait and any probes it launched. It never cancels the
|
|
117
|
+
workers being observed. The machine and process must remain alive.
|
|
118
|
+
|
|
119
|
+
## Local state
|
|
120
|
+
|
|
121
|
+
Hooks and waiters share a local SQLite database, defaulting to
|
|
122
|
+
`$XDG_STATE_HOME/superwait/events.sqlite3` or
|
|
123
|
+
`~/.local/state/superwait/events.sqlite3`. Set `SUPERWAIT_DB` or `--db` to use
|
|
124
|
+
another store. Stored observations include worker IDs, report excerpts, and
|
|
125
|
+
available transcript paths. `superwait prune --days 30` removes old observations.
|
|
126
|
+
|
|
127
|
+
The Codex task-name adapter reads only the metadata header of the exact worker
|
|
128
|
+
transcript supplied by its hook. It does not scan conversation history.
|
|
129
|
+
Network requests occur only for requested HTTP or command checks.
|
|
130
|
+
|
|
131
|
+
See [contributing](https://github.com/ctxrs/superwait/blob/main/CONTRIBUTING.md)
|
|
132
|
+
for local development. Licensed under [MIT](https://github.com/ctxrs/superwait/blob/main/LICENSE).
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Host integration
|
|
2
|
+
|
|
3
|
+
All three hosts use the same `wait_for`, `list_agents`, and `signal` MCP API.
|
|
4
|
+
The CLI runs the same wait engine. Setup writes only to the project you name.
|
|
5
|
+
|
|
6
|
+
| Host | MCP configuration | Lifecycle hook configuration | Skill |
|
|
7
|
+
| --- | --- | --- | --- |
|
|
8
|
+
| Codex | `.codex/config.toml` | `.codex/hooks.json` | `.agents/skills/superwait/SKILL.md` |
|
|
9
|
+
| Claude Code | `.mcp.json` | `.claude/settings.json` | `.claude/skills/superwait/SKILL.md` |
|
|
10
|
+
| Cursor | `.cursor/mcp.json` | `.cursor/hooks.json` | `.cursor/skills/superwait/SKILL.md` |
|
|
11
|
+
|
|
12
|
+
## Codex
|
|
13
|
+
|
|
14
|
+
`SubagentStart` and `SubagentStop` supply the agent ID and parent session ID.
|
|
15
|
+
Superwait records the last assistant message when present. A stop hook observes
|
|
16
|
+
a response ending before other stop hooks necessarily finish deciding whether
|
|
17
|
+
to continue it. It is not a durable promise that the agent will never run again.
|
|
18
|
+
|
|
19
|
+
Some Codex runtimes return a task path such as `/root/reviewer` from spawning,
|
|
20
|
+
but lifecycle hooks report a UUID. Both can now be passed directly to `agents`.
|
|
21
|
+
At the stop hook, Superwait reads only the first metadata record of the
|
|
22
|
+
exact worker transcript Codex supplies. It validates both worker and parent IDs
|
|
23
|
+
before recording the task path. This metadata adapter supports the Codex 0.153.1
|
|
24
|
+
header format. It reads no conversation body and scans
|
|
25
|
+
no history. It is version-dependent; absent or unsupported metadata leaves
|
|
26
|
+
UUID-based waiting and explicit signals available.
|
|
27
|
+
|
|
28
|
+
A previously unknown task path remains pending until that mapping arrives.
|
|
29
|
+
Task paths require `session` even if only one old mapping exists: an unscoped
|
|
30
|
+
name could otherwise match a previous conversation before the new worker stops.
|
|
31
|
+
Setup installs a SessionStart hook that supplies this context to Codex; it
|
|
32
|
+
makes no permission decisions. The CLI fills missing scope from CODEX_THREAD_ID
|
|
33
|
+
only for task paths. Explicit session values and ordinary UUIDs are preserved.
|
|
34
|
+
Duplicate or ambiguous references return an actionable error. `list_agents`
|
|
35
|
+
remains available for discovery and diagnosis, with UUIDs in `key` and observed
|
|
36
|
+
task paths in `data.aliases`.
|
|
37
|
+
|
|
38
|
+
Setup sets `tool_timeout_sec` to the desired maximum wait plus 30 seconds.
|
|
39
|
+
The server sends MCP progress keepalives during normal polling; these do not
|
|
40
|
+
override the client's hard timeout. The tool remains cancellable.
|
|
41
|
+
|
|
42
|
+
For noninteractive `codex exec`, first review and trust the hooks with `/hooks`
|
|
43
|
+
in the same project and Codex home. A `never` approval policy cannot approve
|
|
44
|
+
MCP calls that still require a prompt. After reviewing this server's access,
|
|
45
|
+
configure `tools.wait_for.approval_mode = "approve"` and the other needed tools
|
|
46
|
+
under `[mcp_servers.superwait]`, or use that server's
|
|
47
|
+
`default_tools_approval_mode = "approve"`. Setup leaves that decision to the
|
|
48
|
+
host's normal approval flow. This approval includes repeated command and HTTP
|
|
49
|
+
probes with the server process's access.
|
|
50
|
+
|
|
51
|
+
Sources: [hooks](https://developers.openai.com/codex/hooks),
|
|
52
|
+
[MCP settings](https://learn.chatgpt.com/docs/extend/mcp),
|
|
53
|
+
[skill discovery](https://learn.chatgpt.com/docs/build-skills).
|
|
54
|
+
|
|
55
|
+
## Claude Code
|
|
56
|
+
|
|
57
|
+
The same lifecycle event names provide `agent_id` and `session_id`. Setup also
|
|
58
|
+
observes `PostToolUse` for `SubagentHandback` because newer versions can deliver
|
|
59
|
+
the actual report there; the final assistant text may only be a closing note.
|
|
60
|
+
The report does not itself mark the agent as stopped.
|
|
61
|
+
|
|
62
|
+
The per-server `timeout` is in milliseconds. Current Claude Code can background
|
|
63
|
+
long MCP calls and notify on completion; its wall-clock limit still applies.
|
|
64
|
+
The CLI can also run under Bash or Monitor. Monitor receives its final JSON line
|
|
65
|
+
when the wait returns. A killed subagent may never emit a stop event, so retain
|
|
66
|
+
the wait's deadline and check native status if needed.
|
|
67
|
+
|
|
68
|
+
Sources: [hook payloads](https://code.claude.com/docs/en/hooks#subagentstop),
|
|
69
|
+
[MCP timeouts/backgrounding](https://code.claude.com/docs/en/mcp),
|
|
70
|
+
[Monitor](https://code.claude.com/docs/en/tools-reference#monitor-tool).
|
|
71
|
+
|
|
72
|
+
## Cursor
|
|
73
|
+
|
|
74
|
+
`subagentStart` includes `subagent_id` and `parent_conversation_id`.
|
|
75
|
+
The documented `subagentStop` payload can omit both. Superwait uses the common
|
|
76
|
+
conversation ID and matches the exact task/type fingerprint only when there is
|
|
77
|
+
one running candidate in that parent. Concurrent identical tasks remain
|
|
78
|
+
unmatched. No timing heuristic guesses which agent finished.
|
|
79
|
+
|
|
80
|
+
For these cases, assign unique outcome signal keys in worker instructions.
|
|
81
|
+
Signals also work for checkpoints and blockers in every host. Cursor's explicit
|
|
82
|
+
`error` and `aborted` stop states are preserved when correlated.
|
|
83
|
+
|
|
84
|
+
The public MCP documentation does not specify a supported long-call timeout
|
|
85
|
+
setting. Setup does not invent one. Prefer the CLI through the background
|
|
86
|
+
terminal for hours-long waits. A live Cursor build is needed to qualify its
|
|
87
|
+
background completion delivery and exact hook payloads.
|
|
88
|
+
|
|
89
|
+
Sources: [hooks](https://cursor.com/docs/hooks),
|
|
90
|
+
[MCP](https://cursor.com/docs/mcp),
|
|
91
|
+
[subagents](https://cursor.com/docs/subagents).
|
|
92
|
+
|
|
93
|
+
## Permissions and lifecycle
|
|
94
|
+
|
|
95
|
+
The tool does not bypass host permissions. MCP/terminal/hook trust is still
|
|
96
|
+
owned by the host. A predicate is an operation: HTTP requests and command probes
|
|
97
|
+
use the access of the local server process, so authorize it as you would other
|
|
98
|
+
local MCP tools. Use observational probes because they run repeatedly.
|
|
99
|
+
|
|
100
|
+
Signals are local observations, not messages to other tasks. No hosted service,
|
|
101
|
+
cloud account, or model endpoint is involved. Hooks and CLI use one shared
|
|
102
|
+
SQLite store. No provider database is accessed. The Codex task-name adapter
|
|
103
|
+
reads the metadata header described above; Claude and Cursor use their
|
|
104
|
+
documented hook fields.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "superwait"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Conditional waiting for coding agents"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
dependencies = ["mcp>=2.2,<3", "httpx2>=2.13,<3", "pydantic>=2.12,<3"]
|
|
13
|
+
|
|
14
|
+
[project.urls]
|
|
15
|
+
Repository = "https://github.com/ctxrs/superwait"
|
|
16
|
+
Issues = "https://github.com/ctxrs/superwait/issues"
|
|
17
|
+
|
|
18
|
+
[project.scripts]
|
|
19
|
+
superwait = "superwait.cli:main"
|
|
20
|
+
|
|
21
|
+
[dependency-groups]
|
|
22
|
+
dev = ["pytest>=8", "pytest-asyncio>=1"]
|
|
23
|
+
|
|
24
|
+
[tool.pytest.ini_options]
|
|
25
|
+
asyncio_mode = "auto"
|
|
26
|
+
testpaths = ["tests"]
|
|
27
|
+
|
|
28
|
+
[tool.hatch.build.targets.wheel]
|
|
29
|
+
packages = ["src/superwait"]
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: superwait
|
|
3
|
+
description: Wait for agents and external conditions together, with any/all/quorum, early wake conditions, long deadlines, and useful partial results.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Superwait
|
|
7
|
+
|
|
8
|
+
Express when you want to wake. You choose the workflow; native tools remain
|
|
9
|
+
available for spawning, steering, or collecting additional results.
|
|
10
|
+
|
|
11
|
+
## One request
|
|
12
|
+
|
|
13
|
+
Call wait_for with a request. For example, wait for two reviewers, wake early
|
|
14
|
+
on a blocker, and stop after two hours:
|
|
15
|
+
|
|
16
|
+
~~~json
|
|
17
|
+
{
|
|
18
|
+
"agents": ["/root/reviewer_a", "/root/reviewer_b", "/root/reviewer_c"],
|
|
19
|
+
"session": "parent-session-from-startup-context",
|
|
20
|
+
"mode": "quorum",
|
|
21
|
+
"quorum": 2,
|
|
22
|
+
"wake_on": [{"kind": "signal", "key": "review-42/blocker", "state": "blocked", "label": "Review blocked"}],
|
|
23
|
+
"timeout": "2h"
|
|
24
|
+
}
|
|
25
|
+
~~~
|
|
26
|
+
|
|
27
|
+
agents accepts native IDs and Codex task paths directly. The installed host
|
|
28
|
+
is the default provider; set provider for a different host. Hooks must be
|
|
29
|
+
installed before spawning. Codex paths become resolvable when its stop hook
|
|
30
|
+
supplies the worker metadata. Codex task paths require the parent session from
|
|
31
|
+
the SessionStart context supplied by setup; this prevents matching an old
|
|
32
|
+
conversation with the same names. The Codex CLI fallback fills it from the
|
|
33
|
+
calling thread's environment. Native UUIDs need no extra scope.
|
|
34
|
+
list_agents is available for discovery or troubleshooting, not a prerequisite.
|
|
35
|
+
|
|
36
|
+
mode is all by default, or any, or quorum with a count. Add targets
|
|
37
|
+
for other conditions; they and agents count toward the same threshold.
|
|
38
|
+
wake_on returns early independently of that threshold. Add label to any
|
|
39
|
+
condition to make its meaning explicit in the result.
|
|
40
|
+
|
|
41
|
+
~~~json
|
|
42
|
+
{"kind":"file","path":"/workspace/ci-failed.json","label":"CI failed"}
|
|
43
|
+
{"kind":"http","url":"http://localhost:8080/health","status":200}
|
|
44
|
+
{"kind":"signal","key":"build-42","state":"ready"}
|
|
45
|
+
{"kind":"file","path":"/workspace/server.log","event":"contains","text":"Ready"}
|
|
46
|
+
{"kind":"command","argv":["python3","/workspace/check_ci.py"],"exit_code":0}
|
|
47
|
+
~~~
|
|
48
|
+
|
|
49
|
+
Files support exists (default), missing, changed, and literal contains.
|
|
50
|
+
HTTP matches the status code. Command probes run repeatedly without a shell;
|
|
51
|
+
use an observational check, not the build/deploy itself. Prefer absolute paths.
|
|
52
|
+
interval controls polling, default 1 second; increase it for remote services.
|
|
53
|
+
|
|
54
|
+
## Use the result
|
|
55
|
+
|
|
56
|
+
- status: matched, interrupted, timed_out, or error.
|
|
57
|
+
- reason and triggered: why the wait ended and which early condition fired.
|
|
58
|
+
- ready: observed results and event cursors; pending: remaining work/errors.
|
|
59
|
+
- continue_wait: a ready-to-use request for the remaining work, or null.
|
|
60
|
+
|
|
61
|
+
After handling an interruption, pass continue_wait back as the next request
|
|
62
|
+
if you want to continue. It retains the absolute deadline and file-change
|
|
63
|
+
baselines, carries the remaining quorum, and consumes delivered event signals.
|
|
64
|
+
Persistent conditions such as an existing blocker file must clear or be removed
|
|
65
|
+
from wake_on before proceeding. After reaching a quorum, continuation waits
|
|
66
|
+
for all remaining work. It does not repeat reports you already received.
|
|
67
|
+
|
|
68
|
+
timeout accepts 30s, 10m, 2h, or 1d. A timezone-qualified deadline
|
|
69
|
+
overrides it. A timed-out continuation remains expired; choose a new deadline
|
|
70
|
+
explicitly if you want more time. For a resumed worker, use an agent target
|
|
71
|
+
with after set to its last returned cursor to await a new response:
|
|
72
|
+
|
|
73
|
+
~~~json
|
|
74
|
+
{"session":"parent-session","targets":[{"kind":"agent","provider":"codex","id":"/root/reviewer_a","after":42}],"timeout":"2h"}
|
|
75
|
+
~~~
|
|
76
|
+
|
|
77
|
+
A stopped response is not proof the assignment passed; read its result. Another
|
|
78
|
+
stop hook can continue a worker. Unknown workers stay pending. Cursor stop
|
|
79
|
+
hooks sometimes lack enough identity: use an explicit unique outcome signal
|
|
80
|
+
for indistinguishable concurrent assignments. Signal keys should identify the
|
|
81
|
+
particular task; existing matching signals count unless after excludes them.
|
|
82
|
+
Use signal(key, state, data) for explicit checkpoints and outcomes.
|
|
83
|
+
|
|
84
|
+
## Long waits and cancellation
|
|
85
|
+
|
|
86
|
+
Setup configures Codex/Claude's MCP timeout for 24 hours by default. For waits
|
|
87
|
+
beyond the configured host limit, or long Cursor waits, use the same request
|
|
88
|
+
with superwait wait --request wait.json --output result.json in the host's
|
|
89
|
+
background terminal. The CLI emits one final result; normal host notifications
|
|
90
|
+
or native collection retrieve it. It does not wake a closed conversation.
|
|
91
|
+
|
|
92
|
+
Cancel the MCP call or send Ctrl-C to the CLI to stop waiting. The observed
|
|
93
|
+
workers continue; only the wait's own probes are cleaned up. The process and
|
|
94
|
+
machine must stay alive. CLI exits: 0 matched/interrupted, 124 timeout,
|
|
95
|
+
130 cancelled, 2 error. details: true adds raw observations for diagnosis.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Conditional waits with no model calls between observations."""
|