paimon 0.2.7__tar.gz → 0.3.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.
- paimon-0.3.0/PKG-INFO +237 -0
- paimon-0.3.0/README.md +218 -0
- {paimon-0.2.7 → paimon-0.3.0}/README.zh-CN.md +45 -24
- paimon-0.3.0/paimon/_grep_worker.py +23 -0
- paimon-0.3.0/paimon/agent.py +1329 -0
- paimon-0.3.0/paimon/agentpane.py +148 -0
- paimon-0.3.0/paimon/antigravity.py +440 -0
- paimon-0.3.0/paimon/app.py +875 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/app.tcss +55 -18
- paimon-0.3.0/paimon/chatgpt.py +208 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/cli.py +56 -46
- paimon-0.3.0/paimon/codemode.py +244 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/commandpane.py +39 -16
- {paimon-0.2.7 → paimon-0.3.0}/paimon/commands.py +141 -57
- {paimon-0.2.7 → paimon-0.3.0}/paimon/compaction.py +77 -69
- paimon-0.3.0/paimon/config.py +464 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/diff.py +20 -5
- {paimon-0.2.7 → paimon-0.3.0}/paimon/errors.py +1 -1
- paimon-0.3.0/paimon/events.py +274 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/headless.py +92 -70
- {paimon-0.2.7 → paimon-0.3.0}/paimon/herdr.py +6 -6
- paimon-0.3.0/paimon/history.py +183 -0
- paimon-0.3.0/paimon/jobs.py +428 -0
- paimon-0.3.0/paimon/llm.py +250 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/login.py +115 -45
- paimon-0.3.0/paimon/logtext.py +200 -0
- paimon-0.3.0/paimon/oauth.py +242 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/pane.py +234 -496
- paimon-0.3.0/paimon/progstatus.py +56 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/prompt.py +19 -12
- paimon-0.3.0/paimon/review.py +252 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/session.py +149 -78
- {paimon-0.2.7 → paimon-0.3.0}/paimon/skill/SKILL.md +9 -9
- {paimon-0.2.7 → paimon-0.3.0}/paimon/skills.py +6 -6
- {paimon-0.2.7 → paimon-0.3.0}/paimon/tabs.py +17 -22
- {paimon-0.2.7 → paimon-0.3.0}/paimon/telemetry.py +4 -5
- paimon-0.3.0/paimon/textual_debug.py +230 -0
- paimon-0.3.0/paimon/tools/__init__.py +10 -0
- paimon-0.3.0/paimon/tools/background.py +221 -0
- paimon-0.3.0/paimon/tools/base.py +47 -0
- paimon-0.3.0/paimon/tools/files.py +191 -0
- paimon-0.3.0/paimon/tools/grep.py +226 -0
- paimon-0.3.0/paimon/tools/permissions.py +780 -0
- paimon-0.3.0/paimon/tools/registry.py +563 -0
- paimon-0.3.0/paimon/tools/result.py +54 -0
- paimon-0.3.0/paimon/tools/shell.py +701 -0
- paimon-0.3.0/paimon/transcript.py +293 -0
- paimon-0.3.0/paimon/turns.py +446 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/ui.py +362 -41
- {paimon-0.2.7 → paimon-0.3.0}/paimon/websearch.py +8 -6
- paimon-0.3.0/paimon.egg-info/PKG-INFO +237 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon.egg-info/SOURCES.txt +23 -3
- paimon-0.3.0/paimon.egg-info/requires.txt +10 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon.egg-info/scm_file_list.json +31 -3
- paimon-0.3.0/paimon.egg-info/scm_version.json +8 -0
- {paimon-0.2.7 → paimon-0.3.0}/pyproject.toml +6 -2
- paimon-0.2.7/PKG-INFO +0 -211
- paimon-0.2.7/README.md +0 -196
- paimon-0.2.7/paimon/_grep_worker.py +0 -16
- paimon-0.2.7/paimon/agent.py +0 -1384
- paimon-0.2.7/paimon/app.py +0 -636
- paimon-0.2.7/paimon/config.py +0 -382
- paimon-0.2.7/paimon/llm.py +0 -130
- paimon-0.2.7/paimon/model_windows.py +0 -2852
- paimon-0.2.7/paimon/tools.py +0 -2250
- paimon-0.2.7/paimon/turns.py +0 -198
- paimon-0.2.7/paimon.egg-info/PKG-INFO +0 -211
- paimon-0.2.7/paimon.egg-info/requires.txt +0 -6
- paimon-0.2.7/paimon.egg-info/scm_version.json +0 -8
- {paimon-0.2.7 → paimon-0.3.0}/LICENSE +0 -0
- {paimon-0.2.7 → paimon-0.3.0}/MANIFEST.in +0 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/__init__.py +0 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/__main__.py +0 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/lockfile.py +0 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/mentions.py +0 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon/retry.py +0 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon.egg-info/dependency_links.txt +0 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon.egg-info/entry_points.txt +0 -0
- {paimon-0.2.7 → paimon-0.3.0}/paimon.egg-info/top_level.txt +0 -0
- {paimon-0.2.7 → paimon-0.3.0}/setup.cfg +0 -0
paimon-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: paimon
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: A minimal code agent built on pydantic-ai + textual
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: httpx>=0.27
|
|
9
|
+
Requires-Dist: genai-prices>=0.1.6
|
|
10
|
+
Requires-Dist: pydantic-ai-slim[anthropic,google,openai]>=2.54.0
|
|
11
|
+
Requires-Dist: textual>=8.2.7
|
|
12
|
+
Requires-Dist: textual-serve>=1.1.3
|
|
13
|
+
Requires-Dist: pyyaml>=6
|
|
14
|
+
Requires-Dist: pydantic>=2.10
|
|
15
|
+
Requires-Dist: typing-extensions>=4.12
|
|
16
|
+
Requires-Dist: ddgs>=9
|
|
17
|
+
Requires-Dist: pydantic-monty<2,>=1.0
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Paimon
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
English | [简体中文](README.zh-CN.md)
|
|
25
|
+
|
|
26
|
+
Paimon is a coding agent that lives in your terminal. It reads and edits files in the current directory and runs commands. It also runs headless and imports as a library, so a stronger agent or a program of your own can drive it.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uv tool install paimon # or: pip install paimon
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Getting started
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
paimon
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or run it without installing anything:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
uvx paimon
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The first launch asks for a provider, its API base and key, then a model. Then just type what you want done. Write `@path/to/file` in a prompt to hand a file to the agent.
|
|
47
|
+
|
|
48
|
+
While it runs: `Shift+Tab` switches how much the agent may do on its own (**read** only reads, **auto** edits inside the working directory and has a second model call approve everything else, **yolo** checks nothing and is the default), `Esc` interrupts the current turn, `Ctrl+P` opens the command palette, `Ctrl+C` quits. A line starting with `!` runs in a shell instead of being sent, and Paimon sees what it printed. `!!` keeps it to yourself.
|
|
49
|
+
|
|
50
|
+
`Ctrl+T` opens another session in a pane of its own, `Ctrl+W` closes one, `Ctrl+PageUp` and `Ctrl+PageDown` move between them, and `Ctrl+G` jumps to a pane waiting for permission. `Ctrl+L` returns keyboard focus to the current pane's input or pending answer. The status bar identifies input, reading, and answer focus; pane switches preserve reading focus, and background completion does not steal the keyboard. Paimon can work in parallel: ask for two independent things and it starts a second agent in a tab of its own, where you can watch it work and answer what it asks, and whose answer comes back into the conversation when it is done. Closing the tab stops it. It can also leave a command running in a tab, a dev server or a watcher, instead of holding up a turn.
|
|
51
|
+
|
|
52
|
+
## Web search
|
|
53
|
+
|
|
54
|
+
Paimon can search the web without an API key. It asks in no permission mode, because a search changes nothing on your machine. Searches go through [ddgs](https://github.com/deedy5/ddgs), a metasearch library, so a query may be sent to any of several public search engines. `--no-web-search` takes the tool away for a run.
|
|
55
|
+
|
|
56
|
+
## Skills
|
|
57
|
+
|
|
58
|
+
Paimon loads [Agent Skills](https://agentskills.io) from `~/.config/paimon/skills`, `~/.agents/skills` and every `.agents/skills` from the working directory up to the repository root. Only each skill's name and description go into the system prompt; the model reads the `SKILL.md` when a task matches, and `/skill:name args` sends it explicitly (the `/` command palette lists them). More locations go in `config.json` as `"skills": ["~/.claude/skills"]` or on the command line with `--skill PATH`; `--no-skills` skips the default locations. When two skills share a name, explicit paths beat the project's, which beat the global ones.
|
|
59
|
+
|
|
60
|
+
## Using Paimon as a subagent
|
|
61
|
+
|
|
62
|
+
Frontier models are good at planning and reviewing; the steps in between are often mechanical. Point Paimon at a cheaper model and let Claude Code or Codex write the plan and check the result. Log in to that model's provider once, then name the model on each run:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
paimon login zai --api-key-env ZAI_API_KEY
|
|
66
|
+
paimon --model zai:glm-4.7 -p "apply the plan in PLAN.md" --mode auto --output-format result
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The bundled skill teaches the calling agent this workflow:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
paimon install-skill # into Claude Code (~/.claude/skills/paimon)
|
|
73
|
+
paimon install-skill --target codex # into Codex; --dest DIR for anywhere else
|
|
74
|
+
npx skills add aisk/paimon # the same skill, via skills.sh
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Using Paimon as a library
|
|
78
|
+
|
|
79
|
+
The agent loop is importable, so a Python program can drive it without going through the CLI. `Agent.open()` starts or resumes a session and `agent.run_until_idle()` yields typed events, one per text chunk, tool call and turn end, which the caller renders or filters however it likes. It returns once the agent is done, including any agents it started along the way: their answers come back later, and it runs the model again on each of them until nothing is left running:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import asyncio
|
|
83
|
+
|
|
84
|
+
from paimon.agent import Agent
|
|
85
|
+
from paimon.events import TextDelta
|
|
86
|
+
|
|
87
|
+
async def main():
|
|
88
|
+
agent = Agent.open(mode="auto")
|
|
89
|
+
async for event in agent.run_until_idle("summarize the tests in this directory"):
|
|
90
|
+
if isinstance(event, TextDelta):
|
|
91
|
+
print(event.text, end="", flush=True)
|
|
92
|
+
|
|
93
|
+
asyncio.run(main())
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`agent.run()` runs exactly one turn and returns with those agents possibly still at work; it is the call for a program that wants to decide itself when to run the model again, with `agent.run(None)`. An agent runs one turn or compaction at a time, and starting a second while one is under way raises `AgentBusyError`.
|
|
97
|
+
|
|
98
|
+
`Agent.open()` also takes a working directory, an async `confirm` callback for the permission prompts that are left, and a `toolset` to hand the model fewer tools or tools of your own. An agent holds its session until it goes away; to give it back at a definite moment, call `close()` or use the agent as a context manager. It writes the same session files as the CLI, so a run started in code can be resumed later with `paimon -r`.
|
|
99
|
+
|
|
100
|
+
## Sessions
|
|
101
|
+
|
|
102
|
+
Every conversation is saved, and long ones are summarized in place near the context limit. Paimon prints the command that brings a session back when you leave:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
paimon -r # choose a session started in this directory
|
|
106
|
+
paimon -r a1b2c3 # resume one by id
|
|
107
|
+
paimon -c # resume the most recent one
|
|
108
|
+
paimon sessions # list them (--json for machines)
|
|
109
|
+
paimon log a1b2c3 # what a session did, one line per event
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Other ways to run it
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
paimon --mode read # start in a more cautious permission mode (yolo is the default)
|
|
116
|
+
paimon --strict # hold every command, even read-only ones
|
|
117
|
+
paimon --no-web-search # take the web search tool away for this run
|
|
118
|
+
paimon --no-code-mode # take the run_code tool away (or "code_mode": false in config.json)
|
|
119
|
+
paimon --web # the same UI in a browser (--port, default 8000)
|
|
120
|
+
paimon -p "what does cli.py do?" # one answer on stdout, no UI
|
|
121
|
+
cat log.txt | paimon -p "summarize this"
|
|
122
|
+
paimon --model zai:glm-4.7 # this model for this run only
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`-p` never stops to ask, so with the default `yolo` mode it can already write files and run commands. Add `--output-format result` for a single JSON object with the outcome, which is what a calling program should read. `paimon --help` lists the rest.
|
|
126
|
+
|
|
127
|
+
Inside a [Herdr](https://herdr.dev) pane the UI reports its state and resume command to Herdr on its own, with nothing to install. It also reports its state to the terminal itself with [OSC 7501](https://mitchellh.com/writing/program-status-osc7501), for terminals that show it.
|
|
128
|
+
|
|
129
|
+
### Debugging the Textual UI
|
|
130
|
+
|
|
131
|
+
Start with `paimon --textual-debug` (also works with `--web`) when Paimon needs to diagnose its own layout or styling. It is off by default. When enabled, the model receives five tools scoped to the live UI process:
|
|
132
|
+
|
|
133
|
+
- `textual_inspect` queries the DOM by CSS selector and reports geometry, pseudo-classes, focus and computed CSS;
|
|
134
|
+
- `textual_eval` / `textual_exec` run Python in the UI process, including private APIs, persistent variables and top-level `await`;
|
|
135
|
+
- `textual_apply_css` layers on CSS and relayouts immediately;
|
|
136
|
+
- `textual_screenshot` exports the current render to SVG.
|
|
137
|
+
|
|
138
|
+
You can ask it, for example, to “find why `#prompt` is narrower than expected, try a live fix, and take a screenshot.” `textual_exec` deliberately provides unrestricted in-process Python and can change any Textual or Paimon state. Use this development option only with trusted prompts. Live changes last only until exit; after confirming a fix, ask Paimon to edit the source.
|
|
139
|
+
|
|
140
|
+
## Configuration
|
|
141
|
+
|
|
142
|
+
Model settings are kept in `~/.config/paimon/config.json`, written by the first launch or by `paimon login`. Each provider keeps its own credentials, and `paimon model provider:name` or Switch model in the command palette picks the model to run on. A ChatGPT plan works in place of an API key: `paimon login chatgpt` signs in through the browser. `paimon login antigravity` does the same for an Antigravity plan, which Google does not support outside Antigravity: Paimon signs in as the Antigravity client and calls an internal endpoint, so the account may be limited or closed, and the risk is yours. Sessions live in `~/.local/share/paimon/sessions/`.
|
|
143
|
+
|
|
144
|
+
Read and auto modes run a small set of clearly read-only commands (`ls`, `cat`, `git status`, …) on their own; `--strict` turns that off. On Windows commands run in Git Bash, found through `git` on your PATH or the standard install locations; without Git for Windows they run in cmd.exe, and then none are recognized. Read mode refuses everything else. Auto mode asks a reviewer model, which sees your messages and the agent's tool calls but not its reasoning or any tool output, and answers allow or block. It asks you instead when the reviewer cannot be reached or has blocked three calls in a row, and under `-p` those are refusals. Current models review with their provider's fast tier by default (GPT with Luna, Claude with Haiku, GLM with Flash) and the rest review with themselves; set `"review_model": "provider:name"` in the config to choose. **All of this is a guardrail against agent mistakes, not a security boundary.** For real isolation, run Paimon inside a container or VM.
|
|
145
|
+
|
|
146
|
+
## Architecture
|
|
147
|
+
|
|
148
|
+
`Agent.run` is a UI-agnostic stream of events; the TUI, `--web` and headless mode are three renderers over that one stream. An agent holds the subagents and background commands it starts. A subagent is another `Agent` running as a task in the same process, and its answer is delivered to its parent as a message. A UI that wants to show a job hooks `Agent.open_job`, which is how the TUI gives each one a pane.
|
|
149
|
+
|
|
150
|
+
```mermaid
|
|
151
|
+
flowchart TD
|
|
152
|
+
subgraph entry["Entry points"]
|
|
153
|
+
CLI["cli.py"]
|
|
154
|
+
Commands["commands.py<br/>status / login / model / sessions"]
|
|
155
|
+
Headless["headless.py<br/>-p, one-shot"]
|
|
156
|
+
App["app.py<br/>Textual TUI / --web"]
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
subgraph tui["TUI widgets"]
|
|
160
|
+
Pane["pane.py<br/>SessionPane"]
|
|
161
|
+
AgentPane["agentpane.py<br/>subagent pane"]
|
|
162
|
+
CommandPane["commandpane.py<br/>background command pane"]
|
|
163
|
+
Transcript["transcript.py<br/>conversation log, event renderer"]
|
|
164
|
+
Tabs["tabs.py<br/>pane strip"]
|
|
165
|
+
Login["login.py<br/>provider / key"]
|
|
166
|
+
UIWidgets["ui.py<br/>prompt input, confirmations"]
|
|
167
|
+
Diff["diff.py<br/>side-by-side diff rendering"]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
subgraph core["Agent loop"]
|
|
171
|
+
AgentLoop["agent.py<br/>Agent.run()"]
|
|
172
|
+
LLM["llm.py<br/>build_model()"]
|
|
173
|
+
PromptMod["prompt.py<br/>system prompt"]
|
|
174
|
+
ToolsMod["tools/<br/>tool REGISTRY"]
|
|
175
|
+
SessionMod["session.py<br/>JSONL persistence"]
|
|
176
|
+
Compaction["compaction.py"]
|
|
177
|
+
Retry["retry.py"]
|
|
178
|
+
Mentions["mentions.py<br/>@path expansion"]
|
|
179
|
+
JobMgr["jobs.py<br/>subagents, background commands"]
|
|
180
|
+
Jobs["turns.py<br/>turn driver"]
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
subgraph support["Config & skills"]
|
|
184
|
+
Config["config.py<br/>settings, credentials"]
|
|
185
|
+
Skills["skills.py<br/>Agent Skills discovery"]
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
CLI --> Commands
|
|
189
|
+
CLI --> Headless
|
|
190
|
+
CLI --> App
|
|
191
|
+
CLI --> Config
|
|
192
|
+
|
|
193
|
+
Headless --> AgentLoop
|
|
194
|
+
Headless --> Mentions
|
|
195
|
+
|
|
196
|
+
App --> Pane
|
|
197
|
+
App --> AgentPane
|
|
198
|
+
App --> CommandPane
|
|
199
|
+
App --> Tabs
|
|
200
|
+
App --> Login
|
|
201
|
+
App --> Config
|
|
202
|
+
|
|
203
|
+
Pane --> Jobs
|
|
204
|
+
Pane --> AgentLoop
|
|
205
|
+
Pane --> Diff
|
|
206
|
+
Pane --> Transcript
|
|
207
|
+
Pane --> UIWidgets
|
|
208
|
+
Pane --> LLM
|
|
209
|
+
AgentPane --> Pane
|
|
210
|
+
AgentPane --> Transcript
|
|
211
|
+
CommandPane --> Pane
|
|
212
|
+
Transcript --> UIWidgets
|
|
213
|
+
UIWidgets --> Diff
|
|
214
|
+
|
|
215
|
+
AgentLoop --> LLM
|
|
216
|
+
AgentLoop --> PromptMod
|
|
217
|
+
AgentLoop --> ToolsMod
|
|
218
|
+
AgentLoop --> SessionMod
|
|
219
|
+
AgentLoop --> Compaction
|
|
220
|
+
AgentLoop --> Retry
|
|
221
|
+
AgentLoop --> Mentions
|
|
222
|
+
AgentLoop --> JobMgr
|
|
223
|
+
JobMgr -. "spawn_agent" .-> AgentLoop
|
|
224
|
+
JobMgr -. "spawn_agent" .-> AgentPane
|
|
225
|
+
JobMgr -. "run_background" .-> CommandPane
|
|
226
|
+
|
|
227
|
+
PromptMod --> Skills
|
|
228
|
+
|
|
229
|
+
Jobs --> AgentLoop
|
|
230
|
+
|
|
231
|
+
Skills --> Config
|
|
232
|
+
Config --> LLM
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Telemetry
|
|
236
|
+
|
|
237
|
+
Each launch sends one anonymous event to Google Analytics: a random install id, the launch mode, the version, the OS name, the language from `LC_ALL`/`LANG`, whether this is the first launch, and the configured provider and model name. Nothing from your sessions, prompts, files or credentials is included. Set `PAIMON_NO_TELEMETRY=1` or `DO_NOT_TRACK=1` to turn it off.
|
paimon-0.3.0/README.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Paimon
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
English | [简体中文](README.zh-CN.md)
|
|
6
|
+
|
|
7
|
+
Paimon is a coding agent that lives in your terminal. It reads and edits files in the current directory and runs commands. It also runs headless and imports as a library, so a stronger agent or a program of your own can drive it.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
uv tool install paimon # or: pip install paimon
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Getting started
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
paimon
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or run it without installing anything:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
uvx paimon
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The first launch asks for a provider, its API base and key, then a model. Then just type what you want done. Write `@path/to/file` in a prompt to hand a file to the agent.
|
|
28
|
+
|
|
29
|
+
While it runs: `Shift+Tab` switches how much the agent may do on its own (**read** only reads, **auto** edits inside the working directory and has a second model call approve everything else, **yolo** checks nothing and is the default), `Esc` interrupts the current turn, `Ctrl+P` opens the command palette, `Ctrl+C` quits. A line starting with `!` runs in a shell instead of being sent, and Paimon sees what it printed. `!!` keeps it to yourself.
|
|
30
|
+
|
|
31
|
+
`Ctrl+T` opens another session in a pane of its own, `Ctrl+W` closes one, `Ctrl+PageUp` and `Ctrl+PageDown` move between them, and `Ctrl+G` jumps to a pane waiting for permission. `Ctrl+L` returns keyboard focus to the current pane's input or pending answer. The status bar identifies input, reading, and answer focus; pane switches preserve reading focus, and background completion does not steal the keyboard. Paimon can work in parallel: ask for two independent things and it starts a second agent in a tab of its own, where you can watch it work and answer what it asks, and whose answer comes back into the conversation when it is done. Closing the tab stops it. It can also leave a command running in a tab, a dev server or a watcher, instead of holding up a turn.
|
|
32
|
+
|
|
33
|
+
## Web search
|
|
34
|
+
|
|
35
|
+
Paimon can search the web without an API key. It asks in no permission mode, because a search changes nothing on your machine. Searches go through [ddgs](https://github.com/deedy5/ddgs), a metasearch library, so a query may be sent to any of several public search engines. `--no-web-search` takes the tool away for a run.
|
|
36
|
+
|
|
37
|
+
## Skills
|
|
38
|
+
|
|
39
|
+
Paimon loads [Agent Skills](https://agentskills.io) from `~/.config/paimon/skills`, `~/.agents/skills` and every `.agents/skills` from the working directory up to the repository root. Only each skill's name and description go into the system prompt; the model reads the `SKILL.md` when a task matches, and `/skill:name args` sends it explicitly (the `/` command palette lists them). More locations go in `config.json` as `"skills": ["~/.claude/skills"]` or on the command line with `--skill PATH`; `--no-skills` skips the default locations. When two skills share a name, explicit paths beat the project's, which beat the global ones.
|
|
40
|
+
|
|
41
|
+
## Using Paimon as a subagent
|
|
42
|
+
|
|
43
|
+
Frontier models are good at planning and reviewing; the steps in between are often mechanical. Point Paimon at a cheaper model and let Claude Code or Codex write the plan and check the result. Log in to that model's provider once, then name the model on each run:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
paimon login zai --api-key-env ZAI_API_KEY
|
|
47
|
+
paimon --model zai:glm-4.7 -p "apply the plan in PLAN.md" --mode auto --output-format result
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The bundled skill teaches the calling agent this workflow:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
paimon install-skill # into Claude Code (~/.claude/skills/paimon)
|
|
54
|
+
paimon install-skill --target codex # into Codex; --dest DIR for anywhere else
|
|
55
|
+
npx skills add aisk/paimon # the same skill, via skills.sh
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Using Paimon as a library
|
|
59
|
+
|
|
60
|
+
The agent loop is importable, so a Python program can drive it without going through the CLI. `Agent.open()` starts or resumes a session and `agent.run_until_idle()` yields typed events, one per text chunk, tool call and turn end, which the caller renders or filters however it likes. It returns once the agent is done, including any agents it started along the way: their answers come back later, and it runs the model again on each of them until nothing is left running:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import asyncio
|
|
64
|
+
|
|
65
|
+
from paimon.agent import Agent
|
|
66
|
+
from paimon.events import TextDelta
|
|
67
|
+
|
|
68
|
+
async def main():
|
|
69
|
+
agent = Agent.open(mode="auto")
|
|
70
|
+
async for event in agent.run_until_idle("summarize the tests in this directory"):
|
|
71
|
+
if isinstance(event, TextDelta):
|
|
72
|
+
print(event.text, end="", flush=True)
|
|
73
|
+
|
|
74
|
+
asyncio.run(main())
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`agent.run()` runs exactly one turn and returns with those agents possibly still at work; it is the call for a program that wants to decide itself when to run the model again, with `agent.run(None)`. An agent runs one turn or compaction at a time, and starting a second while one is under way raises `AgentBusyError`.
|
|
78
|
+
|
|
79
|
+
`Agent.open()` also takes a working directory, an async `confirm` callback for the permission prompts that are left, and a `toolset` to hand the model fewer tools or tools of your own. An agent holds its session until it goes away; to give it back at a definite moment, call `close()` or use the agent as a context manager. It writes the same session files as the CLI, so a run started in code can be resumed later with `paimon -r`.
|
|
80
|
+
|
|
81
|
+
## Sessions
|
|
82
|
+
|
|
83
|
+
Every conversation is saved, and long ones are summarized in place near the context limit. Paimon prints the command that brings a session back when you leave:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
paimon -r # choose a session started in this directory
|
|
87
|
+
paimon -r a1b2c3 # resume one by id
|
|
88
|
+
paimon -c # resume the most recent one
|
|
89
|
+
paimon sessions # list them (--json for machines)
|
|
90
|
+
paimon log a1b2c3 # what a session did, one line per event
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Other ways to run it
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
paimon --mode read # start in a more cautious permission mode (yolo is the default)
|
|
97
|
+
paimon --strict # hold every command, even read-only ones
|
|
98
|
+
paimon --no-web-search # take the web search tool away for this run
|
|
99
|
+
paimon --no-code-mode # take the run_code tool away (or "code_mode": false in config.json)
|
|
100
|
+
paimon --web # the same UI in a browser (--port, default 8000)
|
|
101
|
+
paimon -p "what does cli.py do?" # one answer on stdout, no UI
|
|
102
|
+
cat log.txt | paimon -p "summarize this"
|
|
103
|
+
paimon --model zai:glm-4.7 # this model for this run only
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`-p` never stops to ask, so with the default `yolo` mode it can already write files and run commands. Add `--output-format result` for a single JSON object with the outcome, which is what a calling program should read. `paimon --help` lists the rest.
|
|
107
|
+
|
|
108
|
+
Inside a [Herdr](https://herdr.dev) pane the UI reports its state and resume command to Herdr on its own, with nothing to install. It also reports its state to the terminal itself with [OSC 7501](https://mitchellh.com/writing/program-status-osc7501), for terminals that show it.
|
|
109
|
+
|
|
110
|
+
### Debugging the Textual UI
|
|
111
|
+
|
|
112
|
+
Start with `paimon --textual-debug` (also works with `--web`) when Paimon needs to diagnose its own layout or styling. It is off by default. When enabled, the model receives five tools scoped to the live UI process:
|
|
113
|
+
|
|
114
|
+
- `textual_inspect` queries the DOM by CSS selector and reports geometry, pseudo-classes, focus and computed CSS;
|
|
115
|
+
- `textual_eval` / `textual_exec` run Python in the UI process, including private APIs, persistent variables and top-level `await`;
|
|
116
|
+
- `textual_apply_css` layers on CSS and relayouts immediately;
|
|
117
|
+
- `textual_screenshot` exports the current render to SVG.
|
|
118
|
+
|
|
119
|
+
You can ask it, for example, to “find why `#prompt` is narrower than expected, try a live fix, and take a screenshot.” `textual_exec` deliberately provides unrestricted in-process Python and can change any Textual or Paimon state. Use this development option only with trusted prompts. Live changes last only until exit; after confirming a fix, ask Paimon to edit the source.
|
|
120
|
+
|
|
121
|
+
## Configuration
|
|
122
|
+
|
|
123
|
+
Model settings are kept in `~/.config/paimon/config.json`, written by the first launch or by `paimon login`. Each provider keeps its own credentials, and `paimon model provider:name` or Switch model in the command palette picks the model to run on. A ChatGPT plan works in place of an API key: `paimon login chatgpt` signs in through the browser. `paimon login antigravity` does the same for an Antigravity plan, which Google does not support outside Antigravity: Paimon signs in as the Antigravity client and calls an internal endpoint, so the account may be limited or closed, and the risk is yours. Sessions live in `~/.local/share/paimon/sessions/`.
|
|
124
|
+
|
|
125
|
+
Read and auto modes run a small set of clearly read-only commands (`ls`, `cat`, `git status`, …) on their own; `--strict` turns that off. On Windows commands run in Git Bash, found through `git` on your PATH or the standard install locations; without Git for Windows they run in cmd.exe, and then none are recognized. Read mode refuses everything else. Auto mode asks a reviewer model, which sees your messages and the agent's tool calls but not its reasoning or any tool output, and answers allow or block. It asks you instead when the reviewer cannot be reached or has blocked three calls in a row, and under `-p` those are refusals. Current models review with their provider's fast tier by default (GPT with Luna, Claude with Haiku, GLM with Flash) and the rest review with themselves; set `"review_model": "provider:name"` in the config to choose. **All of this is a guardrail against agent mistakes, not a security boundary.** For real isolation, run Paimon inside a container or VM.
|
|
126
|
+
|
|
127
|
+
## Architecture
|
|
128
|
+
|
|
129
|
+
`Agent.run` is a UI-agnostic stream of events; the TUI, `--web` and headless mode are three renderers over that one stream. An agent holds the subagents and background commands it starts. A subagent is another `Agent` running as a task in the same process, and its answer is delivered to its parent as a message. A UI that wants to show a job hooks `Agent.open_job`, which is how the TUI gives each one a pane.
|
|
130
|
+
|
|
131
|
+
```mermaid
|
|
132
|
+
flowchart TD
|
|
133
|
+
subgraph entry["Entry points"]
|
|
134
|
+
CLI["cli.py"]
|
|
135
|
+
Commands["commands.py<br/>status / login / model / sessions"]
|
|
136
|
+
Headless["headless.py<br/>-p, one-shot"]
|
|
137
|
+
App["app.py<br/>Textual TUI / --web"]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
subgraph tui["TUI widgets"]
|
|
141
|
+
Pane["pane.py<br/>SessionPane"]
|
|
142
|
+
AgentPane["agentpane.py<br/>subagent pane"]
|
|
143
|
+
CommandPane["commandpane.py<br/>background command pane"]
|
|
144
|
+
Transcript["transcript.py<br/>conversation log, event renderer"]
|
|
145
|
+
Tabs["tabs.py<br/>pane strip"]
|
|
146
|
+
Login["login.py<br/>provider / key"]
|
|
147
|
+
UIWidgets["ui.py<br/>prompt input, confirmations"]
|
|
148
|
+
Diff["diff.py<br/>side-by-side diff rendering"]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
subgraph core["Agent loop"]
|
|
152
|
+
AgentLoop["agent.py<br/>Agent.run()"]
|
|
153
|
+
LLM["llm.py<br/>build_model()"]
|
|
154
|
+
PromptMod["prompt.py<br/>system prompt"]
|
|
155
|
+
ToolsMod["tools/<br/>tool REGISTRY"]
|
|
156
|
+
SessionMod["session.py<br/>JSONL persistence"]
|
|
157
|
+
Compaction["compaction.py"]
|
|
158
|
+
Retry["retry.py"]
|
|
159
|
+
Mentions["mentions.py<br/>@path expansion"]
|
|
160
|
+
JobMgr["jobs.py<br/>subagents, background commands"]
|
|
161
|
+
Jobs["turns.py<br/>turn driver"]
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
subgraph support["Config & skills"]
|
|
165
|
+
Config["config.py<br/>settings, credentials"]
|
|
166
|
+
Skills["skills.py<br/>Agent Skills discovery"]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
CLI --> Commands
|
|
170
|
+
CLI --> Headless
|
|
171
|
+
CLI --> App
|
|
172
|
+
CLI --> Config
|
|
173
|
+
|
|
174
|
+
Headless --> AgentLoop
|
|
175
|
+
Headless --> Mentions
|
|
176
|
+
|
|
177
|
+
App --> Pane
|
|
178
|
+
App --> AgentPane
|
|
179
|
+
App --> CommandPane
|
|
180
|
+
App --> Tabs
|
|
181
|
+
App --> Login
|
|
182
|
+
App --> Config
|
|
183
|
+
|
|
184
|
+
Pane --> Jobs
|
|
185
|
+
Pane --> AgentLoop
|
|
186
|
+
Pane --> Diff
|
|
187
|
+
Pane --> Transcript
|
|
188
|
+
Pane --> UIWidgets
|
|
189
|
+
Pane --> LLM
|
|
190
|
+
AgentPane --> Pane
|
|
191
|
+
AgentPane --> Transcript
|
|
192
|
+
CommandPane --> Pane
|
|
193
|
+
Transcript --> UIWidgets
|
|
194
|
+
UIWidgets --> Diff
|
|
195
|
+
|
|
196
|
+
AgentLoop --> LLM
|
|
197
|
+
AgentLoop --> PromptMod
|
|
198
|
+
AgentLoop --> ToolsMod
|
|
199
|
+
AgentLoop --> SessionMod
|
|
200
|
+
AgentLoop --> Compaction
|
|
201
|
+
AgentLoop --> Retry
|
|
202
|
+
AgentLoop --> Mentions
|
|
203
|
+
AgentLoop --> JobMgr
|
|
204
|
+
JobMgr -. "spawn_agent" .-> AgentLoop
|
|
205
|
+
JobMgr -. "spawn_agent" .-> AgentPane
|
|
206
|
+
JobMgr -. "run_background" .-> CommandPane
|
|
207
|
+
|
|
208
|
+
PromptMod --> Skills
|
|
209
|
+
|
|
210
|
+
Jobs --> AgentLoop
|
|
211
|
+
|
|
212
|
+
Skills --> Config
|
|
213
|
+
Config --> LLM
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Telemetry
|
|
217
|
+
|
|
218
|
+
Each launch sends one anonymous event to Google Analytics: a random install id, the launch mode, the version, the OS name, the language from `LC_ALL`/`LANG`, whether this is the first launch, and the configured provider and model name. Nothing from your sessions, prompts, files or credentials is included. Set `PAIMON_NO_TELEMETRY=1` or `DO_NOT_TRACK=1` to turn it off.
|
|
@@ -24,11 +24,11 @@ paimon
|
|
|
24
24
|
uvx paimon
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
首次启动会询问 provider
|
|
27
|
+
首次启动会询问 provider 及其 API base 和 key,然后选择模型。之后输入要完成的任务即可。在提示中写 `@path/to/file` 可以把文件提供给 agent。
|
|
28
28
|
|
|
29
|
-
运行时:`Shift+Tab` 切换 agent 的自主程度(**read**
|
|
29
|
+
运行时:`Shift+Tab` 切换 agent 的自主程度(**read** 只读,**auto** 放行工作目录内的编辑,其余操作交给另一次模型调用审批,**yolo** 不做任何检查,也是默认值),`Esc` 打断当前回合,`Ctrl+P` 打开命令面板,`Ctrl+C` 退出。以 `!` 开头的一行不发给模型,而是直接在 shell 里执行,输出 Paimon 也能看到。`!!` 则不告诉它。
|
|
30
30
|
|
|
31
|
-
`Ctrl+T` 在新 pane 里打开另一个会话,`Ctrl+W` 关闭当前 pane,`Ctrl+PageUp` 和 `Ctrl+PageDown` 在 pane 之间切换,`Ctrl+G` 跳到正在等待授权的 pane
|
|
31
|
+
`Ctrl+T` 在新 pane 里打开另一个会话,`Ctrl+W` 关闭当前 pane,`Ctrl+PageUp` 和 `Ctrl+PageDown` 在 pane 之间切换,`Ctrl+G` 跳到正在等待授权的 pane,`Ctrl+L` 将键盘焦点返回当前 pane 的输入框或待回答面板。状态栏会显示键盘当前在输入、阅读还是回答区域;切换 pane 会保留阅读焦点,后台任务完成不会抢走键盘。Paimon 也能并行干活:让它同时做两件互不相干的事,它会在单独的 tab 里起第二个 agent,你可以在那里看它干活、回答它的授权请求,做完后结果会回到当前会话里,关掉 tab 就是停掉它。它也能把一条命令留在 tab 里跑,比如开发服务器或者文件监视,不占着当前回合。
|
|
32
32
|
|
|
33
33
|
## 网页搜索
|
|
34
34
|
|
|
@@ -40,11 +40,11 @@ Paimon 会从 `~/.config/paimon/skills`、`~/.agents/skills` 以及工作目录
|
|
|
40
40
|
|
|
41
41
|
## 当作 subagent 使用
|
|
42
42
|
|
|
43
|
-
前沿模型擅长制定计划和验收结果,中间的执行步骤往往比较机械。让 Paimon 使用成本较低的模型执行,由 Claude Code 或 Codex
|
|
43
|
+
前沿模型擅长制定计划和验收结果,中间的执行步骤往往比较机械。让 Paimon 使用成本较低的模型执行,由 Claude Code 或 Codex 制定计划并检查结果。先登录该模型的 provider,之后每次运行时指定模型:
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
|
-
paimon login
|
|
47
|
-
paimon --
|
|
46
|
+
paimon login zai --api-key-env ZAI_API_KEY
|
|
47
|
+
paimon --model zai:glm-4.7 -p "apply the plan in PLAN.md" --mode auto --output-format result
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
自带的 skill 会向调用方 agent 说明这套流程:
|
|
@@ -57,23 +57,26 @@ npx skills add aisk/paimon # 通过 skills.sh 安装同一个 skill
|
|
|
57
57
|
|
|
58
58
|
## 当作库使用
|
|
59
59
|
|
|
60
|
-
agent 循环本身是可以导入的,Python 程序不必经过 CLI 就能驱动它。`Agent.open()` 新建或恢复一个会话,`agent.
|
|
60
|
+
agent 循环本身是可以导入的,Python 程序不必经过 CLI 就能驱动它。`Agent.open()` 新建或恢复一个会话,`agent.run_until_idle()` 产出类型化的事件,每段文本、每次工具调用、每个回合结束各一个,调用方自己决定怎么渲染或过滤。它在 agent 做完之后才返回,包括它中途启动的其他 agent:这些 agent 的回答稍后才到,每到一个它就让模型再跑一轮,直到没有还在运行的为止:
|
|
61
61
|
|
|
62
62
|
```python
|
|
63
63
|
import asyncio
|
|
64
64
|
|
|
65
|
-
from paimon.agent import Agent
|
|
65
|
+
from paimon.agent import Agent
|
|
66
|
+
from paimon.events import TextDelta
|
|
66
67
|
|
|
67
68
|
async def main():
|
|
68
|
-
agent = Agent.open(mode="
|
|
69
|
-
async for event in agent.
|
|
69
|
+
agent = Agent.open(mode="auto")
|
|
70
|
+
async for event in agent.run_until_idle("summarize the tests in this directory"):
|
|
70
71
|
if isinstance(event, TextDelta):
|
|
71
72
|
print(event.text, end="", flush=True)
|
|
72
73
|
|
|
73
74
|
asyncio.run(main())
|
|
74
75
|
```
|
|
75
76
|
|
|
76
|
-
`
|
|
77
|
+
`agent.run()` 只跑一个回合,返回时它启动的 agent 可能还在运行;想自己决定什么时候用 `agent.run(None)` 让模型再跑一轮的程序,用它。同一个 agent 一次只跑一个回合或一次压缩,前一个还没结束就开始下一个会抛出 `AgentBusyError`。
|
|
78
|
+
|
|
79
|
+
`Agent.open()` 还接受工作目录、用于剩余权限确认的异步 `confirm` 回调,以及 `toolset`,可以只给模型一部分工具,或者换成你自己的工具。agent 被回收时会交还会话,想在确定的时刻交还就调 `close()`,或者把它当上下文管理器用。它写的会话文件和 CLI 一样,所以代码里跑出来的会话之后可以用 `paimon -r` 恢复。
|
|
77
80
|
|
|
78
81
|
## 会话
|
|
79
82
|
|
|
@@ -91,41 +94,53 @@ paimon log a1b2c3 # 查看会话做了什么,每个事件一行
|
|
|
91
94
|
|
|
92
95
|
```bash
|
|
93
96
|
paimon --mode read # 以更谨慎的权限模式启动(默认为 yolo)
|
|
94
|
-
paimon --strict #
|
|
97
|
+
paimon --strict # 不再放行只读命令
|
|
95
98
|
paimon --no-web-search # 本次运行不提供网页搜索工具
|
|
96
99
|
paimon --web # 在浏览器中使用同一套 UI(--port,默认 8000)
|
|
97
100
|
paimon -p "what does cli.py do?" # 直接在 stdout 输出回答,不启动 UI
|
|
98
101
|
cat log.txt | paimon -p "summarize this"
|
|
99
102
|
paimon --model zai:glm-4.7 # 仅本次运行使用该模型
|
|
100
|
-
paimon --profile work # 单独配置的另一个账号
|
|
101
103
|
```
|
|
102
104
|
|
|
103
105
|
`-p` 不会停下来询问,配合默认的 `yolo` 模式,它已经可以修改文件和执行命令。加 `--output-format result` 会输出一个包含结果的 JSON 对象,调用方程序读这个就够了。其余选项见 `paimon --help`。
|
|
104
106
|
|
|
107
|
+
### 调试 Textual UI
|
|
108
|
+
|
|
109
|
+
需要让 Paimon 排查自己的布局或样式时,用 `paimon --textual-debug` 启动(也可与 `--web` 一起使用)。默认不启用;开启后模型会多出五个只作用于当前 UI 进程的工具:
|
|
110
|
+
|
|
111
|
+
- `textual_inspect`:按 CSS selector 查看 DOM、geometry、pseudo-classes、焦点和 computed CSS;
|
|
112
|
+
- `textual_eval` / `textual_exec`:在 UI 进程里运行 Python,支持 private API、持久变量和 top-level `await`;
|
|
113
|
+
- `textual_apply_css`:动态叠加 CSS 并立即重新布局;
|
|
114
|
+
- `textual_screenshot`:把当前渲染结果导出成 SVG。
|
|
115
|
+
|
|
116
|
+
例如可以直接让它“检查 `#prompt` 为什么比预期窄,动态试一个修复并截图”。`textual_exec` 有意提供完整的进程内 Python 能力,能修改任意 Textual/Paimon 状态;这个选项只适合开发调试,不应对不受信任的 prompt 开启。动态修改只保留到进程退出,确定修复后仍需让 Paimon编辑源码。
|
|
117
|
+
|
|
105
118
|
## 配置
|
|
106
119
|
|
|
107
|
-
|
|
120
|
+
模型设置保存在 `~/.config/paimon/config.json`,由首次启动或 `paimon login` 写入。每个 provider 各自保存凭据,用 `paimon model provider:name` 或命令面板里的 Switch model 选择要用的模型。也可以用 ChatGPT 订阅代替 API key,`paimon login chatgpt` 会通过浏览器登录。会话存放在 `~/.local/share/paimon/sessions/`。
|
|
108
121
|
|
|
109
|
-
read 和
|
|
122
|
+
read 和 auto 模式会直接执行一小组明确只读的命令(`ls`、`cat`、`git status` 等),`--strict` 可以关掉。Windows 上命令由 Git Bash 执行,通过 PATH 上的 `git` 或默认安装位置找到;没装 Git for Windows 时改由 cmd.exe 执行,这时一条也不识别。read 模式拒绝其余所有操作。auto 模式把它们交给审批模型,它能看到你的消息和 agent 的工具调用,看不到 agent 的推理和工具输出,只回答放行或拦截。审批模型不可用或者连续拦截三次时改为询问你,`-p` 下则直接拒绝。当前的模型默认用同一家里快速档的那个来审批(GPT 用 Luna,Claude 用 Haiku,GLM 用 Flash),其余模型自己审自己,在配置里写 `"review_model": "provider:name"` 可以自行指定。**这些都是防止 agent 失误的护栏,不是安全边界。** 需要真正的隔离时,请在容器或虚拟机中运行 Paimon。
|
|
110
123
|
|
|
111
124
|
## 架构
|
|
112
125
|
|
|
113
|
-
`Agent.run` 产出一串与 UI 无关的事件流,TUI、`--web` 和无头模式都是这条事件流的渲染器。agent 自己持有它启动的子 agent 和后台命令。子 agent 是同一进程里作为任务运行的另一个 `Agent`,它的结果会作为一条消息送回父 agent
|
|
126
|
+
`Agent.run` 产出一串与 UI 无关的事件流,TUI、`--web` 和无头模式都是这条事件流的渲染器。agent 自己持有它启动的子 agent 和后台命令。子 agent 是同一进程里作为任务运行的另一个 `Agent`,它的结果会作为一条消息送回父 agent。想把 job 显示出来的 UI 挂上 `Agent.open_job` 即可,TUI 就是这样给每个 job 开 pane 的。
|
|
114
127
|
|
|
115
128
|
```mermaid
|
|
116
129
|
flowchart TD
|
|
117
130
|
subgraph entry["入口"]
|
|
118
131
|
CLI["cli.py"]
|
|
119
|
-
Commands["commands.py<br/>status / login / sessions"]
|
|
132
|
+
Commands["commands.py<br/>status / login / model / sessions"]
|
|
120
133
|
Headless["headless.py<br/>-p,单次运行"]
|
|
121
134
|
App["app.py<br/>Textual TUI / --web"]
|
|
122
135
|
end
|
|
123
136
|
|
|
124
137
|
subgraph tui["TUI 组件"]
|
|
125
138
|
Pane["pane.py<br/>SessionPane"]
|
|
139
|
+
AgentPane["agentpane.py<br/>子 agent pane"]
|
|
126
140
|
CommandPane["commandpane.py<br/>后台命令 pane"]
|
|
141
|
+
Transcript["transcript.py<br/>对话记录与事件渲染"]
|
|
127
142
|
Tabs["tabs.py<br/>pane 标签栏"]
|
|
128
|
-
Login["login.py<br/>provider /
|
|
143
|
+
Login["login.py<br/>provider / key"]
|
|
129
144
|
UIWidgets["ui.py<br/>输入框、确认面板"]
|
|
130
145
|
Diff["diff.py<br/>并排 diff 渲染"]
|
|
131
146
|
end
|
|
@@ -134,17 +149,17 @@ flowchart TD
|
|
|
134
149
|
AgentLoop["agent.py<br/>Agent.run()"]
|
|
135
150
|
LLM["llm.py<br/>build_model()"]
|
|
136
151
|
PromptMod["prompt.py<br/>system prompt"]
|
|
137
|
-
ToolsMod["tools
|
|
152
|
+
ToolsMod["tools/<br/>工具注册表"]
|
|
138
153
|
SessionMod["session.py<br/>JSONL 持久化"]
|
|
139
154
|
Compaction["compaction.py"]
|
|
140
|
-
ModelWindows["model_windows.py<br/>各模型上下文窗口大小"]
|
|
141
155
|
Retry["retry.py"]
|
|
142
156
|
Mentions["mentions.py<br/>@path 展开"]
|
|
157
|
+
JobMgr["jobs.py<br/>子代理、后台命令"]
|
|
143
158
|
Jobs["turns.py<br/>回合驱动"]
|
|
144
159
|
end
|
|
145
160
|
|
|
146
161
|
subgraph support["配置与 skills"]
|
|
147
|
-
Config["config.py<br
|
|
162
|
+
Config["config.py<br/>设置、凭证"]
|
|
148
163
|
Skills["skills.py<br/>Agent Skills 发现"]
|
|
149
164
|
end
|
|
150
165
|
|
|
@@ -157,6 +172,7 @@ flowchart TD
|
|
|
157
172
|
Headless --> Mentions
|
|
158
173
|
|
|
159
174
|
App --> Pane
|
|
175
|
+
App --> AgentPane
|
|
160
176
|
App --> CommandPane
|
|
161
177
|
App --> Tabs
|
|
162
178
|
App --> Login
|
|
@@ -165,9 +181,13 @@ flowchart TD
|
|
|
165
181
|
Pane --> Jobs
|
|
166
182
|
Pane --> AgentLoop
|
|
167
183
|
Pane --> Diff
|
|
184
|
+
Pane --> Transcript
|
|
168
185
|
Pane --> UIWidgets
|
|
169
186
|
Pane --> LLM
|
|
187
|
+
AgentPane --> Pane
|
|
188
|
+
AgentPane --> Transcript
|
|
170
189
|
CommandPane --> Pane
|
|
190
|
+
Transcript --> UIWidgets
|
|
171
191
|
UIWidgets --> Diff
|
|
172
192
|
|
|
173
193
|
AgentLoop --> LLM
|
|
@@ -177,11 +197,12 @@ flowchart TD
|
|
|
177
197
|
AgentLoop --> Compaction
|
|
178
198
|
AgentLoop --> Retry
|
|
179
199
|
AgentLoop --> Mentions
|
|
180
|
-
AgentLoop
|
|
181
|
-
|
|
200
|
+
AgentLoop --> JobMgr
|
|
201
|
+
JobMgr -. "spawn_agent" .-> AgentLoop
|
|
202
|
+
JobMgr -. "spawn_agent" .-> AgentPane
|
|
203
|
+
JobMgr -. "run_background" .-> CommandPane
|
|
182
204
|
|
|
183
205
|
PromptMod --> Skills
|
|
184
|
-
Compaction --> ModelWindows
|
|
185
206
|
|
|
186
207
|
Jobs --> AgentLoop
|
|
187
208
|
|