jusi-acp 0.1.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.
- jusi_acp-0.1.0/.gitignore +7 -0
- jusi_acp-0.1.0/LICENSE +21 -0
- jusi_acp-0.1.0/PKG-INFO +230 -0
- jusi_acp-0.1.0/README.md +186 -0
- jusi_acp-0.1.0/pyproject.toml +40 -0
- jusi_acp-0.1.0/src/jusi_acp/__init__.py +30 -0
- jusi_acp-0.1.0/src/jusi_acp/application.py +946 -0
- jusi_acp-0.1.0/src/jusi_acp/compat.py +27 -0
- jusi_acp-0.1.0/src/jusi_acp/config.py +77 -0
- jusi_acp-0.1.0/src/jusi_acp/family.py +136 -0
- jusi_acp-0.1.0/src/jusi_acp/ipc.py +194 -0
- jusi_acp-0.1.0/src/jusi_acp/provider.py +58 -0
- jusi_acp-0.1.0/src/jusi_acp/questions.py +51 -0
- jusi_acp-0.1.0/src/jusi_acp/state.py +348 -0
- jusi_acp-0.1.0/src/jusi_acp/terminal.py +172 -0
- jusi_acp-0.1.0/src/jusi_acp/ui.py +207 -0
- jusi_acp-0.1.0/src/jusi_acp/worker.py +114 -0
- jusi_acp-0.1.0/tests/fixtures/acp_followup_provider.py +25 -0
- jusi_acp-0.1.0/tests/fixtures/fake_acp_agent.py +136 -0
- jusi_acp-0.1.0/tests/fixtures/followup_questions.lua +83 -0
- jusi_acp-0.1.0/tests/test_application.py +535 -0
- jusi_acp-0.1.0/tests/test_family.py +140 -0
- jusi_acp-0.1.0/tests/test_followup_ui.py +50 -0
- jusi_acp-0.1.0/tests/test_state.py +100 -0
- jusi_acp-0.1.0/tests/test_terminal.py +36 -0
- jusi_acp-0.1.0/tests/test_ui.py +64 -0
- jusi_acp-0.1.0/tests/test_worker.py +50 -0
jusi_acp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 notawhaleble
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
jusi_acp-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jusi-acp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: ACP plugin family and shared interactive client for Jusi 1.0
|
|
5
|
+
Project-URL: Repository, https://github.com/notawhaleble/jusi-acp
|
|
6
|
+
Author: notawhaleble
|
|
7
|
+
License: MIT License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2026 notawhaleble
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
in the Software without restriction, including without limitation the rights
|
|
14
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
SOFTWARE.
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Keywords: acp,agent,jupyter,jusi,visidata
|
|
30
|
+
Classifier: Development Status :: 3 - Alpha
|
|
31
|
+
Classifier: Framework :: Jupyter
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Programming Language :: Python :: 3
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
38
|
+
Requires-Python: >=3.10
|
|
39
|
+
Requires-Dist: agent-client-protocol<0.13,>=0.12.1
|
|
40
|
+
Requires-Dist: jusi[vd]<2,>=1.0.2
|
|
41
|
+
Provides-Extra: dev
|
|
42
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
# jusi-acp
|
|
46
|
+
|
|
47
|
+
`jusi-acp` is the shared Agent Client Protocol family for Jusi 1.0. It gives
|
|
48
|
+
independently installed ACP agent providers one `%%acp` notebook experience:
|
|
49
|
+
structured VisiData events, durable follow-ups, exact cancellation, permission
|
|
50
|
+
choices, client-owned command execution, session controls, completion, and
|
|
51
|
+
editor-native copy/open/diff actions.
|
|
52
|
+
|
|
53
|
+
This package is a family library, not an exact provider. Installing it alone
|
|
54
|
+
does not add a Jusi catalog entry. An exact package such as a future
|
|
55
|
+
`jusi-gigacode` depends on `jusi-acp` and supplies its own catalog entry,
|
|
56
|
+
attesting kernel module, launch validation, and worker factory.
|
|
57
|
+
|
|
58
|
+
## Configuration
|
|
59
|
+
|
|
60
|
+
Jusi loads `~/.jusi/jusi.toml` at the kernel target. ACP uses the standard
|
|
61
|
+
`[magic.alias]` layout:
|
|
62
|
+
|
|
63
|
+
```toml
|
|
64
|
+
[acp.work]
|
|
65
|
+
provider = "gigacode"
|
|
66
|
+
path = "/work/project"
|
|
67
|
+
additional_directories = ["../shared"]
|
|
68
|
+
model = "provider-owned-model-name"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`provider`, `path`, `additional_directories`, and `mcp_servers` are common
|
|
72
|
+
family keys. The selected exact provider validates the complete table and owns
|
|
73
|
+
the remaining keys. Configuration is frozen for one notebook runtime; use a
|
|
74
|
+
full Jusi restart to reload it.
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
%%acp work
|
|
78
|
+
Inspect the project and implement the requested change.
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Resume syntax is explicit:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
%%acp work --load SESSION_ID
|
|
85
|
+
Continue after replaying agent history.
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
%%acp work --resume SESSION_ID
|
|
90
|
+
Continue without requesting history replay.
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Exact provider integration
|
|
94
|
+
|
|
95
|
+
The exact package advertises the shared claim and wraps its identity around the
|
|
96
|
+
family kernel adapter:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
# catalog.py
|
|
100
|
+
from jusi_acp import family_claim
|
|
101
|
+
|
|
102
|
+
def catalog_entry():
|
|
103
|
+
return {
|
|
104
|
+
"plugin_id": "gigacode",
|
|
105
|
+
"plugin_version": __version__,
|
|
106
|
+
"distribution": "jusi-gigacode",
|
|
107
|
+
"families": [family_claim()],
|
|
108
|
+
"kernel_extensions": ["jusi_gigacode.kernel"],
|
|
109
|
+
"worker_entry_point": "jusi_gigacode.worker:create_worker",
|
|
110
|
+
"media_types": ["text/x-ansi"],
|
|
111
|
+
"interaction": "terminal_interactive",
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
# kernel.py
|
|
117
|
+
from jusi_acp import KernelProviderAdapter
|
|
118
|
+
|
|
119
|
+
_adapter = KernelProviderAdapter("gigacode", __version__)
|
|
120
|
+
jusi_kernel_adapter_v1 = _adapter.manifest
|
|
121
|
+
configure_jusi_runtime_v1 = _adapter.configure
|
|
122
|
+
load_ipython_extension = _adapter.load_ipython_extension
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
# worker.py
|
|
127
|
+
from pathlib import Path
|
|
128
|
+
from jusi_acp import ACPWorker, AgentLaunch, ProviderSpec
|
|
129
|
+
|
|
130
|
+
def resolve_launch(config, cwd: Path):
|
|
131
|
+
return AgentLaunch(("gigacode", "--acp"), cwd)
|
|
132
|
+
|
|
133
|
+
PROVIDER = ProviderSpec("gigacode", __version__, resolve_launch)
|
|
134
|
+
|
|
135
|
+
def create_worker(context):
|
|
136
|
+
return ACPWorker(context, PROVIDER)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The provider must use the same ID and version in its catalog, kernel adapter,
|
|
140
|
+
and `ProviderSpec`. It must not register `%%acp` itself or store live sessions
|
|
141
|
+
globally.
|
|
142
|
+
|
|
143
|
+
## Shared behavior
|
|
144
|
+
|
|
145
|
+
- ACP v1 over the official Python SDK.
|
|
146
|
+
- Python 3.10 or newer.
|
|
147
|
+
- `execute`, `followup`, `complete`, `interrupt`, and `editor_actions` for every
|
|
148
|
+
exact provider claiming the family.
|
|
149
|
+
- Markdown syntax and indentation in ACP prompt cells.
|
|
150
|
+
- ACP terminal methods run bounded, non-interactive subprocesses at the target.
|
|
151
|
+
- ACP filesystem, elicitation, and terminal-auth capabilities are not
|
|
152
|
+
advertised in the initial release.
|
|
153
|
+
- Agent-driven authentication is available through `/auth METHOD_ID` or an
|
|
154
|
+
exact provider's configured `AgentLaunch.auth_method`.
|
|
155
|
+
- `/mode ID`, `/config ID VALUE`, and `/cancel` are family commands.
|
|
156
|
+
- Initial bodies and later follow-ups use the same family-command dispatcher.
|
|
157
|
+
- ACP `available_commands_update` entries appear in completion and in the event
|
|
158
|
+
sheet with their descriptions and optional free-text input hints. Invoking an
|
|
159
|
+
advertised slash command sends its complete text as a regular ACP prompt;
|
|
160
|
+
command-specific behavior and output remain agent-owned.
|
|
161
|
+
- Diff content opens through Jusi's acknowledged, remote-safe read-only diff
|
|
162
|
+
action.
|
|
163
|
+
- ACP remains authoritative for conversation context. The family stores a
|
|
164
|
+
normalized read-only presentation cache so resume-only sessions retain useful
|
|
165
|
+
review history.
|
|
166
|
+
- The live event view groups adjacent assistant and thought chunks, consolidates
|
|
167
|
+
tool updates by call ID, and updates terminal output in place while retaining
|
|
168
|
+
every raw ACP update in the durable journal.
|
|
169
|
+
- Completed prompts appear as one row each in a turns sheet. Enter opens that
|
|
170
|
+
turn's grouped event view; turn completion returns focus to the summary.
|
|
171
|
+
|
|
172
|
+
The first turn begins inside the terminal application after Jusi creates the
|
|
173
|
+
client. It is cancellable with the application's `c` command or `/cancel`, but
|
|
174
|
+
is not published as a Jusi client operation. Later follow-ups are exact Jusi
|
|
175
|
+
operations and support `:JusiInterrupt` without closing the session.
|
|
176
|
+
|
|
177
|
+
## Development
|
|
178
|
+
|
|
179
|
+
```sh
|
|
180
|
+
python3.12 -m venv .venv
|
|
181
|
+
.venv/bin/pip install -e '.[dev]'
|
|
182
|
+
.venv/bin/pytest
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The notebook follow-up integration test uses a sibling `jusi` frontend checkout,
|
|
186
|
+
or the path supplied in `JUSI_NVIM_ROOT`, together with the installed Python
|
|
187
|
+
backend. It edits notebook cells through Neovim and checks the real serialized
|
|
188
|
+
follow-up lane, answer history, and same-client reuse.
|
|
189
|
+
|
|
190
|
+
### Turn review and questions
|
|
191
|
+
|
|
192
|
+
The turns sheet records the model ID reported by the agent, from either model
|
|
193
|
+
configuration or older ACP session model metadata. It stays blank when the
|
|
194
|
+
agent does not report a model; it is not inferred from the executable name.
|
|
195
|
+
Tool events show the tool name/kind, command, and raw input when supplied.
|
|
196
|
+
Enter opens the full event details (or the existing diff view).
|
|
197
|
+
|
|
198
|
+
Qwen/GigaCode question requests carried in `rawInput.questions` pause the agent
|
|
199
|
+
and show the current question and choices in a read-only view. Type your answer
|
|
200
|
+
in the notebook cell in Vim and submit it using the usual Jusi follow-up action.
|
|
201
|
+
Each follow-up answers one question. Write the option label (available in cell
|
|
202
|
+
completion) or any free-form, multiline answer; text and whitespace are preserved.
|
|
203
|
+
For multiple-choice questions, describe all your choices in the answer. An empty
|
|
204
|
+
answer is rejected without advancing to the next question.
|
|
205
|
+
|
|
206
|
+
When a question arrives, the current Jusi operation returns successfully with
|
|
207
|
+
`status: awaiting_answer`, freeing the follow-up lane while keeping the ACP
|
|
208
|
+
request pending. After the last answer, that same ACP turn resumes, and the
|
|
209
|
+
answer follow-up stays active until the turn finishes or asks another question.
|
|
210
|
+
Answers remain in the original turn's event history and in normal Jusi follow-up
|
|
211
|
+
history; they never become separate agent prompts. Once the turn finishes, the
|
|
212
|
+
next follow-up starts a new prompt as usual.
|
|
213
|
+
|
|
214
|
+
Submit `/cancel` from the cell to cancel a waiting turn, including any partially
|
|
215
|
+
answered question set. Closing the question view just hides it; it does not
|
|
216
|
+
answer or cancel anything. While waiting, all other cell text is an answer,
|
|
217
|
+
including slash-prefixed text. During resumed work, use `:JusiInterrupt` as usual.
|
|
218
|
+
While waiting for input there is no active Jusi operation to interrupt, so use
|
|
219
|
+
`/cancel` (or `c` in the question view). No text entry in VisiData is required.
|
|
220
|
+
|
|
221
|
+
Ordinary tool permission requests retain their approval choices, with `d`
|
|
222
|
+
exposing the full request payload. Questions use the
|
|
223
|
+
[Qwen permission extension](https://github.com/QwenLM/qwen-code/blob/main/packages/vscode-ide-companion/src/services/acpConnection.ts),
|
|
224
|
+
not generic ACP elicitation.
|
|
225
|
+
|
|
226
|
+
ACP UI updates run from the drawing thread with a bounded curses polling interval,
|
|
227
|
+
including while the terminal has no focus. SDK logging goes to event diagnostics
|
|
228
|
+
and VisiData's Ctrl-E error history. Agent stderr appears as labelled diagnostic
|
|
229
|
+
events, including during authentication. Browser launcher environment variables
|
|
230
|
+
are preserved, and provider environment overrides take precedence.
|
jusi_acp-0.1.0/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# jusi-acp
|
|
2
|
+
|
|
3
|
+
`jusi-acp` is the shared Agent Client Protocol family for Jusi 1.0. It gives
|
|
4
|
+
independently installed ACP agent providers one `%%acp` notebook experience:
|
|
5
|
+
structured VisiData events, durable follow-ups, exact cancellation, permission
|
|
6
|
+
choices, client-owned command execution, session controls, completion, and
|
|
7
|
+
editor-native copy/open/diff actions.
|
|
8
|
+
|
|
9
|
+
This package is a family library, not an exact provider. Installing it alone
|
|
10
|
+
does not add a Jusi catalog entry. An exact package such as a future
|
|
11
|
+
`jusi-gigacode` depends on `jusi-acp` and supplies its own catalog entry,
|
|
12
|
+
attesting kernel module, launch validation, and worker factory.
|
|
13
|
+
|
|
14
|
+
## Configuration
|
|
15
|
+
|
|
16
|
+
Jusi loads `~/.jusi/jusi.toml` at the kernel target. ACP uses the standard
|
|
17
|
+
`[magic.alias]` layout:
|
|
18
|
+
|
|
19
|
+
```toml
|
|
20
|
+
[acp.work]
|
|
21
|
+
provider = "gigacode"
|
|
22
|
+
path = "/work/project"
|
|
23
|
+
additional_directories = ["../shared"]
|
|
24
|
+
model = "provider-owned-model-name"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`provider`, `path`, `additional_directories`, and `mcp_servers` are common
|
|
28
|
+
family keys. The selected exact provider validates the complete table and owns
|
|
29
|
+
the remaining keys. Configuration is frozen for one notebook runtime; use a
|
|
30
|
+
full Jusi restart to reload it.
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
%%acp work
|
|
34
|
+
Inspect the project and implement the requested change.
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Resume syntax is explicit:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
%%acp work --load SESSION_ID
|
|
41
|
+
Continue after replaying agent history.
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
%%acp work --resume SESSION_ID
|
|
46
|
+
Continue without requesting history replay.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Exact provider integration
|
|
50
|
+
|
|
51
|
+
The exact package advertises the shared claim and wraps its identity around the
|
|
52
|
+
family kernel adapter:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
# catalog.py
|
|
56
|
+
from jusi_acp import family_claim
|
|
57
|
+
|
|
58
|
+
def catalog_entry():
|
|
59
|
+
return {
|
|
60
|
+
"plugin_id": "gigacode",
|
|
61
|
+
"plugin_version": __version__,
|
|
62
|
+
"distribution": "jusi-gigacode",
|
|
63
|
+
"families": [family_claim()],
|
|
64
|
+
"kernel_extensions": ["jusi_gigacode.kernel"],
|
|
65
|
+
"worker_entry_point": "jusi_gigacode.worker:create_worker",
|
|
66
|
+
"media_types": ["text/x-ansi"],
|
|
67
|
+
"interaction": "terminal_interactive",
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
# kernel.py
|
|
73
|
+
from jusi_acp import KernelProviderAdapter
|
|
74
|
+
|
|
75
|
+
_adapter = KernelProviderAdapter("gigacode", __version__)
|
|
76
|
+
jusi_kernel_adapter_v1 = _adapter.manifest
|
|
77
|
+
configure_jusi_runtime_v1 = _adapter.configure
|
|
78
|
+
load_ipython_extension = _adapter.load_ipython_extension
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
# worker.py
|
|
83
|
+
from pathlib import Path
|
|
84
|
+
from jusi_acp import ACPWorker, AgentLaunch, ProviderSpec
|
|
85
|
+
|
|
86
|
+
def resolve_launch(config, cwd: Path):
|
|
87
|
+
return AgentLaunch(("gigacode", "--acp"), cwd)
|
|
88
|
+
|
|
89
|
+
PROVIDER = ProviderSpec("gigacode", __version__, resolve_launch)
|
|
90
|
+
|
|
91
|
+
def create_worker(context):
|
|
92
|
+
return ACPWorker(context, PROVIDER)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The provider must use the same ID and version in its catalog, kernel adapter,
|
|
96
|
+
and `ProviderSpec`. It must not register `%%acp` itself or store live sessions
|
|
97
|
+
globally.
|
|
98
|
+
|
|
99
|
+
## Shared behavior
|
|
100
|
+
|
|
101
|
+
- ACP v1 over the official Python SDK.
|
|
102
|
+
- Python 3.10 or newer.
|
|
103
|
+
- `execute`, `followup`, `complete`, `interrupt`, and `editor_actions` for every
|
|
104
|
+
exact provider claiming the family.
|
|
105
|
+
- Markdown syntax and indentation in ACP prompt cells.
|
|
106
|
+
- ACP terminal methods run bounded, non-interactive subprocesses at the target.
|
|
107
|
+
- ACP filesystem, elicitation, and terminal-auth capabilities are not
|
|
108
|
+
advertised in the initial release.
|
|
109
|
+
- Agent-driven authentication is available through `/auth METHOD_ID` or an
|
|
110
|
+
exact provider's configured `AgentLaunch.auth_method`.
|
|
111
|
+
- `/mode ID`, `/config ID VALUE`, and `/cancel` are family commands.
|
|
112
|
+
- Initial bodies and later follow-ups use the same family-command dispatcher.
|
|
113
|
+
- ACP `available_commands_update` entries appear in completion and in the event
|
|
114
|
+
sheet with their descriptions and optional free-text input hints. Invoking an
|
|
115
|
+
advertised slash command sends its complete text as a regular ACP prompt;
|
|
116
|
+
command-specific behavior and output remain agent-owned.
|
|
117
|
+
- Diff content opens through Jusi's acknowledged, remote-safe read-only diff
|
|
118
|
+
action.
|
|
119
|
+
- ACP remains authoritative for conversation context. The family stores a
|
|
120
|
+
normalized read-only presentation cache so resume-only sessions retain useful
|
|
121
|
+
review history.
|
|
122
|
+
- The live event view groups adjacent assistant and thought chunks, consolidates
|
|
123
|
+
tool updates by call ID, and updates terminal output in place while retaining
|
|
124
|
+
every raw ACP update in the durable journal.
|
|
125
|
+
- Completed prompts appear as one row each in a turns sheet. Enter opens that
|
|
126
|
+
turn's grouped event view; turn completion returns focus to the summary.
|
|
127
|
+
|
|
128
|
+
The first turn begins inside the terminal application after Jusi creates the
|
|
129
|
+
client. It is cancellable with the application's `c` command or `/cancel`, but
|
|
130
|
+
is not published as a Jusi client operation. Later follow-ups are exact Jusi
|
|
131
|
+
operations and support `:JusiInterrupt` without closing the session.
|
|
132
|
+
|
|
133
|
+
## Development
|
|
134
|
+
|
|
135
|
+
```sh
|
|
136
|
+
python3.12 -m venv .venv
|
|
137
|
+
.venv/bin/pip install -e '.[dev]'
|
|
138
|
+
.venv/bin/pytest
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The notebook follow-up integration test uses a sibling `jusi` frontend checkout,
|
|
142
|
+
or the path supplied in `JUSI_NVIM_ROOT`, together with the installed Python
|
|
143
|
+
backend. It edits notebook cells through Neovim and checks the real serialized
|
|
144
|
+
follow-up lane, answer history, and same-client reuse.
|
|
145
|
+
|
|
146
|
+
### Turn review and questions
|
|
147
|
+
|
|
148
|
+
The turns sheet records the model ID reported by the agent, from either model
|
|
149
|
+
configuration or older ACP session model metadata. It stays blank when the
|
|
150
|
+
agent does not report a model; it is not inferred from the executable name.
|
|
151
|
+
Tool events show the tool name/kind, command, and raw input when supplied.
|
|
152
|
+
Enter opens the full event details (or the existing diff view).
|
|
153
|
+
|
|
154
|
+
Qwen/GigaCode question requests carried in `rawInput.questions` pause the agent
|
|
155
|
+
and show the current question and choices in a read-only view. Type your answer
|
|
156
|
+
in the notebook cell in Vim and submit it using the usual Jusi follow-up action.
|
|
157
|
+
Each follow-up answers one question. Write the option label (available in cell
|
|
158
|
+
completion) or any free-form, multiline answer; text and whitespace are preserved.
|
|
159
|
+
For multiple-choice questions, describe all your choices in the answer. An empty
|
|
160
|
+
answer is rejected without advancing to the next question.
|
|
161
|
+
|
|
162
|
+
When a question arrives, the current Jusi operation returns successfully with
|
|
163
|
+
`status: awaiting_answer`, freeing the follow-up lane while keeping the ACP
|
|
164
|
+
request pending. After the last answer, that same ACP turn resumes, and the
|
|
165
|
+
answer follow-up stays active until the turn finishes or asks another question.
|
|
166
|
+
Answers remain in the original turn's event history and in normal Jusi follow-up
|
|
167
|
+
history; they never become separate agent prompts. Once the turn finishes, the
|
|
168
|
+
next follow-up starts a new prompt as usual.
|
|
169
|
+
|
|
170
|
+
Submit `/cancel` from the cell to cancel a waiting turn, including any partially
|
|
171
|
+
answered question set. Closing the question view just hides it; it does not
|
|
172
|
+
answer or cancel anything. While waiting, all other cell text is an answer,
|
|
173
|
+
including slash-prefixed text. During resumed work, use `:JusiInterrupt` as usual.
|
|
174
|
+
While waiting for input there is no active Jusi operation to interrupt, so use
|
|
175
|
+
`/cancel` (or `c` in the question view). No text entry in VisiData is required.
|
|
176
|
+
|
|
177
|
+
Ordinary tool permission requests retain their approval choices, with `d`
|
|
178
|
+
exposing the full request payload. Questions use the
|
|
179
|
+
[Qwen permission extension](https://github.com/QwenLM/qwen-code/blob/main/packages/vscode-ide-companion/src/services/acpConnection.ts),
|
|
180
|
+
not generic ACP elicitation.
|
|
181
|
+
|
|
182
|
+
ACP UI updates run from the drawing thread with a bounded curses polling interval,
|
|
183
|
+
including while the terminal has no focus. SDK logging goes to event diagnostics
|
|
184
|
+
and VisiData's Ctrl-E error history. Agent stderr appears as labelled diagnostic
|
|
185
|
+
events, including during authentication. Browser launcher environment variables
|
|
186
|
+
are preserved, and provider environment overrides take precedence.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.21"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "jusi-acp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "ACP plugin family and shared interactive client for Jusi 1.0"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { file = "LICENSE" }
|
|
12
|
+
authors = [{ name = "notawhaleble" }]
|
|
13
|
+
keywords = ["acp", "jusi", "jupyter", "agent", "visidata"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Framework :: Jupyter",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"agent-client-protocol>=0.12.1,<0.13",
|
|
26
|
+
"jusi[vd]>=1.0.2,<2",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
dev = ["pytest>=8"]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Repository = "https://github.com/notawhaleble/jusi-acp"
|
|
34
|
+
|
|
35
|
+
[tool.hatch.build.targets.wheel]
|
|
36
|
+
packages = ["src/jusi_acp"]
|
|
37
|
+
|
|
38
|
+
[tool.pytest.ini_options]
|
|
39
|
+
testpaths = ["tests"]
|
|
40
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Provider-facing API for the Jusi ACP plugin family."""
|
|
2
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
__version__ = version("jusi-acp")
|
|
6
|
+
except PackageNotFoundError: # source checkout
|
|
7
|
+
__version__ = "0.1.0"
|
|
8
|
+
|
|
9
|
+
from .family import ( # noqa: E402
|
|
10
|
+
CAPABILITIES,
|
|
11
|
+
FAMILY_ID,
|
|
12
|
+
MAGIC_NAME,
|
|
13
|
+
PRESENTATION,
|
|
14
|
+
KernelProviderAdapter,
|
|
15
|
+
family_claim,
|
|
16
|
+
)
|
|
17
|
+
from .provider import AgentLaunch, ProviderSpec # noqa: E402
|
|
18
|
+
from .worker import ACPWorker # noqa: E402
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"ACPWorker",
|
|
22
|
+
"AgentLaunch",
|
|
23
|
+
"CAPABILITIES",
|
|
24
|
+
"FAMILY_ID",
|
|
25
|
+
"KernelProviderAdapter",
|
|
26
|
+
"MAGIC_NAME",
|
|
27
|
+
"PRESENTATION",
|
|
28
|
+
"ProviderSpec",
|
|
29
|
+
"family_claim",
|
|
30
|
+
]
|