kon-coding-agent 0.2.0__tar.gz → 0.2.2__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.
- kon_coding_agent-0.2.2/.kon/skills/kon-release-publish/SKILL.md +85 -0
- kon_coding_agent-0.2.2/LOCAL.md +25 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/PKG-INFO +19 -20
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/README.md +18 -19
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/TODO.md +0 -2
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/pyproject.toml +1 -1
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/config.py +10 -1
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/defaults/config.toml +4 -1
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/events.py +9 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/providers/mock.py +40 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/loop.py +9 -4
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/session.py +16 -7
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/turn.py +31 -1
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/app.py +29 -6
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/commands.py +13 -3
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/session_ui.py +8 -3
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/widgets.py +10 -1
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/test_agentic_loop.py +49 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/test_compaction.py +2 -2
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/test_session_persistence.py +20 -6
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/uv.lock +1 -1
- kon_coding_agent-0.2.0/LOCAL.md +0 -34
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/.gitignore +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/.kon/skills/kon-tmux-test/SKILL.md +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/.kon/skills/kon-tmux-test/run-e2e-tests.sh +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/.kon/skills/kon-tmux-test/setup-test-project.sh +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/.python-version +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/AGENTS.md +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/LICENSE +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/scripts/test_models.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/scripts/test_thinking_blocks.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/__init__.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/context/__init__.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/context/agents.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/context/loader.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/context/shared.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/context/skills.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/core/__init__.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/core/compaction.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/core/types.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/defaults/__init__.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/__init__.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/base.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/models.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/oauth/__init__.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/oauth/copilot.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/oauth/openai.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/providers/__init__.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/providers/anthropic.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/providers/copilot.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/providers/copilot_anthropic.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/providers/github_copilot_headers.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/providers/openai_codex_responses.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/providers/openai_completions.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/providers/openai_responses.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/llm/providers/sanitize.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/py.typed +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/shared.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/tools/__init__.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/tools/_read_image.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/tools/base.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/tools/bash.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/tools/edit.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/tools/find.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/tools/grep.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/tools/read.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/tools/write.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/tools_manager.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/__init__.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/app_protocol.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/autocomplete.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/blocks.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/chat.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/clipboard.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/export.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/floating_list.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/formatting.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/input.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/path_complete.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/selection_mode.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/ui/styles.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/src/kon/update_check.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/conftest.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/context/test_agents.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/context/test_skills.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/llm/__init__.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/llm/test_mock_provider.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/test_cli_provider_resolution.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/test_config_binaries.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/test_config_error_fallback.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/test_config_injection.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/test_model_provider_resolution.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/test_system_prompt.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/test_update_check.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/test_update_notice_behavior.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/tools/test_diff.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/tools/test_edit.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/tools/test_read.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/tools/test_read_image.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/tools/test_read_image_integration.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/tools/test_write.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/ui/test_autocomplete.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/ui/test_floating_list.py +0 -0
- {kon_coding_agent-0.2.0 → kon_coding_agent-0.2.2}/tests/ui/test_input_paste.py +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: kon-release-publish
|
|
3
|
+
description: Tag, publish to PyPI, and create GitHub release for Kon with validation and rollback-safe steps
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Kon Release + PyPI Publish
|
|
7
|
+
|
|
8
|
+
Use this skill when the user asks to cut a new Kon version, tag it, publish to PyPI, and/or create a GitHub release.
|
|
9
|
+
|
|
10
|
+
## Inputs to confirm
|
|
11
|
+
|
|
12
|
+
- Target version (example: `0.2.1`)
|
|
13
|
+
- Base range for notes (usually previous tag, example: `v0.2.0..HEAD`)
|
|
14
|
+
- Whether to push `main`
|
|
15
|
+
- Whether to publish to PyPI now
|
|
16
|
+
- Whether to create GitHub release now
|
|
17
|
+
|
|
18
|
+
## Files to bump
|
|
19
|
+
|
|
20
|
+
- `pyproject.toml` → `[project].version`
|
|
21
|
+
- `src/kon/ui/app.py` → fallback `VERSION = "..."`
|
|
22
|
+
- `uv.lock` → local package version block
|
|
23
|
+
|
|
24
|
+
## Release workflow
|
|
25
|
+
|
|
26
|
+
1. **Preflight**
|
|
27
|
+
- `git status --short --branch` must be clean (or confirm with user)
|
|
28
|
+
- `git tag --list` and `git log --oneline <prev_tag>..HEAD` to summarize changes
|
|
29
|
+
|
|
30
|
+
2. **Version bump**
|
|
31
|
+
- Update version in all 3 files above
|
|
32
|
+
|
|
33
|
+
3. **Quality gates**
|
|
34
|
+
- `uv run ruff format .`
|
|
35
|
+
- `uv run ruff check .`
|
|
36
|
+
- `uv run pyright .`
|
|
37
|
+
- `uv run pytest`
|
|
38
|
+
|
|
39
|
+
4. **Commit**
|
|
40
|
+
- Commit message: `build: bump version to <version>`
|
|
41
|
+
|
|
42
|
+
5. **Tag**
|
|
43
|
+
- Annotated tag: `git tag -a v<version> -m "v<version> ..."`
|
|
44
|
+
- Include concise “changes since previous tag” bullets
|
|
45
|
+
|
|
46
|
+
6. **Push**
|
|
47
|
+
- `git push origin main`
|
|
48
|
+
- `git push origin v<version>`
|
|
49
|
+
|
|
50
|
+
7. **Build + verify artifacts**
|
|
51
|
+
- `rm -rf dist && uv build`
|
|
52
|
+
- `uv run python -m twine check dist/*`
|
|
53
|
+
|
|
54
|
+
8. **Publish to PyPI**
|
|
55
|
+
- Prefer token file if present (example `~/.pypi-token`):
|
|
56
|
+
- `TWINE_USERNAME=__token__ TWINE_PASSWORD="$(< ~/.pypi-token)" uv run python -m twine upload dist/*`
|
|
57
|
+
- Verify:
|
|
58
|
+
- `https://pypi.org/project/kon-coding-agent/<version>/`
|
|
59
|
+
- `https://pypi.org/pypi/kon-coding-agent/json` reports latest version
|
|
60
|
+
|
|
61
|
+
9. **Create GitHub release**
|
|
62
|
+
- If token exists at `~/.github-token`, call Releases API:
|
|
63
|
+
- `POST /repos/<owner>/<repo>/releases` with:
|
|
64
|
+
- `tag_name: v<version>`
|
|
65
|
+
- `target_commitish: main`
|
|
66
|
+
- `name: v<version>`
|
|
67
|
+
- `generate_release_notes: true`
|
|
68
|
+
- If 403 occurs, report missing token scopes/permissions (`contents:write` required)
|
|
69
|
+
|
|
70
|
+
## Important notes
|
|
71
|
+
|
|
72
|
+
- **Tagging and GitHub release are separate**:
|
|
73
|
+
- Tag = git ref in repository
|
|
74
|
+
- Release = GitHub object attached to a tag (notes/assets)
|
|
75
|
+
- You can do either independently, but most projects do both together for user-facing releases.
|
|
76
|
+
- If PyPI publish succeeds but GitHub release fails, do **not** retag/re-publish. Just fix auth and create the release for the existing tag.
|
|
77
|
+
|
|
78
|
+
## Output checklist to report
|
|
79
|
+
|
|
80
|
+
- Version bumped in all files
|
|
81
|
+
- Checks passed
|
|
82
|
+
- Commit hash
|
|
83
|
+
- Tag created and pushed
|
|
84
|
+
- PyPI upload URL
|
|
85
|
+
- GitHub release URL (or exact error + remediation)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Local Models
|
|
2
|
+
|
|
3
|
+
This document provides detailed information about running and configuring local models with Kon.
|
|
4
|
+
|
|
5
|
+
## Tested Models
|
|
6
|
+
|
|
7
|
+
| Model | Quantization | Context Length | TPS | System Specs |
|
|
8
|
+
| ----- | -------------- | -------------- | --- | ------------ |
|
|
9
|
+
| `qwen/qwen3-coder-next` | Q4_K_M | 64,000 | N/A | i7-14700F × 28, 64GB RAM, 24GB VRAM (RTX 3090) |
|
|
10
|
+
| `zai-org/glm-4.7-flash` | Q4_K_M | 64,000 | ~80-90 | i7-14700F × 28, 64GB RAM, 24GB VRAM (RTX 3090) |
|
|
11
|
+
|
|
12
|
+
Run a local model using llama-server with the following command:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
./llama-server -m <models-dir>/GLM-4.7-Flash-GGUF/GLM-4.7-Flash-Q4_K_M.gguf -n 8192 -c 64000
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Then start kon:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
kon --model zai-org/glm-4.7-flash --provider openai --base-url http://localhost:8080/v1 --api-key ""
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
> [!NOTE]
|
|
25
|
+
> I was not able to run qwen-coder-next reliably on my system. Either the provider config had some issues or it's too big for my system (i'm not sure)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kon-coding-agent
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Minimal coding agent
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -215,34 +215,32 @@ UI (app.py)
|
|
|
215
215
|
|
|
216
216
|
## Supported Models
|
|
217
217
|
|
|
218
|
-
Kon works well with local models exposed through an OpenAI-compatible `/v1` API
|
|
218
|
+
Kon works well with local models exposed through an OpenAI-compatible `/v1` API.
|
|
219
219
|
|
|
220
|
-
### Example
|
|
220
|
+
### Example using llama-server
|
|
221
221
|
|
|
222
|
-
To run a local model
|
|
222
|
+
To run a local model using llama-server:
|
|
223
223
|
|
|
224
224
|
```bash
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
--model qwen/qwen3-coder-next \
|
|
235
|
-
--api-key ""
|
|
225
|
+
./llama-server -m <models-dir>/GLM-4.7-Flash-GGUF/GLM-4.7-Flash-Q4_K_M.gguf \
|
|
226
|
+
-n 8192 \
|
|
227
|
+
-c 64000
|
|
228
|
+
|
|
229
|
+
# Then use Kon with:
|
|
230
|
+
kon --model zai-org/glm-4.7-flash \
|
|
231
|
+
--provider openai \
|
|
232
|
+
--base-url http://localhost:8080/v1 \
|
|
233
|
+
--api-key ""
|
|
236
234
|
```
|
|
237
235
|
|
|
238
|
-
|
|
236
|
+
`GLM-4.7-Flash-Q4` ran at 80-90 tps on my i7-14700F × 28, 64GB RAM, 24GB VRAM (RTX 3090)
|
|
239
237
|
|
|
240
238
|
### All Supported Providers
|
|
241
239
|
|
|
242
240
|
| Model (local=*) | Provider | Thinking | Vision |
|
|
243
241
|
| ----- | -------- | -------- | ------ |
|
|
244
|
-
| `*zai-org/glm-4.7-flash` | OpenAI
|
|
245
|
-
| `*qwen/qwen3-coder-next` | OpenAI
|
|
242
|
+
| `*zai-org/glm-4.7-flash` | OpenAI Completions | Yes | No |
|
|
243
|
+
| `*qwen/qwen3-coder-next` | OpenAI Completions | Yes | No |
|
|
246
244
|
| `glm-4.7` | ZhiPu (OpenAI Completions) | Yes | No |
|
|
247
245
|
| `glm-5` | ZhiPu (OpenAI Completions) | Yes | No |
|
|
248
246
|
| `claude-sonnet-4.5` | GitHub Copilot | Yes | Yes |
|
|
@@ -264,7 +262,8 @@ Most important knobs:
|
|
|
264
262
|
- `llm.default_thinking_level`
|
|
265
263
|
- `llm.system_prompt` (**you can fully override Kon’s system prompt here**)
|
|
266
264
|
- `llm.tool_call_idle_timeout_seconds` (fallback timeout for stalled tool-call streaming)
|
|
267
|
-
- `compaction.on_overflow`, `compaction.buffer_tokens
|
|
265
|
+
- `compaction.on_overflow`, `compaction.buffer_tokens`
|
|
266
|
+
- `agent.max_turns`, `agent.default_context_window`
|
|
268
267
|
|
|
269
268
|
You can also theme the UI via `[ui.colors]` values.
|
|
270
269
|
|
|
@@ -275,7 +274,7 @@ Example:
|
|
|
275
274
|
default_provider = "openai-codex"
|
|
276
275
|
default_model = "gpt-5.3-codex"
|
|
277
276
|
default_thinking_level = "high"
|
|
278
|
-
tool_call_idle_timeout_seconds =
|
|
277
|
+
tool_call_idle_timeout_seconds = 60
|
|
279
278
|
system_prompt = """Your custom system prompt here"""
|
|
280
279
|
|
|
281
280
|
[compaction]
|
|
@@ -199,34 +199,32 @@ UI (app.py)
|
|
|
199
199
|
|
|
200
200
|
## Supported Models
|
|
201
201
|
|
|
202
|
-
Kon works well with local models exposed through an OpenAI-compatible `/v1` API
|
|
202
|
+
Kon works well with local models exposed through an OpenAI-compatible `/v1` API.
|
|
203
203
|
|
|
204
|
-
### Example
|
|
204
|
+
### Example using llama-server
|
|
205
205
|
|
|
206
|
-
To run a local model
|
|
206
|
+
To run a local model using llama-server:
|
|
207
207
|
|
|
208
208
|
```bash
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
--model qwen/qwen3-coder-next \
|
|
219
|
-
--api-key ""
|
|
209
|
+
./llama-server -m <models-dir>/GLM-4.7-Flash-GGUF/GLM-4.7-Flash-Q4_K_M.gguf \
|
|
210
|
+
-n 8192 \
|
|
211
|
+
-c 64000
|
|
212
|
+
|
|
213
|
+
# Then use Kon with:
|
|
214
|
+
kon --model zai-org/glm-4.7-flash \
|
|
215
|
+
--provider openai \
|
|
216
|
+
--base-url http://localhost:8080/v1 \
|
|
217
|
+
--api-key ""
|
|
220
218
|
```
|
|
221
219
|
|
|
222
|
-
|
|
220
|
+
`GLM-4.7-Flash-Q4` ran at 80-90 tps on my i7-14700F × 28, 64GB RAM, 24GB VRAM (RTX 3090)
|
|
223
221
|
|
|
224
222
|
### All Supported Providers
|
|
225
223
|
|
|
226
224
|
| Model (local=*) | Provider | Thinking | Vision |
|
|
227
225
|
| ----- | -------- | -------- | ------ |
|
|
228
|
-
| `*zai-org/glm-4.7-flash` | OpenAI
|
|
229
|
-
| `*qwen/qwen3-coder-next` | OpenAI
|
|
226
|
+
| `*zai-org/glm-4.7-flash` | OpenAI Completions | Yes | No |
|
|
227
|
+
| `*qwen/qwen3-coder-next` | OpenAI Completions | Yes | No |
|
|
230
228
|
| `glm-4.7` | ZhiPu (OpenAI Completions) | Yes | No |
|
|
231
229
|
| `glm-5` | ZhiPu (OpenAI Completions) | Yes | No |
|
|
232
230
|
| `claude-sonnet-4.5` | GitHub Copilot | Yes | Yes |
|
|
@@ -248,7 +246,8 @@ Most important knobs:
|
|
|
248
246
|
- `llm.default_thinking_level`
|
|
249
247
|
- `llm.system_prompt` (**you can fully override Kon’s system prompt here**)
|
|
250
248
|
- `llm.tool_call_idle_timeout_seconds` (fallback timeout for stalled tool-call streaming)
|
|
251
|
-
- `compaction.on_overflow`, `compaction.buffer_tokens
|
|
249
|
+
- `compaction.on_overflow`, `compaction.buffer_tokens`
|
|
250
|
+
- `agent.max_turns`, `agent.default_context_window`
|
|
252
251
|
|
|
253
252
|
You can also theme the UI via `[ui.colors]` values.
|
|
254
253
|
|
|
@@ -259,7 +258,7 @@ Example:
|
|
|
259
258
|
default_provider = "openai-codex"
|
|
260
259
|
default_model = "gpt-5.3-codex"
|
|
261
260
|
default_thinking_level = "high"
|
|
262
|
-
tool_call_idle_timeout_seconds =
|
|
261
|
+
tool_call_idle_timeout_seconds = 60
|
|
263
262
|
system_prompt = """Your custom system prompt here"""
|
|
264
263
|
|
|
265
264
|
[compaction]
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
- show new release update in ui to prompt the user to upgrade
|
|
2
|
-
- show tokens streamed in for bash, edit and write tools (which can large at times)
|
|
3
1
|
- if @ or / menu is open pressing esc closes it but interrupts stream as well
|
|
4
2
|
- bug in how we report context size, tokens in and out and cached tokens for codex?
|
|
5
3
|
- steer (immediate) and normal queues
|
|
@@ -59,12 +59,16 @@ class LLMConfig(BaseModel):
|
|
|
59
59
|
default_model: str
|
|
60
60
|
default_thinking_level: str
|
|
61
61
|
system_prompt: str
|
|
62
|
-
tool_call_idle_timeout_seconds: float =
|
|
62
|
+
tool_call_idle_timeout_seconds: float = 60
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
class CompactionConfig(BaseModel):
|
|
66
66
|
on_overflow: OnOverflowMode = "continue"
|
|
67
67
|
buffer_tokens: int = 20000
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class AgentConfig(BaseModel):
|
|
71
|
+
max_turns: int = 500
|
|
68
72
|
default_context_window: int = 200000
|
|
69
73
|
|
|
70
74
|
|
|
@@ -72,6 +76,7 @@ class ConfigSchema(BaseModel):
|
|
|
72
76
|
llm: LLMConfig
|
|
73
77
|
ui: UIConfig
|
|
74
78
|
compaction: CompactionConfig
|
|
79
|
+
agent: AgentConfig
|
|
75
80
|
|
|
76
81
|
|
|
77
82
|
class _BinariesConfig:
|
|
@@ -126,6 +131,10 @@ class Config:
|
|
|
126
131
|
def compaction(self) -> CompactionConfig:
|
|
127
132
|
return self._parsed.compaction
|
|
128
133
|
|
|
134
|
+
@property
|
|
135
|
+
def agent(self) -> AgentConfig:
|
|
136
|
+
return self._parsed.agent
|
|
137
|
+
|
|
129
138
|
@property
|
|
130
139
|
def binaries(self) -> _BinariesConfig:
|
|
131
140
|
return _BinariesConfig(AVAILABLE_BINARIES)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
default_provider = "openai-codex" # "zhipu", "github-copilot", "openai-codex"
|
|
3
3
|
default_model = "gpt-5.3-codex"
|
|
4
4
|
default_thinking_level = "high"
|
|
5
|
-
tool_call_idle_timeout_seconds =
|
|
5
|
+
tool_call_idle_timeout_seconds = 120
|
|
6
6
|
system_prompt = """You are an expert coding assistant called `Kon`.
|
|
7
7
|
You help users by reading, searching, executing commands, editing code, and writing new files.
|
|
8
8
|
|
|
@@ -21,6 +21,9 @@ You help users by reading, searching, executing commands, editing code, and writ
|
|
|
21
21
|
[compaction]
|
|
22
22
|
on_overflow = "continue" # "continue" or "pause"
|
|
23
23
|
buffer_tokens = 20000
|
|
24
|
+
|
|
25
|
+
[agent]
|
|
26
|
+
max_turns = 500
|
|
24
27
|
default_context_window = 200000
|
|
25
28
|
|
|
26
29
|
[ui.colors]
|
|
@@ -100,6 +100,14 @@ class ToolArgsDeltaEvent:
|
|
|
100
100
|
delta: str = ""
|
|
101
101
|
|
|
102
102
|
|
|
103
|
+
@dataclass
|
|
104
|
+
class ToolArgsTokenUpdateEvent:
|
|
105
|
+
type: Literal["tool_args_token_update"] = "tool_args_token_update"
|
|
106
|
+
tool_call_id: str = ""
|
|
107
|
+
tool_name: str = ""
|
|
108
|
+
token_count: int = 0
|
|
109
|
+
|
|
110
|
+
|
|
103
111
|
@dataclass
|
|
104
112
|
class ToolEndEvent:
|
|
105
113
|
type: Literal["tool_end"] = "tool_end"
|
|
@@ -180,6 +188,7 @@ StreamEvent = (
|
|
|
180
188
|
| TextEndEvent
|
|
181
189
|
| ToolStartEvent
|
|
182
190
|
| ToolArgsDeltaEvent
|
|
191
|
+
| ToolArgsTokenUpdateEvent
|
|
183
192
|
| ToolEndEvent
|
|
184
193
|
| ToolResultEvent
|
|
185
194
|
| RetryEvent
|
|
@@ -15,6 +15,7 @@ Scenarios (set via scenario parameter):
|
|
|
15
15
|
- "unknown_tool": call unknown tool
|
|
16
16
|
- "long_text": multiple text chunks
|
|
17
17
|
- "tool_hang": emits a tool call and then never sends StreamDone
|
|
18
|
+
- "tool_with_many_chunks": tool call with many argument chunks for token counting tests
|
|
18
19
|
"""
|
|
19
20
|
|
|
20
21
|
import asyncio
|
|
@@ -138,6 +139,45 @@ class MockProvider(BaseProvider):
|
|
|
138
139
|
|
|
139
140
|
return tool_hang_iter()
|
|
140
141
|
|
|
142
|
+
case "tool_with_many_chunks":
|
|
143
|
+
|
|
144
|
+
async def tool_with_many_chunks_iter():
|
|
145
|
+
# Tool call with many chunks to test token counting
|
|
146
|
+
# 24 chunks of 8 chars each = 192 chars = 48 tokens
|
|
147
|
+
# Should trigger token update events at chunks 12, 16, 20, 24
|
|
148
|
+
yield ToolCallStart(id="call-1", name="bash", index=0)
|
|
149
|
+
chunks = [
|
|
150
|
+
"aaaaaaa",
|
|
151
|
+
"bbbbbbb",
|
|
152
|
+
"ccccccc",
|
|
153
|
+
"ddddddd",
|
|
154
|
+
"eeeeeee",
|
|
155
|
+
"fffffff",
|
|
156
|
+
"ggggggg",
|
|
157
|
+
"hhhhhhh",
|
|
158
|
+
"iiiiiii",
|
|
159
|
+
"jjjjjjj",
|
|
160
|
+
"kkkkkkk",
|
|
161
|
+
"lllllll",
|
|
162
|
+
"mmmmmmm",
|
|
163
|
+
"nnnnnnn",
|
|
164
|
+
"ooooooo",
|
|
165
|
+
"ppppppp",
|
|
166
|
+
"qqqqqqq",
|
|
167
|
+
"rrrrrrr",
|
|
168
|
+
"sssssss",
|
|
169
|
+
"ttttttt",
|
|
170
|
+
"uuuuuuu",
|
|
171
|
+
"vvvvvvv",
|
|
172
|
+
"wwwwwww",
|
|
173
|
+
"xxxxxxxx",
|
|
174
|
+
]
|
|
175
|
+
for chunk in chunks:
|
|
176
|
+
yield ToolCallDelta(index=0, arguments_delta=chunk)
|
|
177
|
+
yield StreamDone(stop_reason=StopReason.TOOL_USE)
|
|
178
|
+
|
|
179
|
+
return tool_with_many_chunks_iter()
|
|
180
|
+
|
|
141
181
|
case _:
|
|
142
182
|
# Fallback to default
|
|
143
183
|
async def default_iter():
|
|
@@ -76,7 +76,7 @@ def build_system_prompt(cwd: str, context: Context | None = None) -> str:
|
|
|
76
76
|
|
|
77
77
|
@dataclass
|
|
78
78
|
class AgentConfig:
|
|
79
|
-
max_turns: int =
|
|
79
|
+
max_turns: int | None = None
|
|
80
80
|
system_prompt: str | None = None
|
|
81
81
|
cwd: str | None = None
|
|
82
82
|
context: Context | None = None
|
|
@@ -137,7 +137,12 @@ class Agent:
|
|
|
137
137
|
system_prompt = self.config.system_prompt or build_system_prompt(cwd, self.config.context)
|
|
138
138
|
|
|
139
139
|
try:
|
|
140
|
-
|
|
140
|
+
max_turns = (
|
|
141
|
+
self.config.max_turns
|
|
142
|
+
if self.config.max_turns is not None
|
|
143
|
+
else kon_config.agent.max_turns
|
|
144
|
+
)
|
|
145
|
+
while turn < max_turns:
|
|
141
146
|
if cancel_event and cancel_event.is_set():
|
|
142
147
|
was_interrupted = True
|
|
143
148
|
stop_reason = StopReason.INTERRUPTED
|
|
@@ -194,7 +199,7 @@ class Agent:
|
|
|
194
199
|
if stop_reason != StopReason.TOOL_USE:
|
|
195
200
|
break
|
|
196
201
|
|
|
197
|
-
if turn >=
|
|
202
|
+
if turn >= max_turns and not was_interrupted:
|
|
198
203
|
stop_reason = StopReason.LENGTH
|
|
199
204
|
|
|
200
205
|
except Exception as e: # intentionally broad — top-level boundary; crash = broken TUI
|
|
@@ -219,7 +224,7 @@ class Agent:
|
|
|
219
224
|
if last_usage is None:
|
|
220
225
|
return
|
|
221
226
|
|
|
222
|
-
context_window = self.config.context_window or kon_config.
|
|
227
|
+
context_window = self.config.context_window or kon_config.agent.default_context_window
|
|
223
228
|
max_output = self.config.max_output_tokens or self.provider.config.max_tokens
|
|
224
229
|
buffer_tokens = kon_config.compaction.buffer_tokens
|
|
225
230
|
|
|
@@ -58,6 +58,7 @@ class ModelChangeEntry(EntryBase):
|
|
|
58
58
|
type: Literal["model_change"] = "model_change"
|
|
59
59
|
provider: str
|
|
60
60
|
model_id: str
|
|
61
|
+
base_url: str | None = None
|
|
61
62
|
|
|
62
63
|
|
|
63
64
|
class CompactionEntry(EntryBase):
|
|
@@ -241,13 +242,16 @@ class Session:
|
|
|
241
242
|
self._append_entry(entry)
|
|
242
243
|
return entry.id
|
|
243
244
|
|
|
244
|
-
def append_model_change(
|
|
245
|
+
def append_model_change(
|
|
246
|
+
self, provider: str, model_id: str, base_url: str | None = None
|
|
247
|
+
) -> str:
|
|
245
248
|
entry = ModelChangeEntry(
|
|
246
249
|
id=self._generate_entry_id(),
|
|
247
250
|
parent_id=self._leaf_id,
|
|
248
251
|
timestamp=_now_iso(),
|
|
249
252
|
provider=provider,
|
|
250
253
|
model_id=model_id,
|
|
254
|
+
base_url=base_url,
|
|
251
255
|
)
|
|
252
256
|
self._append_entry(entry)
|
|
253
257
|
return entry.id
|
|
@@ -372,20 +376,25 @@ class Session:
|
|
|
372
376
|
return self._initial_thinking_level
|
|
373
377
|
|
|
374
378
|
@property
|
|
375
|
-
def model(self) -> tuple[str, str] | None:
|
|
379
|
+
def model(self) -> tuple[str, str, str | None] | None:
|
|
376
380
|
for entry in reversed(self._entries):
|
|
377
381
|
if isinstance(entry, ModelChangeEntry):
|
|
378
|
-
return (entry.provider, entry.model_id)
|
|
382
|
+
return (entry.provider, entry.model_id, entry.base_url)
|
|
379
383
|
|
|
380
384
|
if self._initial_provider and self._initial_model_id:
|
|
381
|
-
return (self._initial_provider, self._initial_model_id)
|
|
385
|
+
return (self._initial_provider, self._initial_model_id, None)
|
|
382
386
|
return None
|
|
383
387
|
|
|
384
|
-
def set_model(self, provider: str, model_id: str) -> None:
|
|
388
|
+
def set_model(self, provider: str, model_id: str, base_url: str | None = None) -> None:
|
|
385
389
|
current = self.model
|
|
386
|
-
if
|
|
390
|
+
if (
|
|
391
|
+
current
|
|
392
|
+
and current[0] == provider
|
|
393
|
+
and current[1] == model_id
|
|
394
|
+
and current[2] == base_url
|
|
395
|
+
):
|
|
387
396
|
return
|
|
388
|
-
self.append_model_change(provider, model_id)
|
|
397
|
+
self.append_model_change(provider, model_id, base_url)
|
|
389
398
|
|
|
390
399
|
def set_thinking_level(self, thinking_level: str) -> None:
|
|
391
400
|
if self.thinking_level == thinking_level:
|
|
@@ -59,6 +59,7 @@ from .events import (
|
|
|
59
59
|
ThinkingEndEvent,
|
|
60
60
|
ThinkingStartEvent,
|
|
61
61
|
ToolArgsDeltaEvent,
|
|
62
|
+
ToolArgsTokenUpdateEvent,
|
|
62
63
|
ToolEndEvent,
|
|
63
64
|
ToolResultEvent,
|
|
64
65
|
ToolStartEvent,
|
|
@@ -70,7 +71,14 @@ from .llm.base import LLMStream
|
|
|
70
71
|
from .tools import BaseTool, get_tool, get_tool_definitions
|
|
71
72
|
|
|
72
73
|
_STREAM_EXHAUSTED = object()
|
|
73
|
-
_DEFAULT_TOOL_CALL_IDLE_TIMEOUT_SECONDS =
|
|
74
|
+
_DEFAULT_TOOL_CALL_IDLE_TIMEOUT_SECONDS = 60.0
|
|
75
|
+
_TOOL_ARGS_TOKEN_DISPLAY_THRESHOLD = 20
|
|
76
|
+
_TOOL_ARGS_TOKEN_CHUNK_UPDATE_INTERVAL = 4
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _count_tokens(text: str) -> int:
|
|
80
|
+
"""Estimate token count from text (approx 4 chars per token)."""
|
|
81
|
+
return len(text) // 4
|
|
74
82
|
|
|
75
83
|
|
|
76
84
|
class StreamState(StrEnum):
|
|
@@ -228,6 +236,10 @@ async def run_single_turn(
|
|
|
228
236
|
pending_tool_calls: list[dict] = []
|
|
229
237
|
current_tool_call: dict | None = None
|
|
230
238
|
|
|
239
|
+
# Token counting for tool argument streaming
|
|
240
|
+
_tool_arg_chunk_counter = 0
|
|
241
|
+
_tool_arg_token_count = 0
|
|
242
|
+
|
|
231
243
|
current_state: StreamState | None = None
|
|
232
244
|
stop_reason: StopReason = StopReason.STOP
|
|
233
245
|
interrupted = False
|
|
@@ -381,6 +393,10 @@ async def run_single_turn(
|
|
|
381
393
|
pending_tool_calls.append(current_tool_call)
|
|
382
394
|
current_tool_call = None
|
|
383
395
|
|
|
396
|
+
# Reset token counters when starting a new tool call
|
|
397
|
+
_tool_arg_chunk_counter = 0
|
|
398
|
+
_tool_arg_token_count = 0
|
|
399
|
+
|
|
384
400
|
current_state = StreamState.TOOL_CALL
|
|
385
401
|
current_tool_call = {"id": id, "name": name, "arguments": ""}
|
|
386
402
|
|
|
@@ -391,6 +407,20 @@ async def run_single_turn(
|
|
|
391
407
|
current_tool_call["arguments"] += delta
|
|
392
408
|
yield ToolArgsDeltaEvent(tool_call_id=current_tool_call["id"], delta=delta)
|
|
393
409
|
|
|
410
|
+
# Count tokens and fire update event every Nth chunk after threshold tokens
|
|
411
|
+
_tool_arg_chunk_counter += 1
|
|
412
|
+
_tool_arg_token_count += _count_tokens(delta)
|
|
413
|
+
|
|
414
|
+
if (
|
|
415
|
+
_tool_arg_token_count > _TOOL_ARGS_TOKEN_DISPLAY_THRESHOLD
|
|
416
|
+
and _tool_arg_chunk_counter % _TOOL_ARGS_TOKEN_CHUNK_UPDATE_INTERVAL == 0
|
|
417
|
+
):
|
|
418
|
+
yield ToolArgsTokenUpdateEvent(
|
|
419
|
+
tool_call_id=current_tool_call["id"],
|
|
420
|
+
tool_name=current_tool_call["name"],
|
|
421
|
+
token_count=_tool_arg_token_count,
|
|
422
|
+
)
|
|
423
|
+
|
|
394
424
|
case StreamDone(stop_reason=reason):
|
|
395
425
|
stop_reason = reason
|
|
396
426
|
|
|
@@ -4,8 +4,10 @@ import os
|
|
|
4
4
|
import shutil
|
|
5
5
|
import sys
|
|
6
6
|
import time
|
|
7
|
+
import tomllib
|
|
7
8
|
from collections import deque
|
|
8
9
|
from importlib.metadata import PackageNotFoundError, version
|
|
10
|
+
from pathlib import Path
|
|
9
11
|
from typing import ClassVar
|
|
10
12
|
|
|
11
13
|
from rich.console import Console
|
|
@@ -32,6 +34,7 @@ from ..events import (
|
|
|
32
34
|
ThinkingDeltaEvent,
|
|
33
35
|
ThinkingEndEvent,
|
|
34
36
|
ThinkingStartEvent,
|
|
37
|
+
ToolArgsTokenUpdateEvent,
|
|
35
38
|
ToolEndEvent,
|
|
36
39
|
ToolResultEvent,
|
|
37
40
|
ToolStartEvent,
|
|
@@ -65,12 +68,24 @@ from .session_ui import SessionUIMixin
|
|
|
65
68
|
from .styles import STYLES
|
|
66
69
|
from .widgets import InfoBar, QueueDisplay, StatusLine, format_path
|
|
67
70
|
|
|
68
|
-
|
|
71
|
+
|
|
72
|
+
def _get_package_name() -> str:
|
|
73
|
+
pyproject_path = Path(__file__).parent.parent.parent.parent / "pyproject.toml"
|
|
74
|
+
if pyproject_path.exists():
|
|
75
|
+
try:
|
|
76
|
+
data = tomllib.loads(pyproject_path.read_text())
|
|
77
|
+
return data["project"]["name"]
|
|
78
|
+
except Exception:
|
|
79
|
+
pass
|
|
80
|
+
return "kon-coding-agent"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
_PYPI_PACKAGE_NAME = _get_package_name()
|
|
69
84
|
|
|
70
85
|
try:
|
|
71
86
|
VERSION = version(_PYPI_PACKAGE_NAME)
|
|
72
87
|
except PackageNotFoundError:
|
|
73
|
-
VERSION = "0.2.
|
|
88
|
+
VERSION = "0.2.2"
|
|
74
89
|
|
|
75
90
|
_COPILOT_API_TYPES: frozenset[ApiType] = frozenset(
|
|
76
91
|
{ApiType.GITHUB_COPILOT, ApiType.GITHUB_COPILOT_RESPONSES, ApiType.ANTHROPIC_COPILOT}
|
|
@@ -202,16 +217,20 @@ class Kon(CommandsMixin, SessionUIMixin, App[None]):
|
|
|
202
217
|
if self._session.entries:
|
|
203
218
|
model_info = self._session.model
|
|
204
219
|
if model_info:
|
|
205
|
-
provider, self._model = model_info
|
|
220
|
+
provider, self._model, session_base_url = model_info
|
|
206
221
|
self._model_provider = provider
|
|
222
|
+
if self._base_url is None and session_base_url:
|
|
223
|
+
self._base_url = session_base_url
|
|
207
224
|
self._thinking_level = self._session.thinking_level
|
|
208
225
|
elif self._continue_recent:
|
|
209
226
|
self._session = Session.continue_recent(self._cwd)
|
|
210
227
|
if self._session.entries:
|
|
211
228
|
model_info = self._session.model
|
|
212
229
|
if model_info:
|
|
213
|
-
provider, self._model = model_info
|
|
230
|
+
provider, self._model, session_base_url = model_info
|
|
214
231
|
self._model_provider = provider
|
|
232
|
+
if self._base_url is None and session_base_url:
|
|
233
|
+
self._base_url = session_base_url
|
|
215
234
|
self._thinking_level = self._session.thinking_level
|
|
216
235
|
|
|
217
236
|
model_info = get_model(self._model, self._model_provider)
|
|
@@ -257,7 +276,7 @@ class Kon(CommandsMixin, SessionUIMixin, App[None]):
|
|
|
257
276
|
model_id=self._model,
|
|
258
277
|
thinking_level=self._thinking_level,
|
|
259
278
|
)
|
|
260
|
-
self._session.append_model_change(model_provider, self._model)
|
|
279
|
+
self._session.append_model_change(model_provider, self._model, base_url)
|
|
261
280
|
|
|
262
281
|
self._project_context = Context.load(self._cwd)
|
|
263
282
|
# TODO: Surface self._project_context.skill_warnings in UI (e.g. chat info/error messages)
|
|
@@ -567,7 +586,6 @@ class Kon(CommandsMixin, SessionUIMixin, App[None]):
|
|
|
567
586
|
tools = get_tools(DEFAULT_TOOLS)
|
|
568
587
|
model_info = get_model(self._model, self._model_provider)
|
|
569
588
|
agent_config = AgentConfig(
|
|
570
|
-
max_turns=50,
|
|
571
589
|
system_prompt=self._get_system_prompt(),
|
|
572
590
|
context_window=model_info.context_window if model_info else None,
|
|
573
591
|
max_output_tokens=model_info.max_tokens if model_info else None,
|
|
@@ -621,6 +639,10 @@ class Kon(CommandsMixin, SessionUIMixin, App[None]):
|
|
|
621
639
|
chat.start_tool(name, id, "")
|
|
622
640
|
self._current_block_type = "tool_call"
|
|
623
641
|
status.increment_tool_calls()
|
|
642
|
+
status.set_streaming_tokens(0) # Reset token count for new tool
|
|
643
|
+
|
|
644
|
+
case ToolArgsTokenUpdateEvent(token_count=tc):
|
|
645
|
+
status.set_streaming_tokens(tc)
|
|
624
646
|
|
|
625
647
|
case ToolEndEvent(tool_call_id=id, display=display):
|
|
626
648
|
chat.update_tool_call_msg(id, display)
|
|
@@ -743,6 +765,7 @@ def main():
|
|
|
743
765
|
dest="resume_session",
|
|
744
766
|
help="Resume a specific session by ID (full or unique prefix)",
|
|
745
767
|
)
|
|
768
|
+
parser.add_argument("--version", action="version", version=f"kon {VERSION}")
|
|
746
769
|
args = parser.parse_args()
|
|
747
770
|
|
|
748
771
|
app = Kon(
|