paimon 0.2.5__tar.gz → 0.2.7__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.2.5/paimon.egg-info → paimon-0.2.7}/PKG-INFO +97 -8
- {paimon-0.2.5 → paimon-0.2.7}/README.md +94 -6
- {paimon-0.2.5 → paimon-0.2.7}/README.zh-CN.md +92 -6
- paimon-0.2.7/paimon/_grep_worker.py +16 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon/agent.py +490 -117
- {paimon-0.2.5 → paimon-0.2.7}/paimon/app.py +112 -124
- {paimon-0.2.5 → paimon-0.2.7}/paimon/app.tcss +16 -5
- {paimon-0.2.5 → paimon-0.2.7}/paimon/cli.py +34 -12
- {paimon-0.2.5 → paimon-0.2.7}/paimon/commandpane.py +37 -33
- {paimon-0.2.5 → paimon-0.2.7}/paimon/compaction.py +5 -16
- {paimon-0.2.5 → paimon-0.2.7}/paimon/config.py +3 -7
- {paimon-0.2.5 → paimon-0.2.7}/paimon/diff.py +1 -1
- {paimon-0.2.5 → paimon-0.2.7}/paimon/headless.py +42 -16
- paimon-0.2.7/paimon/herdr.py +145 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon/llm.py +24 -2
- {paimon-0.2.5 → paimon-0.2.7}/paimon/lockfile.py +39 -3
- {paimon-0.2.5 → paimon-0.2.7}/paimon/pane.py +345 -138
- {paimon-0.2.5 → paimon-0.2.7}/paimon/prompt.py +56 -29
- {paimon-0.2.5 → paimon-0.2.7}/paimon/session.py +49 -31
- {paimon-0.2.5 → paimon-0.2.7}/paimon/skill/SKILL.md +5 -2
- {paimon-0.2.5 → paimon-0.2.7}/paimon/tools.py +286 -203
- paimon-0.2.7/paimon/turns.py +198 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon/ui.py +128 -10
- paimon-0.2.7/paimon/websearch.py +126 -0
- {paimon-0.2.5 → paimon-0.2.7/paimon.egg-info}/PKG-INFO +97 -8
- {paimon-0.2.5 → paimon-0.2.7}/paimon.egg-info/SOURCES.txt +4 -4
- {paimon-0.2.5 → paimon-0.2.7}/paimon.egg-info/requires.txt +2 -1
- {paimon-0.2.5 → paimon-0.2.7}/paimon.egg-info/scm_file_list.json +27 -9
- paimon-0.2.7/paimon.egg-info/scm_version.json +8 -0
- {paimon-0.2.5 → paimon-0.2.7}/pyproject.toml +2 -1
- paimon-0.2.5/paimon/agents.py +0 -259
- paimon-0.2.5/paimon/aside.py +0 -165
- paimon-0.2.5/paimon/jobs.py +0 -454
- paimon-0.2.5/paimon/supervisor.py +0 -425
- paimon-0.2.5/paimon.egg-info/scm_version.json +0 -8
- {paimon-0.2.5 → paimon-0.2.7}/LICENSE +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/MANIFEST.in +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon/__init__.py +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon/__main__.py +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon/commands.py +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon/errors.py +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon/login.py +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon/mentions.py +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon/model_windows.py +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon/retry.py +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon/skills.py +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon/tabs.py +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon/telemetry.py +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon.egg-info/dependency_links.txt +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon.egg-info/entry_points.txt +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/paimon.egg-info/top_level.txt +0 -0
- {paimon-0.2.5 → paimon-0.2.7}/setup.cfg +0 -0
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: paimon
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: A minimal code agent built on pydantic-ai + textual
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Requires-Dist: httpx>=0.27
|
|
9
|
-
Requires-Dist: pydantic-ai-slim[anthropic,openai]>=2.
|
|
9
|
+
Requires-Dist: pydantic-ai-slim[anthropic,openai]>=2.38.0
|
|
10
10
|
Requires-Dist: textual>=8.2.7
|
|
11
11
|
Requires-Dist: textual-serve>=1.1.3
|
|
12
12
|
Requires-Dist: pyyaml>=6
|
|
13
|
+
Requires-Dist: ddgs>=9
|
|
13
14
|
Dynamic: license-file
|
|
14
15
|
|
|
15
16
|
# Paimon
|
|
@@ -40,9 +41,13 @@ uvx paimon
|
|
|
40
41
|
|
|
41
42
|
The first launch asks for a provider, model, API base and key. Then just type what you want done. Write `@path/to/file` in a prompt to hand a file to the agent.
|
|
42
43
|
|
|
43
|
-
While it runs: `Shift+Tab` switches how much the agent may do on its own (**read** asks before writing files or running commands, **edit** lets edits inside the working directory through, **yolo** never asks and is the default), `Esc` interrupts the current turn, `Ctrl+P` opens the command palette, `Ctrl+C` quits.
|
|
44
|
+
While it runs: `Shift+Tab` switches how much the agent may do on its own (**read** asks before writing files or running commands, **edit** lets edits inside the working directory through, **yolo** never asks 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.
|
|
44
45
|
|
|
45
|
-
`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. Paimon can
|
|
46
|
+
`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. Paimon can work in parallel: ask for two independent things and it starts a second agent in the background, whose answer comes back into the conversation when it is done. It can also leave a command running in a tab of its own, a dev server or a watcher, instead of holding up a turn.
|
|
47
|
+
|
|
48
|
+
## Web search
|
|
49
|
+
|
|
50
|
+
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.
|
|
46
51
|
|
|
47
52
|
## Skills
|
|
48
53
|
|
|
@@ -102,6 +107,7 @@ paimon log a1b2c3 # what a session did, one line per event
|
|
|
102
107
|
```bash
|
|
103
108
|
paimon --mode read # start in a more cautious permission mode (yolo is the default)
|
|
104
109
|
paimon --strict # ask before every command, even read-only ones
|
|
110
|
+
paimon --no-web-search # take the web search tool away for this run
|
|
105
111
|
paimon --web # the same UI in a browser (--port, default 8000)
|
|
106
112
|
paimon -p "what does cli.py do?" # one answer on stdout, no UI
|
|
107
113
|
cat log.txt | paimon -p "summarize this"
|
|
@@ -111,12 +117,95 @@ paimon --profile work # a separately configured account
|
|
|
111
117
|
|
|
112
118
|
`-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.
|
|
113
119
|
|
|
114
|
-
|
|
120
|
+
Inside a [Herdr](https://herdr.dev) pane the UI reports its state and resume command to Herdr on its own, with nothing to install.
|
|
115
121
|
|
|
116
|
-
|
|
122
|
+
## Configuration
|
|
117
123
|
|
|
118
|
-
|
|
124
|
+
Each profile keeps its model settings in `~/.config/paimon/<name>/config.json`, written by the first launch or by `paimon login`. Sessions live in `~/.local/share/paimon/sessions/`.
|
|
125
|
+
|
|
126
|
+
Read and edit modes run a small set of clearly read-only commands (`ls`, `cat`, `git status`, …) without asking; `--strict` turns that off. On Windows, where cmd.exe runs the commands, every command asks. **This is a guardrail against agent mistakes, not a security boundary.** For real isolation, run Paimon inside a container or VM.
|
|
127
|
+
|
|
128
|
+
## Architecture
|
|
129
|
+
|
|
130
|
+
`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.
|
|
131
|
+
|
|
132
|
+
```mermaid
|
|
133
|
+
flowchart TD
|
|
134
|
+
subgraph entry["Entry points"]
|
|
135
|
+
CLI["cli.py"]
|
|
136
|
+
Commands["commands.py<br/>status / login / sessions"]
|
|
137
|
+
Headless["headless.py<br/>-p, one-shot"]
|
|
138
|
+
App["app.py<br/>Textual TUI / --web"]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
subgraph tui["TUI widgets"]
|
|
142
|
+
Pane["pane.py<br/>SessionPane"]
|
|
143
|
+
CommandPane["commandpane.py<br/>background command pane"]
|
|
144
|
+
Tabs["tabs.py<br/>pane strip"]
|
|
145
|
+
Login["login.py<br/>provider / model / key"]
|
|
146
|
+
UIWidgets["ui.py<br/>prompt input, confirmations"]
|
|
147
|
+
Diff["diff.py<br/>side-by-side diff rendering"]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
subgraph core["Agent loop"]
|
|
151
|
+
AgentLoop["agent.py<br/>Agent.run()"]
|
|
152
|
+
LLM["llm.py<br/>build_model()"]
|
|
153
|
+
PromptMod["prompt.py<br/>system prompt"]
|
|
154
|
+
ToolsMod["tools.py<br/>tool REGISTRY"]
|
|
155
|
+
SessionMod["session.py<br/>JSONL persistence"]
|
|
156
|
+
Compaction["compaction.py"]
|
|
157
|
+
ModelWindows["model_windows.py<br/>context window sizes"]
|
|
158
|
+
Retry["retry.py"]
|
|
159
|
+
Mentions["mentions.py<br/>@path expansion"]
|
|
160
|
+
Jobs["turns.py<br/>turn driver"]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
subgraph support["Config & skills"]
|
|
164
|
+
Config["config.py<br/>profiles, credentials"]
|
|
165
|
+
Skills["skills.py<br/>Agent Skills discovery"]
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
CLI --> Commands
|
|
169
|
+
CLI --> Headless
|
|
170
|
+
CLI --> App
|
|
171
|
+
CLI --> Config
|
|
172
|
+
|
|
173
|
+
Headless --> AgentLoop
|
|
174
|
+
Headless --> Mentions
|
|
175
|
+
|
|
176
|
+
App --> Pane
|
|
177
|
+
App --> CommandPane
|
|
178
|
+
App --> Tabs
|
|
179
|
+
App --> Login
|
|
180
|
+
App --> Config
|
|
181
|
+
|
|
182
|
+
Pane --> Jobs
|
|
183
|
+
Pane --> AgentLoop
|
|
184
|
+
Pane --> Diff
|
|
185
|
+
Pane --> UIWidgets
|
|
186
|
+
Pane --> LLM
|
|
187
|
+
CommandPane --> Pane
|
|
188
|
+
UIWidgets --> Diff
|
|
189
|
+
|
|
190
|
+
AgentLoop --> LLM
|
|
191
|
+
AgentLoop --> PromptMod
|
|
192
|
+
AgentLoop --> ToolsMod
|
|
193
|
+
AgentLoop --> SessionMod
|
|
194
|
+
AgentLoop --> Compaction
|
|
195
|
+
AgentLoop --> Retry
|
|
196
|
+
AgentLoop --> Mentions
|
|
197
|
+
AgentLoop -. "spawn_agent" .-> AgentLoop
|
|
198
|
+
AgentLoop -. "run_background" .-> CommandPane
|
|
199
|
+
|
|
200
|
+
PromptMod --> Skills
|
|
201
|
+
Compaction --> ModelWindows
|
|
202
|
+
|
|
203
|
+
Jobs --> AgentLoop
|
|
204
|
+
|
|
205
|
+
Skills --> Config
|
|
206
|
+
Config --> LLM
|
|
207
|
+
```
|
|
119
208
|
|
|
120
209
|
## Telemetry
|
|
121
210
|
|
|
122
|
-
Each launch sends one anonymous event to Google Analytics: a random install id, the launch mode, the version, the OS name 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.
|
|
211
|
+
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.
|
|
@@ -26,9 +26,13 @@ uvx paimon
|
|
|
26
26
|
|
|
27
27
|
The first launch asks for a provider, model, API base and key. Then just type what you want done. Write `@path/to/file` in a prompt to hand a file to the agent.
|
|
28
28
|
|
|
29
|
-
While it runs: `Shift+Tab` switches how much the agent may do on its own (**read** asks before writing files or running commands, **edit** lets edits inside the working directory through, **yolo** never asks and is the default), `Esc` interrupts the current turn, `Ctrl+P` opens the command palette, `Ctrl+C` quits.
|
|
29
|
+
While it runs: `Shift+Tab` switches how much the agent may do on its own (**read** asks before writing files or running commands, **edit** lets edits inside the working directory through, **yolo** never asks 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
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. Paimon can
|
|
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. Paimon can work in parallel: ask for two independent things and it starts a second agent in the background, whose answer comes back into the conversation when it is done. It can also leave a command running in a tab of its own, 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.
|
|
32
36
|
|
|
33
37
|
## Skills
|
|
34
38
|
|
|
@@ -88,6 +92,7 @@ paimon log a1b2c3 # what a session did, one line per event
|
|
|
88
92
|
```bash
|
|
89
93
|
paimon --mode read # start in a more cautious permission mode (yolo is the default)
|
|
90
94
|
paimon --strict # ask before every command, even read-only ones
|
|
95
|
+
paimon --no-web-search # take the web search tool away for this run
|
|
91
96
|
paimon --web # the same UI in a browser (--port, default 8000)
|
|
92
97
|
paimon -p "what does cli.py do?" # one answer on stdout, no UI
|
|
93
98
|
cat log.txt | paimon -p "summarize this"
|
|
@@ -97,12 +102,95 @@ paimon --profile work # a separately configured account
|
|
|
97
102
|
|
|
98
103
|
`-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.
|
|
99
104
|
|
|
100
|
-
|
|
105
|
+
Inside a [Herdr](https://herdr.dev) pane the UI reports its state and resume command to Herdr on its own, with nothing to install.
|
|
101
106
|
|
|
102
|
-
|
|
107
|
+
## Configuration
|
|
103
108
|
|
|
104
|
-
|
|
109
|
+
Each profile keeps its model settings in `~/.config/paimon/<name>/config.json`, written by the first launch or by `paimon login`. Sessions live in `~/.local/share/paimon/sessions/`.
|
|
110
|
+
|
|
111
|
+
Read and edit modes run a small set of clearly read-only commands (`ls`, `cat`, `git status`, …) without asking; `--strict` turns that off. On Windows, where cmd.exe runs the commands, every command asks. **This is a guardrail against agent mistakes, not a security boundary.** For real isolation, run Paimon inside a container or VM.
|
|
112
|
+
|
|
113
|
+
## Architecture
|
|
114
|
+
|
|
115
|
+
`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.
|
|
116
|
+
|
|
117
|
+
```mermaid
|
|
118
|
+
flowchart TD
|
|
119
|
+
subgraph entry["Entry points"]
|
|
120
|
+
CLI["cli.py"]
|
|
121
|
+
Commands["commands.py<br/>status / login / sessions"]
|
|
122
|
+
Headless["headless.py<br/>-p, one-shot"]
|
|
123
|
+
App["app.py<br/>Textual TUI / --web"]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
subgraph tui["TUI widgets"]
|
|
127
|
+
Pane["pane.py<br/>SessionPane"]
|
|
128
|
+
CommandPane["commandpane.py<br/>background command pane"]
|
|
129
|
+
Tabs["tabs.py<br/>pane strip"]
|
|
130
|
+
Login["login.py<br/>provider / model / key"]
|
|
131
|
+
UIWidgets["ui.py<br/>prompt input, confirmations"]
|
|
132
|
+
Diff["diff.py<br/>side-by-side diff rendering"]
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
subgraph core["Agent loop"]
|
|
136
|
+
AgentLoop["agent.py<br/>Agent.run()"]
|
|
137
|
+
LLM["llm.py<br/>build_model()"]
|
|
138
|
+
PromptMod["prompt.py<br/>system prompt"]
|
|
139
|
+
ToolsMod["tools.py<br/>tool REGISTRY"]
|
|
140
|
+
SessionMod["session.py<br/>JSONL persistence"]
|
|
141
|
+
Compaction["compaction.py"]
|
|
142
|
+
ModelWindows["model_windows.py<br/>context window sizes"]
|
|
143
|
+
Retry["retry.py"]
|
|
144
|
+
Mentions["mentions.py<br/>@path expansion"]
|
|
145
|
+
Jobs["turns.py<br/>turn driver"]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
subgraph support["Config & skills"]
|
|
149
|
+
Config["config.py<br/>profiles, credentials"]
|
|
150
|
+
Skills["skills.py<br/>Agent Skills discovery"]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
CLI --> Commands
|
|
154
|
+
CLI --> Headless
|
|
155
|
+
CLI --> App
|
|
156
|
+
CLI --> Config
|
|
157
|
+
|
|
158
|
+
Headless --> AgentLoop
|
|
159
|
+
Headless --> Mentions
|
|
160
|
+
|
|
161
|
+
App --> Pane
|
|
162
|
+
App --> CommandPane
|
|
163
|
+
App --> Tabs
|
|
164
|
+
App --> Login
|
|
165
|
+
App --> Config
|
|
166
|
+
|
|
167
|
+
Pane --> Jobs
|
|
168
|
+
Pane --> AgentLoop
|
|
169
|
+
Pane --> Diff
|
|
170
|
+
Pane --> UIWidgets
|
|
171
|
+
Pane --> LLM
|
|
172
|
+
CommandPane --> Pane
|
|
173
|
+
UIWidgets --> Diff
|
|
174
|
+
|
|
175
|
+
AgentLoop --> LLM
|
|
176
|
+
AgentLoop --> PromptMod
|
|
177
|
+
AgentLoop --> ToolsMod
|
|
178
|
+
AgentLoop --> SessionMod
|
|
179
|
+
AgentLoop --> Compaction
|
|
180
|
+
AgentLoop --> Retry
|
|
181
|
+
AgentLoop --> Mentions
|
|
182
|
+
AgentLoop -. "spawn_agent" .-> AgentLoop
|
|
183
|
+
AgentLoop -. "run_background" .-> CommandPane
|
|
184
|
+
|
|
185
|
+
PromptMod --> Skills
|
|
186
|
+
Compaction --> ModelWindows
|
|
187
|
+
|
|
188
|
+
Jobs --> AgentLoop
|
|
189
|
+
|
|
190
|
+
Skills --> Config
|
|
191
|
+
Config --> LLM
|
|
192
|
+
```
|
|
105
193
|
|
|
106
194
|
## Telemetry
|
|
107
195
|
|
|
108
|
-
Each launch sends one anonymous event to Google Analytics: a random install id, the launch mode, the version, the OS name 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.
|
|
196
|
+
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.
|
|
@@ -26,9 +26,13 @@ uvx paimon
|
|
|
26
26
|
|
|
27
27
|
首次启动会询问 provider、模型、API base 和 key。之后输入要完成的任务即可。在提示中写 `@path/to/file` 可以把文件提供给 agent。
|
|
28
28
|
|
|
29
|
-
运行时:`Shift+Tab` 切换 agent 的自主程度(**read** 写文件或执行命令前先询问,**edit** 放行工作目录内的编辑,**yolo** 从不询问,也是默认值),`Esc` 打断当前回合,`Ctrl+P` 打开命令面板,`Ctrl+C`
|
|
29
|
+
运行时:`Shift+Tab` 切换 agent 的自主程度(**read** 写文件或执行命令前先询问,**edit** 放行工作目录内的编辑,**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。Paimon
|
|
31
|
+
`Ctrl+T` 在新 pane 里打开另一个会话,`Ctrl+W` 关闭当前 pane,`Ctrl+PageUp` 和 `Ctrl+PageDown` 在 pane 之间切换,`Ctrl+G` 跳到正在等待授权的 pane。Paimon 也能并行干活:让它同时做两件互不相干的事,它会在后台起第二个 agent,做完后结果会回到当前会话里。它也能把一条命令留在单独的 tab 里跑,比如开发服务器或者文件监视,不占着当前回合。
|
|
32
|
+
|
|
33
|
+
## 网页搜索
|
|
34
|
+
|
|
35
|
+
Paimon 可以搜索网页,不需要 API key。任何权限模式下都不会询问,因为搜索不会改动本机的任何东西。搜索通过元搜索库 [ddgs](https://github.com/deedy5/ddgs) 进行,所以一条 query 可能被发往多个公共搜索引擎中的任意一个。`--no-web-search` 可以在本次运行中去掉这个工具。
|
|
32
36
|
|
|
33
37
|
## Skills
|
|
34
38
|
|
|
@@ -88,6 +92,7 @@ paimon log a1b2c3 # 查看会话做了什么,每个事件一行
|
|
|
88
92
|
```bash
|
|
89
93
|
paimon --mode read # 以更谨慎的权限模式启动(默认为 yolo)
|
|
90
94
|
paimon --strict # 每条命令都先询问,包括只读命令
|
|
95
|
+
paimon --no-web-search # 本次运行不提供网页搜索工具
|
|
91
96
|
paimon --web # 在浏览器中使用同一套 UI(--port,默认 8000)
|
|
92
97
|
paimon -p "what does cli.py do?" # 直接在 stdout 输出回答,不启动 UI
|
|
93
98
|
cat log.txt | paimon -p "summarize this"
|
|
@@ -99,10 +104,91 @@ paimon --profile work # 单独配置的另一个账号
|
|
|
99
104
|
|
|
100
105
|
## 配置
|
|
101
106
|
|
|
102
|
-
每个 profile 的模型设置保存在 `~/.config/paimon/<name>/config.json`,由首次启动或 `paimon login` 写入。会话存放在 `~/.local/share/paimon/sessions
|
|
103
|
-
|
|
104
|
-
read 和 edit 模式会不经询问执行一小组明确只读的命令(`ls`、`cat`、`git status` 等),`--strict`
|
|
107
|
+
每个 profile 的模型设置保存在 `~/.config/paimon/<name>/config.json`,由首次启动或 `paimon login` 写入。会话存放在 `~/.local/share/paimon/sessions/`。
|
|
108
|
+
|
|
109
|
+
read 和 edit 模式会不经询问执行一小组明确只读的命令(`ls`、`cat`、`git status` 等),`--strict` 可以关掉。Windows 上命令由 cmd.exe 执行,每条命令都会询问。**这是防止 agent 失误的护栏,不是安全边界。** 需要真正的隔离时,请在容器或虚拟机中运行 Paimon。
|
|
110
|
+
|
|
111
|
+
## 架构
|
|
112
|
+
|
|
113
|
+
`Agent.run` 产出一串与 UI 无关的事件流,TUI、`--web` 和无头模式都是这条事件流的渲染器。agent 自己持有它启动的子 agent 和后台命令。子 agent 是同一进程里作为任务运行的另一个 `Agent`,它的结果会作为一条消息送回父 agent。
|
|
114
|
+
|
|
115
|
+
```mermaid
|
|
116
|
+
flowchart TD
|
|
117
|
+
subgraph entry["入口"]
|
|
118
|
+
CLI["cli.py"]
|
|
119
|
+
Commands["commands.py<br/>status / login / sessions"]
|
|
120
|
+
Headless["headless.py<br/>-p,单次运行"]
|
|
121
|
+
App["app.py<br/>Textual TUI / --web"]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
subgraph tui["TUI 组件"]
|
|
125
|
+
Pane["pane.py<br/>SessionPane"]
|
|
126
|
+
CommandPane["commandpane.py<br/>后台命令 pane"]
|
|
127
|
+
Tabs["tabs.py<br/>pane 标签栏"]
|
|
128
|
+
Login["login.py<br/>provider / 模型 / key"]
|
|
129
|
+
UIWidgets["ui.py<br/>输入框、确认面板"]
|
|
130
|
+
Diff["diff.py<br/>并排 diff 渲染"]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
subgraph core["Agent 主循环"]
|
|
134
|
+
AgentLoop["agent.py<br/>Agent.run()"]
|
|
135
|
+
LLM["llm.py<br/>build_model()"]
|
|
136
|
+
PromptMod["prompt.py<br/>system prompt"]
|
|
137
|
+
ToolsMod["tools.py<br/>工具注册表"]
|
|
138
|
+
SessionMod["session.py<br/>JSONL 持久化"]
|
|
139
|
+
Compaction["compaction.py"]
|
|
140
|
+
ModelWindows["model_windows.py<br/>各模型上下文窗口大小"]
|
|
141
|
+
Retry["retry.py"]
|
|
142
|
+
Mentions["mentions.py<br/>@path 展开"]
|
|
143
|
+
Jobs["turns.py<br/>回合驱动"]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
subgraph support["配置与 skills"]
|
|
147
|
+
Config["config.py<br/>profile、凭证"]
|
|
148
|
+
Skills["skills.py<br/>Agent Skills 发现"]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
CLI --> Commands
|
|
152
|
+
CLI --> Headless
|
|
153
|
+
CLI --> App
|
|
154
|
+
CLI --> Config
|
|
155
|
+
|
|
156
|
+
Headless --> AgentLoop
|
|
157
|
+
Headless --> Mentions
|
|
158
|
+
|
|
159
|
+
App --> Pane
|
|
160
|
+
App --> CommandPane
|
|
161
|
+
App --> Tabs
|
|
162
|
+
App --> Login
|
|
163
|
+
App --> Config
|
|
164
|
+
|
|
165
|
+
Pane --> Jobs
|
|
166
|
+
Pane --> AgentLoop
|
|
167
|
+
Pane --> Diff
|
|
168
|
+
Pane --> UIWidgets
|
|
169
|
+
Pane --> LLM
|
|
170
|
+
CommandPane --> Pane
|
|
171
|
+
UIWidgets --> Diff
|
|
172
|
+
|
|
173
|
+
AgentLoop --> LLM
|
|
174
|
+
AgentLoop --> PromptMod
|
|
175
|
+
AgentLoop --> ToolsMod
|
|
176
|
+
AgentLoop --> SessionMod
|
|
177
|
+
AgentLoop --> Compaction
|
|
178
|
+
AgentLoop --> Retry
|
|
179
|
+
AgentLoop --> Mentions
|
|
180
|
+
AgentLoop -. "spawn_agent" .-> AgentLoop
|
|
181
|
+
AgentLoop -. "run_background" .-> CommandPane
|
|
182
|
+
|
|
183
|
+
PromptMod --> Skills
|
|
184
|
+
Compaction --> ModelWindows
|
|
185
|
+
|
|
186
|
+
Jobs --> AgentLoop
|
|
187
|
+
|
|
188
|
+
Skills --> Config
|
|
189
|
+
Config --> LLM
|
|
190
|
+
```
|
|
105
191
|
|
|
106
192
|
## 遥测
|
|
107
193
|
|
|
108
|
-
每次启动会向 Google Analytics 发送一条匿名事件:随机生成的安装 id
|
|
194
|
+
每次启动会向 Google Analytics 发送一条匿名事件:随机生成的安装 id、启动方式、版本号、操作系统名称、取自 `LC_ALL`/`LANG` 的语言、是否首次启动,以及配置的 provider 和模型名。会话、提示词、文件和密钥一概不上报。设置 `PAIMON_NO_TELEMETRY=1` 或 `DO_NOT_TRACK=1` 可以关闭。
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Disposable process for bounded, cancellable code searches."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from .tools import _grep
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main() -> None:
|
|
11
|
+
args, cwd, sandboxed = json.load(sys.stdin)
|
|
12
|
+
sys.stdout.write(json.dumps(_grep(args, Path(cwd), sandboxed=sandboxed)))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
if __name__ == "__main__":
|
|
16
|
+
main()
|