ai-hats-client 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.
- ai_hats_client-0.2.0/.gitignore +54 -0
- ai_hats_client-0.2.0/CHANGELOG.md +45 -0
- ai_hats_client-0.2.0/LICENSE +21 -0
- ai_hats_client-0.2.0/PKG-INFO +116 -0
- ai_hats_client-0.2.0/README.md +95 -0
- ai_hats_client-0.2.0/pyproject.toml +35 -0
- ai_hats_client-0.2.0/src/ai_hats_client/__init__.py +35 -0
- ai_hats_client-0.2.0/src/ai_hats_client/py.typed +0 -0
- ai_hats_client-0.2.0/src/ai_hats_client/session.py +519 -0
- ai_hats_client-0.2.0/src/ai_hats_client/testing/__init__.py +5 -0
- ai_hats_client-0.2.0/src/ai_hats_client/testing/stub_claude.py +865 -0
- ai_hats_client-0.2.0/tests/fake_holder.py +77 -0
- ai_hats_client-0.2.0/tests/test_client_import_hygiene.py +43 -0
- ai_hats_client-0.2.0/tests/test_client_session.py +126 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
_version.py
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.egg-info/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
.eggs/
|
|
9
|
+
*.egg
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
.env
|
|
13
|
+
*.log
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.coverage
|
|
18
|
+
.coverage.*
|
|
19
|
+
coverage.xml
|
|
20
|
+
htmlcov/
|
|
21
|
+
|
|
22
|
+
# macOS finder metadata
|
|
23
|
+
.DS_Store
|
|
24
|
+
|
|
25
|
+
# uv.lock — local dev tool artifact, ai-hats is a library installed via pip
|
|
26
|
+
uv.lock
|
|
27
|
+
|
|
28
|
+
# Real-session fixtures — recorded from a developer's local Claude Code session,
|
|
29
|
+
# contain absolute home paths, real sessionId/requestId, subscription metadata,
|
|
30
|
+
# and unredacted user prompts. Tests that consume them auto-skip when absent.
|
|
31
|
+
# Generate locally for debugging; never commit. See HATS-343.
|
|
32
|
+
tests/fixtures/real_conversation.jsonl
|
|
33
|
+
tests/fixtures/real_trace.log
|
|
34
|
+
|
|
35
|
+
# ai-hats runtime (generated per-project)
|
|
36
|
+
.agent/
|
|
37
|
+
.githooks/
|
|
38
|
+
.gitlog/
|
|
39
|
+
ai-hats.yaml
|
|
40
|
+
profile.json # legacy, migrated to ai-hats.yaml
|
|
41
|
+
profile.json.bak
|
|
42
|
+
GEMINI.md
|
|
43
|
+
CLAUDE.md
|
|
44
|
+
.claude/skills/
|
|
45
|
+
.claude/scheduled_tasks.lock
|
|
46
|
+
.claude/settings.local.json
|
|
47
|
+
.claude/settings.json
|
|
48
|
+
.envrc
|
|
49
|
+
.gemini/
|
|
50
|
+
.cline/skills/
|
|
51
|
+
.cline/plugins/
|
|
52
|
+
.agy/
|
|
53
|
+
|
|
54
|
+
.agent/ai-hats/
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `ai-hats-client` are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres
|
|
5
|
+
to [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.2.0]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- The stub claude (`ai_hats_client.testing`) follows claude 2.1.283 further:
|
|
12
|
+
- every prompt line gets `command_lifecycle` on the wire (`queued`, `started`,
|
|
13
|
+
`completed`); a prompt folded into a running turn is echoed before it is
|
|
14
|
+
started;
|
|
15
|
+
- slash commands: `/model`, `/context`, `/usage` and `/rename` are answered by
|
|
16
|
+
a `<synthetic>` response before their echo, and `/tui`, `/login` and the
|
|
17
|
+
other refused ones get "isn't available in this environment" and no echo;
|
|
18
|
+
- `/clear` writes `conversation_reset`, then goes on under a new session id
|
|
19
|
+
and in a new transcript, and forgets the previous prompt.
|
|
20
|
+
|
|
21
|
+
## [0.1.0]
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- `HeadlessSession`: start `ai-hats headless`, read its `headless/v1` header, send
|
|
26
|
+
a `prompt`, wait for the `turn_ended` that carries the prompt's id, and close or
|
|
27
|
+
abort the session. Every wait is bounded.
|
|
28
|
+
- `answer(call_id, decision, message=, answers=)` decides a question the
|
|
29
|
+
session put; `interrupt()` stops the running turn and keeps the session.
|
|
30
|
+
- A turn wait takes `on_question`. A question stays offered until it is answered,
|
|
31
|
+
its handler returns, or its call gets a result. With no handler the wait raises
|
|
32
|
+
`QuestionPending` instead of waiting out its bound. `tool_call(call_id)` returns
|
|
33
|
+
the call a question is about.
|
|
34
|
+
- A command the holder would refuse raises `ValueError` before it is sent; one the
|
|
35
|
+
holder does not run (per `header.commands`), or any command after the session
|
|
36
|
+
ended, raises `HeadlessError`.
|
|
37
|
+
- Leaving a `with` block terminates a holder that does not end within
|
|
38
|
+
`close_timeout`, then raises the timeout.
|
|
39
|
+
- Typed: the wheel carries `py.typed`.
|
|
40
|
+
- `ai_hats_client.testing`: a stand-in `claude` binary that speaks the
|
|
41
|
+
stream-json wire, so tests of a client run with no model and no login. It asks
|
|
42
|
+
questions (`@ask`, `@askq`, `@plan`) and runs long turns an interrupt can cut
|
|
43
|
+
(`@slow`, `@slowtool`). It drifts off the wire (`@drift`), hits the quota
|
|
44
|
+
(`@quota <status>`, reset at `QUOTA_RESETS_AT`), dies by a signal (`@kill`,
|
|
45
|
+
`@ignore-term`), and reports itself logged out under `LOGGED_OUT_ENV`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 muratovv
|
|
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.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ai-hats-client
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Drive an ai-hats headless session over its stdin and stdout: turns, answers to its questions, interrupts.
|
|
5
|
+
Project-URL: Homepage, https://github.com/muratovv/ai-hats
|
|
6
|
+
Project-URL: Repository, https://github.com/muratovv/ai-hats
|
|
7
|
+
Project-URL: Changelog, https://github.com/muratovv/ai-hats/blob/master/packages/ai-hats-client/CHANGELOG.md
|
|
8
|
+
Author-email: muratovv <f@muratovv.me>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai-hats,claude,client,headless,session
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: POSIX
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.13
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# ai-hats-client
|
|
23
|
+
|
|
24
|
+
Drive an `ai-hats headless` session from a program: send turns, wait for each
|
|
25
|
+
one to end, and read everything the session did.
|
|
26
|
+
|
|
27
|
+
`ai-hats headless` runs a role's interactive session with no terminal. Its stdin
|
|
28
|
+
takes commands, one JSON object per line (`commands/v1`). Its stdout opens with a
|
|
29
|
+
header line (`headless/v1`) and then copies the session's event log byte for
|
|
30
|
+
byte (`events/v1`). Its exit code says how the session ended. This package speaks
|
|
31
|
+
that contract and nothing else. It uses only the standard library and imports
|
|
32
|
+
nothing from ai-hats.
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from ai_hats_client import HeadlessSession
|
|
36
|
+
|
|
37
|
+
with HeadlessSession.start(["ai-hats", "headless", "-r", "maintainer"]) as s:
|
|
38
|
+
first = s.turn("remember the word KIWI")
|
|
39
|
+
second = s.turn("which word?")
|
|
40
|
+
end = s.close()
|
|
41
|
+
assert "KIWI" in second.text and end.code == 0
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- `HeadlessSession.start(argv, cwd=, env=)` launches the holder and reads the
|
|
45
|
+
header. If the holder refuses before the start, it raises `SessionEnded`, which
|
|
46
|
+
carries the exit code.
|
|
47
|
+
- `prompt(text, id=None)` sends a turn and returns its id. `turn_for(id)` waits
|
|
48
|
+
for the `turn_ended` event that lists that id. `turn(text)` does both.
|
|
49
|
+
- `close()` closes stdin, which tells the holder to finish its turns and exit. It
|
|
50
|
+
then reads to the end and returns an `Exit` with the code, every event and the
|
|
51
|
+
holder's stderr. `terminate()` signals the holder instead.
|
|
52
|
+
- Leaving a `with` block closes the session. If the session does not end within
|
|
53
|
+
`start(..., close_timeout=60)`, the holder is terminated and the
|
|
54
|
+
`HeadlessTimeout` is raised, so no session keeps spending turns.
|
|
55
|
+
|
|
56
|
+
Every wait has a bound. Every error carries what was read so far and the tail of
|
|
57
|
+
the holder's stderr.
|
|
58
|
+
|
|
59
|
+
- A command the holder would refuse raises `ValueError` before it is sent: empty
|
|
60
|
+
text, an `id` that is not a lowercase canonical UUID, a `decision` other than
|
|
61
|
+
`allow` or `deny`.
|
|
62
|
+
- A command the holder does not run raises `HeadlessError`, and so does any
|
|
63
|
+
command after the session has ended. The holder lists what it runs in
|
|
64
|
+
`header.commands`.
|
|
65
|
+
- `prompt()`, `answer()` and `interrupt()` may be called from another thread, for
|
|
66
|
+
instance a watchdog that interrupts a turn while the main thread waits on it.
|
|
67
|
+
The waits themselves belong to one thread.
|
|
68
|
+
|
|
69
|
+
## Questions and interrupts
|
|
70
|
+
|
|
71
|
+
A session asks its stdin owner what it would ask a person at the terminal: a
|
|
72
|
+
gate's `ask`, a consent point, claude's own permission prompt, leaving plan mode,
|
|
73
|
+
the model's own question. Each question arrives as a `person_asked` event with a
|
|
74
|
+
`call_id`, and the call waits for your answer.
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
def decide(question):
|
|
78
|
+
s.answer(question["call_id"], "allow") # or "deny", message="why"
|
|
79
|
+
|
|
80
|
+
turn = s.turn("push the branch", on_question=decide)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- A turn wait hands each question to `on_question`. Without a handler it
|
|
84
|
+
raises `QuestionPending` instead of waiting out its bound. Answer
|
|
85
|
+
`pending.question["call_id"]` and wait again: nothing read so far is lost.
|
|
86
|
+
- `answer(call_id, "allow", answers={"<question>": "<answer>"})` answers a
|
|
87
|
+
question the model asked with `AskUserQuestion`. `tool_call(call_id)` returns
|
|
88
|
+
the call the question is about, with the questions and their options in its
|
|
89
|
+
`input`.
|
|
90
|
+
- A question stays offered until you answer it, its handler returns, or its call
|
|
91
|
+
gets a result: a handler that raised sees it again on the next wait.
|
|
92
|
+
- The first answer on a call id wins. The holder refuses the rest and logs a
|
|
93
|
+
`command_rejected` signal for each.
|
|
94
|
+
- `interrupt()` stops the running turn and keeps the session. The turn still
|
|
95
|
+
ends with its `turn_ended`, and a question it had open is closed.
|
|
96
|
+
|
|
97
|
+
The holder never times out a question. If you will not wait, answer `deny`.
|
|
98
|
+
|
|
99
|
+
The client is synchronous. A caller that needs asyncio runs it in a thread.
|
|
100
|
+
|
|
101
|
+
## Testing without a model
|
|
102
|
+
|
|
103
|
+
`ai_hats_client.testing` has a stand-in `claude` binary that speaks the same wire.
|
|
104
|
+
Tests that use it need no model and no login:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
import os
|
|
108
|
+
|
|
109
|
+
from ai_hats_client.testing import install
|
|
110
|
+
|
|
111
|
+
stub = install(tmp_path) # writes tmp_path/stub-bin/claude
|
|
112
|
+
env = {**os.environ, **stub.env(os.environ["PATH"])}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Each prompt's text picks what the stub does. The directives are listed in the
|
|
116
|
+
module docstring of `ai_hats_client.testing.stub_claude`.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# ai-hats-client
|
|
2
|
+
|
|
3
|
+
Drive an `ai-hats headless` session from a program: send turns, wait for each
|
|
4
|
+
one to end, and read everything the session did.
|
|
5
|
+
|
|
6
|
+
`ai-hats headless` runs a role's interactive session with no terminal. Its stdin
|
|
7
|
+
takes commands, one JSON object per line (`commands/v1`). Its stdout opens with a
|
|
8
|
+
header line (`headless/v1`) and then copies the session's event log byte for
|
|
9
|
+
byte (`events/v1`). Its exit code says how the session ended. This package speaks
|
|
10
|
+
that contract and nothing else. It uses only the standard library and imports
|
|
11
|
+
nothing from ai-hats.
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from ai_hats_client import HeadlessSession
|
|
15
|
+
|
|
16
|
+
with HeadlessSession.start(["ai-hats", "headless", "-r", "maintainer"]) as s:
|
|
17
|
+
first = s.turn("remember the word KIWI")
|
|
18
|
+
second = s.turn("which word?")
|
|
19
|
+
end = s.close()
|
|
20
|
+
assert "KIWI" in second.text and end.code == 0
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- `HeadlessSession.start(argv, cwd=, env=)` launches the holder and reads the
|
|
24
|
+
header. If the holder refuses before the start, it raises `SessionEnded`, which
|
|
25
|
+
carries the exit code.
|
|
26
|
+
- `prompt(text, id=None)` sends a turn and returns its id. `turn_for(id)` waits
|
|
27
|
+
for the `turn_ended` event that lists that id. `turn(text)` does both.
|
|
28
|
+
- `close()` closes stdin, which tells the holder to finish its turns and exit. It
|
|
29
|
+
then reads to the end and returns an `Exit` with the code, every event and the
|
|
30
|
+
holder's stderr. `terminate()` signals the holder instead.
|
|
31
|
+
- Leaving a `with` block closes the session. If the session does not end within
|
|
32
|
+
`start(..., close_timeout=60)`, the holder is terminated and the
|
|
33
|
+
`HeadlessTimeout` is raised, so no session keeps spending turns.
|
|
34
|
+
|
|
35
|
+
Every wait has a bound. Every error carries what was read so far and the tail of
|
|
36
|
+
the holder's stderr.
|
|
37
|
+
|
|
38
|
+
- A command the holder would refuse raises `ValueError` before it is sent: empty
|
|
39
|
+
text, an `id` that is not a lowercase canonical UUID, a `decision` other than
|
|
40
|
+
`allow` or `deny`.
|
|
41
|
+
- A command the holder does not run raises `HeadlessError`, and so does any
|
|
42
|
+
command after the session has ended. The holder lists what it runs in
|
|
43
|
+
`header.commands`.
|
|
44
|
+
- `prompt()`, `answer()` and `interrupt()` may be called from another thread, for
|
|
45
|
+
instance a watchdog that interrupts a turn while the main thread waits on it.
|
|
46
|
+
The waits themselves belong to one thread.
|
|
47
|
+
|
|
48
|
+
## Questions and interrupts
|
|
49
|
+
|
|
50
|
+
A session asks its stdin owner what it would ask a person at the terminal: a
|
|
51
|
+
gate's `ask`, a consent point, claude's own permission prompt, leaving plan mode,
|
|
52
|
+
the model's own question. Each question arrives as a `person_asked` event with a
|
|
53
|
+
`call_id`, and the call waits for your answer.
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
def decide(question):
|
|
57
|
+
s.answer(question["call_id"], "allow") # or "deny", message="why"
|
|
58
|
+
|
|
59
|
+
turn = s.turn("push the branch", on_question=decide)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- A turn wait hands each question to `on_question`. Without a handler it
|
|
63
|
+
raises `QuestionPending` instead of waiting out its bound. Answer
|
|
64
|
+
`pending.question["call_id"]` and wait again: nothing read so far is lost.
|
|
65
|
+
- `answer(call_id, "allow", answers={"<question>": "<answer>"})` answers a
|
|
66
|
+
question the model asked with `AskUserQuestion`. `tool_call(call_id)` returns
|
|
67
|
+
the call the question is about, with the questions and their options in its
|
|
68
|
+
`input`.
|
|
69
|
+
- A question stays offered until you answer it, its handler returns, or its call
|
|
70
|
+
gets a result: a handler that raised sees it again on the next wait.
|
|
71
|
+
- The first answer on a call id wins. The holder refuses the rest and logs a
|
|
72
|
+
`command_rejected` signal for each.
|
|
73
|
+
- `interrupt()` stops the running turn and keeps the session. The turn still
|
|
74
|
+
ends with its `turn_ended`, and a question it had open is closed.
|
|
75
|
+
|
|
76
|
+
The holder never times out a question. If you will not wait, answer `deny`.
|
|
77
|
+
|
|
78
|
+
The client is synchronous. A caller that needs asyncio runs it in a thread.
|
|
79
|
+
|
|
80
|
+
## Testing without a model
|
|
81
|
+
|
|
82
|
+
`ai_hats_client.testing` has a stand-in `claude` binary that speaks the same wire.
|
|
83
|
+
Tests that use it need no model and no login:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
import os
|
|
87
|
+
|
|
88
|
+
from ai_hats_client.testing import install
|
|
89
|
+
|
|
90
|
+
stub = install(tmp_path) # writes tmp_path/stub-bin/claude
|
|
91
|
+
env = {**os.environ, **stub.env(os.environ["PATH"])}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Each prompt's text picks what the stub does. The directives are listed in the
|
|
95
|
+
module docstring of `ai_hats_client.testing.stub_claude`.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ai-hats-client"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Drive an ai-hats headless session over its stdin and stdout: turns, answers to its questions, interrupts."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "muratovv", email = "f@muratovv.me" }, # ai-hats: allow-secret
|
|
15
|
+
]
|
|
16
|
+
keywords = ["headless", "session", "client", "claude", "ai-hats"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 4 - Beta",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Operating System :: POSIX",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
24
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
25
|
+
]
|
|
26
|
+
# Standard library only: a client relies on the holder's pipes and exit code, never on ai-hats.
|
|
27
|
+
dependencies = []
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/muratovv/ai-hats"
|
|
31
|
+
Repository = "https://github.com/muratovv/ai-hats"
|
|
32
|
+
Changelog = "https://github.com/muratovv/ai-hats/blob/master/packages/ai-hats-client/CHANGELOG.md"
|
|
33
|
+
|
|
34
|
+
[tool.hatch.build.targets.wheel]
|
|
35
|
+
packages = ["src/ai_hats_client"]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Drive an ``ai-hats headless`` session through its pipes, knowing nothing but its contract."""
|
|
2
|
+
|
|
3
|
+
from .session import (
|
|
4
|
+
Event,
|
|
5
|
+
Exit,
|
|
6
|
+
Header,
|
|
7
|
+
HeadlessError,
|
|
8
|
+
HeadlessSession,
|
|
9
|
+
HeadlessTimeout,
|
|
10
|
+
OnQuestion,
|
|
11
|
+
ProtocolError,
|
|
12
|
+
QuestionPending,
|
|
13
|
+
SessionEnded,
|
|
14
|
+
Turn,
|
|
15
|
+
answer_command,
|
|
16
|
+
interrupt_command,
|
|
17
|
+
prompt_command,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"Event",
|
|
22
|
+
"Exit",
|
|
23
|
+
"Header",
|
|
24
|
+
"HeadlessError",
|
|
25
|
+
"HeadlessSession",
|
|
26
|
+
"HeadlessTimeout",
|
|
27
|
+
"OnQuestion",
|
|
28
|
+
"ProtocolError",
|
|
29
|
+
"QuestionPending",
|
|
30
|
+
"SessionEnded",
|
|
31
|
+
"Turn",
|
|
32
|
+
"answer_command",
|
|
33
|
+
"interrupt_command",
|
|
34
|
+
"prompt_command",
|
|
35
|
+
]
|
|
File without changes
|