wisp-ai 0.1.0a1__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.
Files changed (130) hide show
  1. wisp_ai-0.1.0a1/LICENSE +21 -0
  2. wisp_ai-0.1.0a1/PKG-INFO +531 -0
  3. wisp_ai-0.1.0a1/README.md +495 -0
  4. wisp_ai-0.1.0a1/pyproject.toml +94 -0
  5. wisp_ai-0.1.0a1/pyproject.toml.orig +87 -0
  6. wisp_ai-0.1.0a1/src/wisp/__init__.py +3 -0
  7. wisp_ai-0.1.0a1/src/wisp/__main__.py +3 -0
  8. wisp_ai-0.1.0a1/src/wisp/agent/__init__.py +1 -0
  9. wisp_ai-0.1.0a1/src/wisp/agent/context.py +138 -0
  10. wisp_ai-0.1.0a1/src/wisp/agent/execution.py +40 -0
  11. wisp_ai-0.1.0a1/src/wisp/agent/harness.py +563 -0
  12. wisp_ai-0.1.0a1/src/wisp/agent/loop.py +589 -0
  13. wisp_ai-0.1.0a1/src/wisp/agent/messages.py +230 -0
  14. wisp_ai-0.1.0a1/src/wisp/agent/mode.py +20 -0
  15. wisp_ai-0.1.0a1/src/wisp/agent/prompt.py +414 -0
  16. wisp_ai-0.1.0a1/src/wisp/agent/transcript.py +108 -0
  17. wisp_ai-0.1.0a1/src/wisp/auth/__init__.py +17 -0
  18. wisp_ai-0.1.0a1/src/wisp/auth/openai_codex.py +372 -0
  19. wisp_ai-0.1.0a1/src/wisp/auth/storage.py +180 -0
  20. wisp_ai-0.1.0a1/src/wisp/cli/__init__.py +683 -0
  21. wisp_ai-0.1.0a1/src/wisp/cli/auth.py +141 -0
  22. wisp_ai-0.1.0a1/src/wisp/cli/options.py +92 -0
  23. wisp_ai-0.1.0a1/src/wisp/cli/output.py +150 -0
  24. wisp_ai-0.1.0a1/src/wisp/cli/rpc.py +863 -0
  25. wisp_ai-0.1.0a1/src/wisp/cli/rpc_configuration.py +8 -0
  26. wisp_ai-0.1.0a1/src/wisp/cli/rpc_coordinator.py +33 -0
  27. wisp_ai-0.1.0a1/src/wisp/cli/rpc_execution.py +9 -0
  28. wisp_ai-0.1.0a1/src/wisp/cli/rpc_transport.py +231 -0
  29. wisp_ai-0.1.0a1/src/wisp/cli/tools.py +17 -0
  30. wisp_ai-0.1.0a1/src/wisp/cli/trust.py +170 -0
  31. wisp_ai-0.1.0a1/src/wisp/cli/types.py +26 -0
  32. wisp_ai-0.1.0a1/src/wisp/coding/__init__.py +25 -0
  33. wisp_ai-0.1.0a1/src/wisp/coding/compaction.py +584 -0
  34. wisp_ai-0.1.0a1/src/wisp/coding/configuration.py +77 -0
  35. wisp_ai-0.1.0a1/src/wisp/coding/costs.py +184 -0
  36. wisp_ai-0.1.0a1/src/wisp/coding/session.py +1517 -0
  37. wisp_ai-0.1.0a1/src/wisp/coding/stats.py +210 -0
  38. wisp_ai-0.1.0a1/src/wisp/coding/tool_execution.py +727 -0
  39. wisp_ai-0.1.0a1/src/wisp/config.py +261 -0
  40. wisp_ai-0.1.0a1/src/wisp/events.py +1519 -0
  41. wisp_ai-0.1.0a1/src/wisp/extensions/__init__.py +1 -0
  42. wisp_ai-0.1.0a1/src/wisp/extensions/builtin.py +69 -0
  43. wisp_ai-0.1.0a1/src/wisp/providers/__init__.py +47 -0
  44. wisp_ai-0.1.0a1/src/wisp/providers/anthropic.py +665 -0
  45. wisp_ai-0.1.0a1/src/wisp/providers/auth.py +75 -0
  46. wisp_ai-0.1.0a1/src/wisp/providers/base.py +107 -0
  47. wisp_ai-0.1.0a1/src/wisp/providers/catalog.py +611 -0
  48. wisp_ai-0.1.0a1/src/wisp/providers/continuations.py +61 -0
  49. wisp_ai-0.1.0a1/src/wisp/providers/data/catalog.toml +360 -0
  50. wisp_ai-0.1.0a1/src/wisp/providers/events.py +126 -0
  51. wisp_ai-0.1.0a1/src/wisp/providers/fake.py +110 -0
  52. wisp_ai-0.1.0a1/src/wisp/providers/google.py +502 -0
  53. wisp_ai-0.1.0a1/src/wisp/providers/openai.py +425 -0
  54. wisp_ai-0.1.0a1/src/wisp/providers/openai_codex.py +619 -0
  55. wisp_ai-0.1.0a1/src/wisp/providers/retry.py +21 -0
  56. wisp_ai-0.1.0a1/src/wisp/py.typed +0 -0
  57. wisp_ai-0.1.0a1/src/wisp/retry.py +193 -0
  58. wisp_ai-0.1.0a1/src/wisp/rpc/__init__.py +65 -0
  59. wisp_ai-0.1.0a1/src/wisp/rpc/client.py +470 -0
  60. wisp_ai-0.1.0a1/src/wisp/rpc/commands.py +271 -0
  61. wisp_ai-0.1.0a1/src/wisp/rpc/configuration.py +148 -0
  62. wisp_ai-0.1.0a1/src/wisp/rpc/coordinator.py +576 -0
  63. wisp_ai-0.1.0a1/src/wisp/rpc/errors.py +9 -0
  64. wisp_ai-0.1.0a1/src/wisp/rpc/execution.py +3372 -0
  65. wisp_ai-0.1.0a1/src/wisp/rpc/host.py +654 -0
  66. wisp_ai-0.1.0a1/src/wisp/runtime/__init__.py +34 -0
  67. wisp_ai-0.1.0a1/src/wisp/runtime/api.py +163 -0
  68. wisp_ai-0.1.0a1/src/wisp/runtime/builtin_commands.py +180 -0
  69. wisp_ai-0.1.0a1/src/wisp/runtime/commands.py +181 -0
  70. wisp_ai-0.1.0a1/src/wisp/runtime/event_bus.py +35 -0
  71. wisp_ai-0.1.0a1/src/wisp/runtime/extensions.py +101 -0
  72. wisp_ai-0.1.0a1/src/wisp/runtime/registry.py +138 -0
  73. wisp_ai-0.1.0a1/src/wisp/sdk.py +492 -0
  74. wisp_ai-0.1.0a1/src/wisp/sessions/__init__.py +87 -0
  75. wisp_ai-0.1.0a1/src/wisp/sessions/branching.py +72 -0
  76. wisp_ai-0.1.0a1/src/wisp/sessions/entries.py +452 -0
  77. wisp_ai-0.1.0a1/src/wisp/sessions/errors.py +39 -0
  78. wisp_ai-0.1.0a1/src/wisp/sessions/jsonl.py +2187 -0
  79. wisp_ai-0.1.0a1/src/wisp/sessions/replay.py +347 -0
  80. wisp_ai-0.1.0a1/src/wisp/settings.py +380 -0
  81. wisp_ai-0.1.0a1/src/wisp/tool_presentation.py +39 -0
  82. wisp_ai-0.1.0a1/src/wisp/tools/__init__.py +20 -0
  83. wisp_ai-0.1.0a1/src/wisp/tools/approval.py +91 -0
  84. wisp_ai-0.1.0a1/src/wisp/tools/base.py +43 -0
  85. wisp_ai-0.1.0a1/src/wisp/tools/builtin.py +39 -0
  86. wisp_ai-0.1.0a1/src/wisp/tools/common.py +74 -0
  87. wisp_ai-0.1.0a1/src/wisp/tools/context.py +57 -0
  88. wisp_ai-0.1.0a1/src/wisp/tools/file_ops.py +304 -0
  89. wisp_ai-0.1.0a1/src/wisp/tools/paths.py +139 -0
  90. wisp_ai-0.1.0a1/src/wisp/tools/policy.py +52 -0
  91. wisp_ai-0.1.0a1/src/wisp/tools/process.py +1552 -0
  92. wisp_ai-0.1.0a1/src/wisp/tools/process_manager.py +985 -0
  93. wisp_ai-0.1.0a1/src/wisp/tools/result.py +19 -0
  94. wisp_ai-0.1.0a1/src/wisp/tools/search.py +821 -0
  95. wisp_ai-0.1.0a1/src/wisp/tools/selection.py +59 -0
  96. wisp_ai-0.1.0a1/src/wisp/tools/shell.py +464 -0
  97. wisp_ai-0.1.0a1/src/wisp/tools/summary.py +170 -0
  98. wisp_ai-0.1.0a1/src/wisp/tools/truncation.py +99 -0
  99. wisp_ai-0.1.0a1/src/wisp/tools/utf8.py +84 -0
  100. wisp_ai-0.1.0a1/src/wisp/trust.py +200 -0
  101. wisp_ai-0.1.0a1/src/wisp/trust_flow.py +63 -0
  102. wisp_ai-0.1.0a1/src/wisp/tui/__init__.py +54 -0
  103. wisp_ai-0.1.0a1/src/wisp/tui/app.py +126 -0
  104. wisp_ai-0.1.0a1/src/wisp/tui/auth_commands.py +123 -0
  105. wisp_ai-0.1.0a1/src/wisp/tui/commands.py +224 -0
  106. wisp_ai-0.1.0a1/src/wisp/tui/compact_echo.py +106 -0
  107. wisp_ai-0.1.0a1/src/wisp/tui/context_widget.py +252 -0
  108. wisp_ai-0.1.0a1/src/wisp/tui/decision_content.py +216 -0
  109. wisp_ai-0.1.0a1/src/wisp/tui/diff_presentation.py +430 -0
  110. wisp_ai-0.1.0a1/src/wisp/tui/file_index.py +246 -0
  111. wisp_ai-0.1.0a1/src/wisp/tui/file_suggest.py +185 -0
  112. wisp_ai-0.1.0a1/src/wisp/tui/history.py +167 -0
  113. wisp_ai-0.1.0a1/src/wisp/tui/launch.py +147 -0
  114. wisp_ai-0.1.0a1/src/wisp/tui/live.py +439 -0
  115. wisp_ai-0.1.0a1/src/wisp/tui/overlay.py +222 -0
  116. wisp_ai-0.1.0a1/src/wisp/tui/prompt_history.py +115 -0
  117. wisp_ai-0.1.0a1/src/wisp/tui/prompt_history_widget.py +229 -0
  118. wisp_ai-0.1.0a1/src/wisp/tui/rendering.py +1568 -0
  119. wisp_ai-0.1.0a1/src/wisp/tui/shell.py +2049 -0
  120. wisp_ai-0.1.0a1/src/wisp/tui/state.py +293 -0
  121. wisp_ai-0.1.0a1/src/wisp/tui/stream_buffer.py +285 -0
  122. wisp_ai-0.1.0a1/src/wisp/tui/textual_app.py +2324 -0
  123. wisp_ai-0.1.0a1/src/wisp/tui/textual_history.py +644 -0
  124. wisp_ai-0.1.0a1/src/wisp/tui/textual_input.py +163 -0
  125. wisp_ai-0.1.0a1/src/wisp/tui/textual_renderer.py +593 -0
  126. wisp_ai-0.1.0a1/src/wisp/tui/textual_transcript.py +387 -0
  127. wisp_ai-0.1.0a1/src/wisp/tui/theme.py +129 -0
  128. wisp_ai-0.1.0a1/src/wisp/tui/tool_output.py +1456 -0
  129. wisp_ai-0.1.0a1/src/wisp/tui/transcript_window.py +149 -0
  130. wisp_ai-0.1.0a1/src/wisp/tui/widgets.py +2607 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hanyu
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,531 @@
1
+ Metadata-Version: 2.4
2
+ Name: wisp-ai
3
+ Version: 0.1.0a1
4
+ Summary: Terminal-first coding agent with typed CLI, TUI, RPC, and SDK interfaces
5
+ Keywords: agent,ai,cli,coding-agent,llm,tui
6
+ Author: whanyu1212
7
+ Author-email: whanyu1212 <whanyu1212@hotmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Code Generators
18
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
19
+ Classifier: Typing :: Typed
20
+ Requires-Dist: anthropic>=0.116.0
21
+ Requires-Dist: anyio>=4.14.1
22
+ Requires-Dist: google-genai>=2.12.0
23
+ Requires-Dist: httpx>=0.28.1
24
+ Requires-Dist: openai>=2.44.0
25
+ Requires-Dist: prompt-toolkit>=3.0.52
26
+ Requires-Dist: pydantic>=2.13.4
27
+ Requires-Dist: rich>=15.0.0
28
+ Requires-Dist: textual>=8.2.8
29
+ Requires-Dist: typer>=0.26.8
30
+ Requires-Python: >=3.12
31
+ Project-URL: Homepage, https://github.com/whanyu1212/Wisp
32
+ Project-URL: Repository, https://github.com/whanyu1212/Wisp
33
+ Project-URL: Changelog, https://github.com/whanyu1212/Wisp/blob/main/CHANGELOG.md
34
+ Project-URL: Issues, https://github.com/whanyu1212/Wisp/issues
35
+ Description-Content-Type: text/markdown
36
+
37
+ <p align="center">
38
+ <img src="https://raw.githubusercontent.com/whanyu1212/Wisp/main/assets/wisp-banner.png" alt="Wisp — A Python coding agent that stays in sync." width="100%">
39
+ </p>
40
+
41
+ # Wisp
42
+
43
+ <p align="center">
44
+ <strong>A terminal-first coding agent with one typed, event-driven core.</strong>
45
+ </p>
46
+
47
+ <p align="center">
48
+ <a href="#install">Install</a>
49
+ ·
50
+ <a href="#quickstart">Quickstart</a>
51
+ ·
52
+ <a href="#architecture">Architecture</a>
53
+ ·
54
+ <a href="https://pypi.org/project/wisp-ai/">PyPI</a>
55
+ ·
56
+ <a href="https://github.com/whanyu1212/Wisp/blob/main/CHANGELOG.md">Changelog</a>
57
+ ·
58
+ <a href="https://github.com/whanyu1212/Wisp/issues">Issues</a>
59
+ </p>
60
+
61
+ <p align="center">
62
+ <a href="https://pypi.org/project/wisp-ai/"><img src="https://img.shields.io/pypi/v/wisp-ai?label=PyPI" alt="PyPI version" /></a>
63
+ <a href="https://www.python.org/"><img src="https://img.shields.io/badge/Python-3.12%2B-blue" alt="Python 3.12+" /></a>
64
+ <a href="https://github.com/whanyu1212/Wisp/actions/workflows/ci.yml"><img src="https://github.com/whanyu1212/Wisp/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
65
+ <a href="https://github.com/whanyu1212/Wisp/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="MIT License" /></a>
66
+ </p>
67
+
68
+ > **Alpha status:** Wisp is under active development. Interfaces may change while the runtime and
69
+ > TUI stabilize.
70
+
71
+ ## What is Wisp?
72
+
73
+ **Wisp is a coding agent that runs in your terminal.** Ask it to inspect a repository, explain an
74
+ architecture, edit code, run commands, or continue a previous session. Wisp streams its work into a
75
+ fullscreen Textual interface and keeps an inspectable JSONL record of the conversation and tool
76
+ activity.
77
+
78
+ Wisp is also an embeddable Python runtime. Its CLI, TUI, JSONL RPC process, and in-process SDK all
79
+ drive the same typed command host and agent loop rather than maintaining separate implementations.
80
+
81
+ Wisp takes behavioral inspiration from Pi while putting explicit trust, protected-path, approval,
82
+ and persistence boundaries around local coding-agent work.
83
+
84
+ ## Install
85
+
86
+ Wisp is published on PyPI as `wisp-ai`, installs a `wisp` command, and requires Python 3.12 or
87
+ newer. The current release is an alpha, so request it explicitly:
88
+
89
+ ```bash
90
+ uv tool install "wisp-ai==0.1.0a1"
91
+ ```
92
+
93
+ If `wisp` is not on your `PATH`, run `uv tool update-shell` once and restart your shell.
94
+
95
+ To run Wisp without installing it:
96
+
97
+ ```bash
98
+ uvx --from "wisp-ai==0.1.0a1" wisp
99
+ ```
100
+
101
+ ## Quickstart
102
+
103
+ Run Wisp from the project you want it to work on:
104
+
105
+ ```bash
106
+ cd path/to/project
107
+ wisp
108
+ ```
109
+
110
+ Wisp defaults to OpenAI Codex subscription access. From the TUI, connect your account with:
111
+
112
+ ```text
113
+ /login openai-codex
114
+ ```
115
+
116
+ You can also authenticate before launch with `wisp auth login openai-codex`. Then enter a request
117
+ such as:
118
+
119
+ ```text
120
+ explain the architecture of this repository
121
+ ```
122
+
123
+ For one-shot prompts and scripts, use print mode:
124
+
125
+ ```bash
126
+ wisp -p "summarize the current changes"
127
+ ```
128
+
129
+ An offline smoke test is available without credentials or network model calls:
130
+
131
+ ```bash
132
+ wisp -p "hello" --provider fake
133
+ ```
134
+
135
+ ## What Wisp can do
136
+
137
+ - Fullscreen Textual TUI plus text, JSONL, and RPC modes.
138
+ - Built-in `read`, `write`, `edit`, `bash`, `grep`, `find`, and `ls` tools.
139
+ - OpenAI Codex, OpenAI API, Anthropic, Google, and deterministic fake providers.
140
+ - Append-only JSONL sessions with resume, branching, compaction, usage, and cost accounting.
141
+ - Project instructions from trusted `AGENTS.md` and `CLAUDE.md` files.
142
+ - Protected secret paths, cwd-constrained file tools, and explicit unsafe-tool approvals.
143
+ - Typed RPC and in-process SDK surfaces for custom frontends and integrations.
144
+
145
+ ## Architecture
146
+
147
+ Wisp has one event-driven runtime shared by every interface:
148
+
149
+ ```text
150
+ CLI / JSONL-RPC / SDK adapters → RPC command host → CodingSession → AgentHarness → run_agent_loop
151
+ ```
152
+
153
+ Each layer adds one concern. The provider/tool cycle does not know about sessions or frontends; the
154
+ harness owns in-memory conversation state; the coding session adds persistence and safety policy;
155
+ and interfaces consume typed `WispEvent` values. The TUI is an RPC client, not a second agent loop.
156
+
157
+ ## Interfaces
158
+
159
+ | Mode | Command | Output | Best for |
160
+ |------|---------|--------|----------|
161
+ | **TUI** | `wisp` (or `wisp tui`) | Fullscreen Textual UI | Interactive development |
162
+ | **Print** | `wisp -p "…"` | Assistant text on stdout, events on stderr | One-shot prompts and scripts |
163
+ | **JSON** | `wisp -p "…" --mode json` | One `WispEvent` JSON object per line | Machine-readable automation |
164
+ | **RPC** | `wisp --mode rpc` | Typed JSONL commands and events | Long-lived integrations |
165
+
166
+ JSON mode writes every event as one JSON object per line. RPC mode and the in-process SDK expose the
167
+ same command, event, session, trust, and approval contracts used by the built-in interfaces.
168
+
169
+ ## Providers & auth
170
+
171
+ | Provider | Credentials |
172
+ |---|---|
173
+ | `openai-codex` *(default)* | ChatGPT Plus/Pro via OAuth — `wisp auth login openai-codex` |
174
+ | `openai` | `OPENAI_API_KEY` |
175
+ | `anthropic` | `ANTHROPIC_API_KEY` |
176
+ | `google` | `GOOGLE_API_KEY` |
177
+ | `fake` | None — deterministic offline provider for tests and smoke runs |
178
+
179
+ ```bash
180
+ wisp -p "hello" --provider anthropic --model claude-sonnet-5
181
+ ```
182
+
183
+ Codex credentials are stored in `WISP_AUTH_FILE` (default `~/.wisp/auth.json`) with private
184
+ permissions.
185
+
186
+ In the TUI, `/model` with no arguments lists every catalog model grouped by provider. If a model id
187
+ belongs to only one registered provider, `/model <id>` switches providers to match; otherwise use
188
+ `/provider <name>` first.
189
+
190
+ ### Model catalog
191
+
192
+ The packaged catalog lists current text-generation models that Wisp's streaming, client-tool
193
+ adapters can use. Catalog entries are **advisory, not access control** — model access varies by
194
+ account and region, and explicitly configured unknown models still pass through to the provider.
195
+
196
+ Context windows and compaction limits are provider-scoped: the direct `openai` API and the
197
+ `openai-codex` subscription can expose the same model id with different limits. Wisp uses the
198
+ earlier of the provider-recommended compaction limit and the configured reserve; provider metadata
199
+ can make the reserve more conservative but never weaken a larger user reserve.
200
+
201
+ Pricing is optional, effective-dated, and provider-scoped, and is used only to estimate new request
202
+ costs. Add account-specific models or negotiated rates in the user-only `~/.wisp/catalog.toml`
203
+ overlay — Wisp never reads a project-local catalog.
204
+
205
+ ## Tools and safety
206
+
207
+ Wisp includes built-in local tools for reading files, editing files, searching projects, and
208
+ running shell commands. File tools are sandboxed to the tool context's working directory.
209
+
210
+ | Category | Tools | Approval |
211
+ |----------|-------|----------|
212
+ | **Read** | `read` · `grep` · `find` · `ls` | Runs directly |
213
+ | **Mutating** | `write` · `edit` | Required |
214
+ | **Command** | `bash` | Required |
215
+
216
+ `bash` defaults to one-shot execution and reports stdout, stderr, truncation state, and exit code.
217
+ It also accepts `operation=start|poll|cancel` for commands needing a retained process handle; those
218
+ return a `process_id`, process state, incremental output, and per-stream truncation metadata under
219
+ the same safety category and approval policy.
220
+
221
+ **Print mode exposes no tools unless you ask.** Read tools are enabled as a group; mutating and
222
+ command tools require per-tool opt-in:
223
+
224
+ ```bash
225
+ wisp -p "list files" --allow-read-tools
226
+ wisp -p "run tests" --allow-tool bash --yes
227
+ ```
228
+
229
+ Because print mode is non-interactive, mutating and command tools are also blocked at execution
230
+ time unless you pass `--yes` (alias `--allow-unsafe-tool-execution`). Without it the model receives
231
+ a clear tool error instead of Wisp executing the operation.
232
+
233
+ Wisp does not cap model/tool rounds by default, matching Pi's permissive agent loop. Pass
234
+ `--max-tool-iterations <n>` for a non-interactive fuse.
235
+
236
+ Extensions may attach optional `ToolPromptMetadata` when calling `ExtensionAPI.register_tool(...)`.
237
+ Wisp adds that guidance only when the tool is actually exposed for the current run, de-duplicates
238
+ and bounds it, and keeps it separate from the provider-facing tool schema. The metadata is
239
+ descriptive — it cannot alter tool policy, sandboxing, protected paths, or approval requirements.
240
+
241
+ ## Sessions
242
+
243
+ Wisp persists each run as a JSONL session and can continue an existing one:
244
+
245
+ ```bash
246
+ wisp -p "continue the work" --continue
247
+ wisp -p "continue the work" --resume path/to/session.jsonl
248
+ wisp -p "continue the work" --resume <session-id-prefix>
249
+ ```
250
+
251
+ - `--continue` resumes the newest session in the active session directory.
252
+ - `--resume` accepts a JSONL path, filename, full session id, or unique id prefix.
253
+ - Sessions live under `~/.wisp/sessions`; override with `--session-dir` or `WISP_SESSION_DIR`.
254
+
255
+ Session files contain provider-facing `message` entries plus selected structured `event` entries
256
+ (tool calls, approvals, tool start/end, errors) for audit. They do **not** persist `message.delta`
257
+ events. Continuation replays only the selected path's messages and compactions, so audit events
258
+ never become model-visible history.
259
+
260
+ Records form a parent-linked tree, and an append-only active-leaf record selects the root-to-leaf
261
+ path used by continuation — abandoned or cancelled work stays in the audit log without entering
262
+ model context. Legacy unversioned and v1 linear session files remain readable and are never
263
+ rewritten on load.
264
+
265
+ The typed session API can derive a new session without rewriting its source: a **clone** copies the
266
+ complete active path, a **fork** copies the path before a selected user message and returns that
267
+ prompt for editing. Copied entries retain stable IDs, parent links, timestamps, and accounting
268
+ metadata under a new session ID. These are available to RPC clients via `clone_session` /
269
+ `fork_session`; direct CLI and TUI commands are not yet exposed.
270
+
271
+ > **Deprecated:** `wisp.agent.messages.SessionEntry(...)` remains available as a factory. New
272
+ > integrations should import the concrete entry models from `wisp.sessions`.
273
+
274
+ ## Configuration
275
+
276
+ Wisp reads configuration from CLI flags, environment variables, and JSON settings files.
277
+
278
+ Precedence, highest to lowest:
279
+
280
+ ```text
281
+ CLI flag > environment variable > project ./.wisp/settings.json > user ~/.wisp/settings.json > built-in default
282
+ ```
283
+
284
+ ### Environment variables
285
+
286
+ | Variable | Purpose |
287
+ |----------|---------|
288
+ | `WISP_PROVIDER` | Provider name: `openai-codex`, `openai`, `anthropic`, `google`, or `fake` |
289
+ | `WISP_MODEL` | Model override; blank uses the provider default |
290
+ | `WISP_MODE` | Default mode; set to `tui` to open the TUI directly |
291
+ | `WISP_TUI_RENDERER` | TUI renderer: `line`, `fullscreen`, or `textual` |
292
+ | `WISP_SESSION_DIR` | Session storage directory; defaults to `~/.wisp/sessions` |
293
+ | `WISP_AUTH_FILE` | Auth file path; defaults to `~/.wisp/auth.json` |
294
+ | `WISP_RETRY_MAX_RETRIES` | Provider retry count; defaults to `2`, set `0` to disable |
295
+ | `WISP_RETRY_BASE_DELAY_SECONDS` | Initial retry delay; defaults to `0.5` |
296
+ | `WISP_RETRY_MAX_DELAY_SECONDS` | Maximum retry delay; defaults to `30` |
297
+ | `WISP_CONTEXT_RESERVE_TOKENS` | Minimum tokens reserved outside estimated input context; defaults to `16384` |
298
+ | `WISP_AUTO_COMPACTION` | Automatic threshold compaction and overflow recovery; defaults to `true` |
299
+ | `OPENAI_API_KEY` · `ANTHROPIC_API_KEY` · `GOOGLE_API_KEY` | Required only for the matching provider |
300
+
301
+ ### Settings files
302
+
303
+ For durable defaults, use a settings file. The user-level file lives at `~/.wisp/settings.json`; a
304
+ project may add `./.wisp/settings.json`, applied only after you trust the project.
305
+
306
+ ```json
307
+ {
308
+ "provider": "openai",
309
+ "model": "gpt-5.6-sol",
310
+ "effort": "high",
311
+ "session_dir": "~/.wisp/sessions",
312
+ "context_reserve_tokens": 16384,
313
+ "auto_compaction_enabled": true,
314
+ "retry": { "max_retries": 2, "base_delay_seconds": 0.5, "max_delay_seconds": 30 }
315
+ }
316
+ ```
317
+
318
+ Some fields are **user-only** and a project file can never set them: `protected_paths`, `retry`,
319
+ `effort`, `context_reserve_tokens`, and `auto_compaction_enabled`. A repository cannot increase your
320
+ API spending, prolong waits, or weaken the secret guard.
321
+
322
+ After a successful TUI `/model` or `/provider` change, Wisp atomically records the active provider,
323
+ model, and effort as user defaults, reused next launch unless a higher-precedence source overrides
324
+ them. Failed changes, trusted-project configuration, CLI flags, and external RPC configuration do
325
+ not rewrite these preferences.
326
+
327
+ Never commit auth files or real API keys.
328
+
329
+ > **Migration note:** Wisp no longer reads a project `.env` file. Move any values you kept there
330
+ > into your shell environment or `~/.wisp/settings.json`. A project `.env` on disk is still treated
331
+ > as a secret and is never surfaced to the model.
332
+
333
+ ### Retry behavior
334
+
335
+ Wisp retries only requests that fail before the provider starts streaming, using bounded
336
+ exponential backoff with jitter. It honors reasonable `Retry-After` requests, emits retry progress
337
+ in JSON/RPC and the TUI, and never replays an already-started response.
338
+
339
+ OpenAI-family streams succeed only after the provider's native completion event. If a connection
340
+ ends first, Wisp reports a failed turn with any partial text and never executes buffered tool calls.
341
+ For Wisp-owned `openai-codex` connections, connect and pool waits are limited to 10 seconds,
342
+ request writes to 30 seconds, and response-header or between-chunk read inactivity to 300 seconds.
343
+ Caller-injected HTTP clients retain their caller-selected timeout policy.
344
+
345
+ ## Project trust
346
+
347
+ Project-local settings, context files (`AGENTS.md` / `CLAUDE.md`), and project extensions are
348
+ loaded only after the project is trusted. Untrusted projects remain fully usable — Wisp simply
349
+ ignores their local configuration and instructions.
350
+
351
+ The first run in an untrusted directory asks `Do you trust the files in /path/to/project?`. Answer
352
+ yes and the decision is remembered globally in `~/.wisp/trust.json`, keyed by resolved path.
353
+
354
+ ```bash
355
+ wisp trust status [path] # trusted, untrusted, or undecided
356
+ wisp trust allow [path] # persistently trust a project
357
+ wisp trust revoke [path] # persistently mark a project untrusted
358
+ wisp trust forget [path] # remove the decision so Wisp can prompt again
359
+ ```
360
+
361
+ Security notes:
362
+
363
+ - **Non-interactive runs** (CI, scripts, standalone RPC) default to untrusted. The interactive TUI
364
+ asks before entering the interface. Set `WISP_TRUST=1` to opt in for one process, or
365
+ `WISP_TRUST=0` to force untrusted mode.
366
+ - `WISP_TRUST` is read only from the real process environment, never from project files, and is
367
+ never persisted.
368
+ - `WISP_TRUST_FILE` may relocate the global trust store, but only to an absolute path outside the
369
+ repository. A relative value is rejected.
370
+
371
+ ## Context & compaction
372
+
373
+ Each turn sends a default coding-agent system prompt plus a bounded project-context message before
374
+ the user prompt: working directory, git branch and capped status summary, detected root files,
375
+ tools exposed to the model, and trusted project instructions.
376
+
377
+ Context files load from the trusted context root down to the working directory, parent instructions
378
+ first. In each directory Wisp uses the first Pi-compatible match: `AGENTS.md`, `AGENTS.MD`,
379
+ `CLAUDE.md`, `CLAUDE.MD`. Symlinked, protected, or out-of-scope files are skipped. Project
380
+ instructions are bounded separately from the tool list, so a large instruction file cannot hide the
381
+ available tools.
382
+
383
+ Project context is trust-gated — in untrusted projects Wisp reads no local instruction files or
384
+ settings. This is stricter than Pi, and keeps project guidance inside the same boundary as project
385
+ settings and future extensions.
386
+
387
+ ### Accounting
388
+
389
+ Before each request Wisp emits `context.estimated`, a deterministic approximation of the system
390
+ prompt, active messages, pending tool results, and tool schemas (a conservative `ceil(chars / 4)`
391
+ heuristic). When the catalog provides a context window, the event also reports the reserve,
392
+ estimated percentage, remaining budget, and whether the estimate crossed it. Unknown models remain
393
+ permissive.
394
+
395
+ Provider-reported `usage.total_tokens` is kept separately as the authoritative observation for a
396
+ completed request. Session statistics sum provider totals exactly as reported and never reconstruct
397
+ totals from input/output categories.
398
+
399
+ ### Compaction
400
+
401
+ `/compact [instructions]` replaces older provider-visible turns with a structured checkpoint while
402
+ retaining the latest complete user turn verbatim. The summary request uses the active provider,
403
+ model, and effort without tools. If the model cannot produce a complete summary, compaction fails
404
+ without changing replay.
405
+
406
+ Compaction is **append-only** and lossy only at replay time: original messages stay in the JSONL
407
+ audit log while later prompts receive the checkpoint plus retained recent context. Wisp never splits
408
+ a tool call from its result.
409
+
410
+ Automatic threshold compaction is enabled by default and runs after a completed prompt when active
411
+ context exceeds the reserved input budget. It triggers only when usage is strictly greater than
412
+ `context_window - context_reserve_tokens`. If an automatic summary fails, Wisp preserves the
413
+ completed prompt and leaves replay unchanged. Disable with `WISP_AUTO_COMPACTION=0` or
414
+ `"auto_compaction_enabled": false`.
415
+
416
+ When a provider explicitly rejects an input for context overflow, Wisp can compact and retry the
417
+ same prompt once. Recovery is skipped after mutating or command tools, or after deltas have already
418
+ reached an interface, because side effects and partial responses cannot be safely repeated.
419
+
420
+ ## TUI
421
+
422
+ ```bash
423
+ wisp
424
+ ```
425
+
426
+ A fullscreen Textual TUI built on the same RPC controller other integrations use. The footer shows
427
+ the working directory/session, status, queued follow-ups, provider/model, context use, and
428
+ cumulative cost.
429
+
430
+ - `ctx 12k/128k` is a current provider observation; `ctx ~12k/128k` is an estimate.
431
+ - `cost $0.042` is complete accounting; `cost ≥$0.042` includes unpriced requests. Estimates are
432
+ not invoices — subscription-backed Codex, custom pricing, and unknown models remain unpriced.
433
+
434
+ Unlike print mode, **the TUI exposes the full tool registry by default** — otherwise it would be a
435
+ chatbot that can't read files or run commands. Mutating and command tools still pause for approval:
436
+ approve once, allow that tool for the session, YOLO all mutating/command tools for the process
437
+ (requires a second confirmation, never persisted), or deny.
438
+
439
+ ### Slash commands
440
+
441
+ ```text
442
+ /help show help
443
+ /auth [provider] show credential status
444
+ /login [provider] [device-code]
445
+ /logout [provider]
446
+ /provider [provider] switch provider (resets model to default)
447
+ /model [model] [effort] switch model and optional reasoning effort
448
+ /new start a fresh session and clear the screen
449
+ /resume [session-id] browse or resume a persisted session
450
+ /compact [instructions] summarize older context while preserving the JSONL audit
451
+ /context [auto on|off] show or toggle compaction policy
452
+ /plan switch to read-only planning mode
453
+ /build switch to normal build mode
454
+ /history search prompts submitted in this TUI run
455
+ /quit, /exit
456
+ ```
457
+
458
+ Type `/` to filter commands inline. Type `@` to reference a project file — an inline picker filters
459
+ as you type, matching loosely so `@tuiapp` finds `src/wisp/tui/textual_app.py`. Only the path is
460
+ inserted; Wisp does not inline file contents, and the listing honors the same `protected_paths`
461
+ policy, so secrets are never offered.
462
+
463
+ ### Keybindings
464
+
465
+ | Key | Action |
466
+ |---|---|
467
+ | `Enter` | Submit |
468
+ | `Shift+Enter` / `Ctrl+J` | Insert newline (`Ctrl+J` in the live fullscreen renderer) |
469
+ | `Shift+Tab` | Toggle plan/build mode |
470
+ | `Ctrl+G` | Toggle contextual help for the focused Textual surface |
471
+ | `Ctrl+R` | Search prompt history for this TUI run |
472
+ | `Escape` | Dismiss nearest menu or overlay, then cancel an active prompt |
473
+ | `Ctrl+C` | Copy selection; otherwise press twice within 1.5s to quit |
474
+ | `Ctrl+D` | Delete right; EOF only from an empty editor |
475
+
476
+ In the Textual TUI, `Ctrl+G` and `/help` open the same native contextual guide. It follows focus
477
+ across the editor, tool cards, pickers, context reports, and safety decisions; its key reference is
478
+ derived from live bindings. The panel moves below the conversation on narrow terminals and never
479
+ runs a tool, changes the session, or resolves an approval. Line and fallback fullscreen modes keep
480
+ their textual `/help` summary.
481
+
482
+ Prompt history holds up to 100 unique prompts and is **memory-only** — never written to session
483
+ JSONL, configuration, or a cache, so prompts containing secrets are not silently persisted.
484
+
485
+ **Plan mode** applies to future prompts in the current process. It exposes only read-only tools that
486
+ were already authorized at startup; `write`, `edit`, `bash`, and non-read extension tools are
487
+ unavailable. Use `/build` to restore. The mode is not persisted in session JSONL.
488
+
489
+ **`/new`** preserves the current JSONL session for `/resume`, clears the transcript and screen, and
490
+ creates the next session lazily. Provider, model, effort, mode, tool permissions, trust, and
491
+ compaction settings are retained.
492
+
493
+ ### Flags and renderers
494
+
495
+ ```bash
496
+ wisp tui --continue
497
+ wisp tui --resume <session-id-prefix>
498
+ wisp tui --no-all-tools # opt-in tool filter instead of the full registry
499
+ wisp tui --yes # auto-approve mutating/command tools
500
+ wisp tui --line # simple line renderer, for fallback/debugging
501
+ ```
502
+
503
+ On `--continue` or `--resume`, the TUI hydrates up to 500 active-path persisted messages through the
504
+ same RPC `get_messages` command available to other frontends before accepting input.
505
+
506
+ The Textual TUI targets truecolor terminals and degrades gracefully — 256-color and 16-color
507
+ terminals are handled by Textual's own detection. Setting `NO_COLOR` switches to deterministic
508
+ grayscale.
509
+
510
+ The legacy `--mode tui` entrypoint remains for compatibility and honors
511
+ `--tui-renderer line|fullscreen|textual` plus `WISP_TUI_RENDERER`.
512
+
513
+ ## Development
514
+
515
+ ```bash
516
+ uv sync # install
517
+ uv run ruff format --check . && uv run ruff check . && uv run mypy # quality gates
518
+ uv run pytest tests # complete suite
519
+ uv run pytest tests -m 'not (slow or tui or process or benchmark)' # faster core checks
520
+ ```
521
+
522
+ The complete suite runs against the deterministic `fake` provider, so the agent core, CLI, and JSONL
523
+ sessions are exercised without API keys or network access. Run the complete command before
524
+ considering a change verified.
525
+
526
+ Changes should preserve the layer boundaries described in [Architecture](#architecture). Local
527
+ agent instruction files remain untracked so contributors can tailor them to their own workflows.
528
+
529
+ ## License
530
+
531
+ See [LICENSE](https://github.com/whanyu1212/Wisp/blob/main/LICENSE).