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.
@@ -0,0 +1,7 @@
1
+ .venv/
2
+ .pytest_cache/
3
+ __pycache__/
4
+ *.py[cod]
5
+ *.egg-info/
6
+ build/
7
+ dist/
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.
@@ -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.
@@ -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
+ ]