paimon 0.2.8__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.2.8/paimon.egg-info → paimon-0.3.0}/PKG-INFO +18 -11
- {paimon-0.2.8 → paimon-0.3.0}/README.md +16 -10
- {paimon-0.2.8 → paimon-0.3.0}/README.zh-CN.md +14 -9
- paimon-0.3.0/paimon/_grep_worker.py +23 -0
- paimon-0.3.0/paimon/agent.py +1329 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon/agentpane.py +28 -16
- {paimon-0.2.8 → paimon-0.3.0}/paimon/antigravity.py +23 -5
- {paimon-0.2.8 → paimon-0.3.0}/paimon/app.py +340 -79
- {paimon-0.2.8 → paimon-0.3.0}/paimon/app.tcss +21 -8
- {paimon-0.2.8 → paimon-0.3.0}/paimon/chatgpt.py +5 -5
- {paimon-0.2.8 → paimon-0.3.0}/paimon/cli.py +36 -35
- paimon-0.3.0/paimon/codemode.py +244 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon/commandpane.py +37 -14
- {paimon-0.2.8 → paimon-0.3.0}/paimon/commands.py +16 -15
- {paimon-0.2.8 → paimon-0.3.0}/paimon/compaction.py +52 -21
- {paimon-0.2.8 → paimon-0.3.0}/paimon/config.py +104 -29
- {paimon-0.2.8 → paimon-0.3.0}/paimon/diff.py +20 -5
- paimon-0.3.0/paimon/events.py +274 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon/headless.py +90 -70
- {paimon-0.2.8 → 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.2.8 → paimon-0.3.0}/paimon/llm.py +4 -4
- {paimon-0.2.8 → paimon-0.3.0}/paimon/login.py +20 -7
- paimon-0.3.0/paimon/logtext.py +200 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon/oauth.py +14 -6
- {paimon-0.2.8 → paimon-0.3.0}/paimon/pane.py +190 -168
- paimon-0.3.0/paimon/progstatus.py +56 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon/prompt.py +19 -12
- {paimon-0.2.8 → paimon-0.3.0}/paimon/review.py +97 -15
- {paimon-0.2.8 → paimon-0.3.0}/paimon/session.py +149 -78
- {paimon-0.2.8 → paimon-0.3.0}/paimon/skills.py +6 -6
- {paimon-0.2.8 → paimon-0.3.0}/paimon/telemetry.py +3 -4
- {paimon-0.2.8 → paimon-0.3.0}/paimon/textual_debug.py +12 -12
- 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.2.8 → paimon-0.3.0}/paimon/transcript.py +15 -9
- paimon-0.3.0/paimon/turns.py +446 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon/ui.py +134 -28
- {paimon-0.2.8 → paimon-0.3.0}/paimon/websearch.py +8 -6
- {paimon-0.2.8 → paimon-0.3.0/paimon.egg-info}/PKG-INFO +18 -11
- {paimon-0.2.8 → paimon-0.3.0}/paimon.egg-info/SOURCES.txt +16 -2
- {paimon-0.2.8 → paimon-0.3.0}/paimon.egg-info/requires.txt +1 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon.egg-info/scm_file_list.json +19 -1
- paimon-0.3.0/paimon.egg-info/scm_version.json +8 -0
- {paimon-0.2.8 → paimon-0.3.0}/pyproject.toml +2 -1
- paimon-0.2.8/paimon/_grep_worker.py +0 -16
- paimon-0.2.8/paimon/agent.py +0 -1511
- paimon-0.2.8/paimon/tools.py +0 -2184
- paimon-0.2.8/paimon/turns.py +0 -198
- paimon-0.2.8/paimon.egg-info/scm_version.json +0 -8
- {paimon-0.2.8 → paimon-0.3.0}/LICENSE +0 -0
- {paimon-0.2.8 → paimon-0.3.0}/MANIFEST.in +0 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon/__init__.py +0 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon/__main__.py +0 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon/errors.py +0 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon/lockfile.py +0 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon/mentions.py +0 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon/retry.py +0 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon/skill/SKILL.md +0 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon/tabs.py +0 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon.egg-info/dependency_links.txt +0 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon.egg-info/entry_points.txt +0 -0
- {paimon-0.2.8 → paimon-0.3.0}/paimon.egg-info/top_level.txt +0 -0
- {paimon-0.2.8 → paimon-0.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: paimon
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
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
|
|
@@ -14,6 +14,7 @@ Requires-Dist: pyyaml>=6
|
|
|
14
14
|
Requires-Dist: pydantic>=2.10
|
|
15
15
|
Requires-Dist: typing-extensions>=4.12
|
|
16
16
|
Requires-Dist: ddgs>=9
|
|
17
|
+
Requires-Dist: pydantic-monty<2,>=1.0
|
|
17
18
|
Dynamic: license-file
|
|
18
19
|
|
|
19
20
|
# Paimon
|
|
@@ -46,7 +47,7 @@ The first launch asks for a provider, its API base and key, then a model. Then j
|
|
|
46
47
|
|
|
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.
|
|
48
49
|
|
|
49
|
-
`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 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.
|
|
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.
|
|
50
51
|
|
|
51
52
|
## Web search
|
|
52
53
|
|
|
@@ -75,22 +76,25 @@ npx skills add aisk/paimon # the same skill, via skills.sh
|
|
|
75
76
|
|
|
76
77
|
## Using Paimon as a library
|
|
77
78
|
|
|
78
|
-
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.
|
|
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:
|
|
79
80
|
|
|
80
81
|
```python
|
|
81
82
|
import asyncio
|
|
82
83
|
|
|
83
|
-
from paimon.agent import Agent
|
|
84
|
+
from paimon.agent import Agent
|
|
85
|
+
from paimon.events import TextDelta
|
|
84
86
|
|
|
85
87
|
async def main():
|
|
86
88
|
agent = Agent.open(mode="auto")
|
|
87
|
-
async for event in agent.
|
|
89
|
+
async for event in agent.run_until_idle("summarize the tests in this directory"):
|
|
88
90
|
if isinstance(event, TextDelta):
|
|
89
91
|
print(event.text, end="", flush=True)
|
|
90
92
|
|
|
91
93
|
asyncio.run(main())
|
|
92
94
|
```
|
|
93
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
|
+
|
|
94
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`.
|
|
95
99
|
|
|
96
100
|
## Sessions
|
|
@@ -111,6 +115,7 @@ paimon log a1b2c3 # what a session did, one line per event
|
|
|
111
115
|
paimon --mode read # start in a more cautious permission mode (yolo is the default)
|
|
112
116
|
paimon --strict # hold every command, even read-only ones
|
|
113
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)
|
|
114
119
|
paimon --web # the same UI in a browser (--port, default 8000)
|
|
115
120
|
paimon -p "what does cli.py do?" # one answer on stdout, no UI
|
|
116
121
|
cat log.txt | paimon -p "summarize this"
|
|
@@ -119,7 +124,7 @@ paimon --model zai:glm-4.7 # this model for this run only
|
|
|
119
124
|
|
|
120
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.
|
|
121
126
|
|
|
122
|
-
Inside a [Herdr](https://herdr.dev) pane the UI reports its state and resume command to Herdr on its own, with nothing to install.
|
|
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.
|
|
123
128
|
|
|
124
129
|
### Debugging the Textual UI
|
|
125
130
|
|
|
@@ -136,7 +141,7 @@ You can ask it, for example, to “find why `#prompt` is narrower than expected,
|
|
|
136
141
|
|
|
137
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/`.
|
|
138
143
|
|
|
139
|
-
Read and auto modes run a small set of clearly read-only commands (`ls`, `cat`, `git status`, …) on their own; `--strict` turns that off,
|
|
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.
|
|
140
145
|
|
|
141
146
|
## Architecture
|
|
142
147
|
|
|
@@ -166,11 +171,12 @@ flowchart TD
|
|
|
166
171
|
AgentLoop["agent.py<br/>Agent.run()"]
|
|
167
172
|
LLM["llm.py<br/>build_model()"]
|
|
168
173
|
PromptMod["prompt.py<br/>system prompt"]
|
|
169
|
-
ToolsMod["tools
|
|
174
|
+
ToolsMod["tools/<br/>tool REGISTRY"]
|
|
170
175
|
SessionMod["session.py<br/>JSONL persistence"]
|
|
171
176
|
Compaction["compaction.py"]
|
|
172
177
|
Retry["retry.py"]
|
|
173
178
|
Mentions["mentions.py<br/>@path expansion"]
|
|
179
|
+
JobMgr["jobs.py<br/>subagents, background commands"]
|
|
174
180
|
Jobs["turns.py<br/>turn driver"]
|
|
175
181
|
end
|
|
176
182
|
|
|
@@ -213,9 +219,10 @@ flowchart TD
|
|
|
213
219
|
AgentLoop --> Compaction
|
|
214
220
|
AgentLoop --> Retry
|
|
215
221
|
AgentLoop --> Mentions
|
|
216
|
-
AgentLoop
|
|
217
|
-
|
|
218
|
-
|
|
222
|
+
AgentLoop --> JobMgr
|
|
223
|
+
JobMgr -. "spawn_agent" .-> AgentLoop
|
|
224
|
+
JobMgr -. "spawn_agent" .-> AgentPane
|
|
225
|
+
JobMgr -. "run_background" .-> CommandPane
|
|
219
226
|
|
|
220
227
|
PromptMod --> Skills
|
|
221
228
|
|
|
@@ -28,7 +28,7 @@ The first launch asks for a provider, its API base and key, then a model. Then j
|
|
|
28
28
|
|
|
29
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
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 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.
|
|
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
32
|
|
|
33
33
|
## Web search
|
|
34
34
|
|
|
@@ -57,22 +57,25 @@ npx skills add aisk/paimon # the same skill, via skills.sh
|
|
|
57
57
|
|
|
58
58
|
## Using Paimon as a library
|
|
59
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.
|
|
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
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
69
|
agent = Agent.open(mode="auto")
|
|
69
|
-
async for event in agent.
|
|
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
|
|
|
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
|
+
|
|
76
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`.
|
|
77
80
|
|
|
78
81
|
## Sessions
|
|
@@ -93,6 +96,7 @@ paimon log a1b2c3 # what a session did, one line per event
|
|
|
93
96
|
paimon --mode read # start in a more cautious permission mode (yolo is the default)
|
|
94
97
|
paimon --strict # hold every command, even read-only ones
|
|
95
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)
|
|
96
100
|
paimon --web # the same UI in a browser (--port, default 8000)
|
|
97
101
|
paimon -p "what does cli.py do?" # one answer on stdout, no UI
|
|
98
102
|
cat log.txt | paimon -p "summarize this"
|
|
@@ -101,7 +105,7 @@ paimon --model zai:glm-4.7 # this model for this run only
|
|
|
101
105
|
|
|
102
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.
|
|
103
107
|
|
|
104
|
-
Inside a [Herdr](https://herdr.dev) pane the UI reports its state and resume command to Herdr on its own, with nothing to install.
|
|
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.
|
|
105
109
|
|
|
106
110
|
### Debugging the Textual UI
|
|
107
111
|
|
|
@@ -118,7 +122,7 @@ You can ask it, for example, to “find why `#prompt` is narrower than expected,
|
|
|
118
122
|
|
|
119
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/`.
|
|
120
124
|
|
|
121
|
-
Read and auto modes run a small set of clearly read-only commands (`ls`, `cat`, `git status`, …) on their own; `--strict` turns that off,
|
|
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.
|
|
122
126
|
|
|
123
127
|
## Architecture
|
|
124
128
|
|
|
@@ -148,11 +152,12 @@ flowchart TD
|
|
|
148
152
|
AgentLoop["agent.py<br/>Agent.run()"]
|
|
149
153
|
LLM["llm.py<br/>build_model()"]
|
|
150
154
|
PromptMod["prompt.py<br/>system prompt"]
|
|
151
|
-
ToolsMod["tools
|
|
155
|
+
ToolsMod["tools/<br/>tool REGISTRY"]
|
|
152
156
|
SessionMod["session.py<br/>JSONL persistence"]
|
|
153
157
|
Compaction["compaction.py"]
|
|
154
158
|
Retry["retry.py"]
|
|
155
159
|
Mentions["mentions.py<br/>@path expansion"]
|
|
160
|
+
JobMgr["jobs.py<br/>subagents, background commands"]
|
|
156
161
|
Jobs["turns.py<br/>turn driver"]
|
|
157
162
|
end
|
|
158
163
|
|
|
@@ -195,9 +200,10 @@ flowchart TD
|
|
|
195
200
|
AgentLoop --> Compaction
|
|
196
201
|
AgentLoop --> Retry
|
|
197
202
|
AgentLoop --> Mentions
|
|
198
|
-
AgentLoop
|
|
199
|
-
|
|
200
|
-
|
|
203
|
+
AgentLoop --> JobMgr
|
|
204
|
+
JobMgr -. "spawn_agent" .-> AgentLoop
|
|
205
|
+
JobMgr -. "spawn_agent" .-> AgentPane
|
|
206
|
+
JobMgr -. "run_background" .-> CommandPane
|
|
201
207
|
|
|
202
208
|
PromptMod --> Skills
|
|
203
209
|
|
|
@@ -28,7 +28,7 @@ uvx paimon
|
|
|
28
28
|
|
|
29
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
|
|
|
@@ -57,22 +57,25 @@ 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
69
|
agent = Agent.open(mode="auto")
|
|
69
|
-
async for event in agent.
|
|
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
|
|
|
77
|
+
`agent.run()` 只跑一个回合,返回时它启动的 agent 可能还在运行;想自己决定什么时候用 `agent.run(None)` 让模型再跑一轮的程序,用它。同一个 agent 一次只跑一个回合或一次压缩,前一个还没结束就开始下一个会抛出 `AgentBusyError`。
|
|
78
|
+
|
|
76
79
|
`Agent.open()` 还接受工作目录、用于剩余权限确认的异步 `confirm` 回调,以及 `toolset`,可以只给模型一部分工具,或者换成你自己的工具。agent 被回收时会交还会话,想在确定的时刻交还就调 `close()`,或者把它当上下文管理器用。它写的会话文件和 CLI 一样,所以代码里跑出来的会话之后可以用 `paimon -r` 恢复。
|
|
77
80
|
|
|
78
81
|
## 会话
|
|
@@ -116,7 +119,7 @@ paimon --model zai:glm-4.7 # 仅本次运行使用该模型
|
|
|
116
119
|
|
|
117
120
|
模型设置保存在 `~/.config/paimon/config.json`,由首次启动或 `paimon login` 写入。每个 provider 各自保存凭据,用 `paimon model provider:name` 或命令面板里的 Switch model 选择要用的模型。也可以用 ChatGPT 订阅代替 API key,`paimon login chatgpt` 会通过浏览器登录。会话存放在 `~/.local/share/paimon/sessions/`。
|
|
118
121
|
|
|
119
|
-
read 和 auto 模式会直接执行一小组明确只读的命令(`ls`、`cat`、`git status` 等),`--strict`
|
|
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。
|
|
120
123
|
|
|
121
124
|
## 架构
|
|
122
125
|
|
|
@@ -146,11 +149,12 @@ flowchart TD
|
|
|
146
149
|
AgentLoop["agent.py<br/>Agent.run()"]
|
|
147
150
|
LLM["llm.py<br/>build_model()"]
|
|
148
151
|
PromptMod["prompt.py<br/>system prompt"]
|
|
149
|
-
ToolsMod["tools
|
|
152
|
+
ToolsMod["tools/<br/>工具注册表"]
|
|
150
153
|
SessionMod["session.py<br/>JSONL 持久化"]
|
|
151
154
|
Compaction["compaction.py"]
|
|
152
155
|
Retry["retry.py"]
|
|
153
156
|
Mentions["mentions.py<br/>@path 展开"]
|
|
157
|
+
JobMgr["jobs.py<br/>子代理、后台命令"]
|
|
154
158
|
Jobs["turns.py<br/>回合驱动"]
|
|
155
159
|
end
|
|
156
160
|
|
|
@@ -193,9 +197,10 @@ flowchart TD
|
|
|
193
197
|
AgentLoop --> Compaction
|
|
194
198
|
AgentLoop --> Retry
|
|
195
199
|
AgentLoop --> Mentions
|
|
196
|
-
AgentLoop
|
|
197
|
-
|
|
198
|
-
|
|
200
|
+
AgentLoop --> JobMgr
|
|
201
|
+
JobMgr -. "spawn_agent" .-> AgentLoop
|
|
202
|
+
JobMgr -. "spawn_agent" .-> AgentPane
|
|
203
|
+
JobMgr -. "run_background" .-> CommandPane
|
|
199
204
|
|
|
200
205
|
PromptMod --> Skills
|
|
201
206
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Disposable process for bounded, cancellable code searches."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from .tools.grep import _grep
|
|
8
|
+
from .tools.result import ToolError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main() -> None:
|
|
12
|
+
args, cwd, sandboxed = json.load(sys.stdin)
|
|
13
|
+
try:
|
|
14
|
+
result = _grep(args, Path(cwd), sandboxed=sandboxed)
|
|
15
|
+
except ToolError as exc:
|
|
16
|
+
# An object rather than a string, so the failure crosses the process
|
|
17
|
+
# boundary as one instead of being read back out of the text.
|
|
18
|
+
result = {"error": exc.text}
|
|
19
|
+
sys.stdout.write(json.dumps(result))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if __name__ == "__main__":
|
|
23
|
+
main()
|