sigit-code 1.6.4__tar.gz → 1.6.6__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.
Files changed (117) hide show
  1. {sigit_code-1.6.4 → sigit_code-1.6.6}/.agents/AGENTS.md +42 -2
  2. {sigit_code-1.6.4 → sigit_code-1.6.6}/AGENTS.md +42 -2
  3. {sigit_code-1.6.4 → sigit_code-1.6.6}/CHANGELOG.md +113 -0
  4. {sigit_code-1.6.4 → sigit_code-1.6.6}/CLAUDE.md +42 -2
  5. {sigit_code-1.6.4 → sigit_code-1.6.6}/Cargo.lock +305 -126
  6. {sigit_code-1.6.4 → sigit_code-1.6.6}/Cargo.toml +7 -4
  7. {sigit_code-1.6.4 → sigit_code-1.6.6}/PKG-INFO +1 -1
  8. {sigit_code-1.6.4 → sigit_code-1.6.6}/README.md +30 -0
  9. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/backend.rs +484 -80
  10. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/chat.rs +28 -7
  11. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/client_fs.rs +3 -2
  12. sigit_code-1.6.6/src/client_terminal.rs +468 -0
  13. sigit_code-1.6.6/src/harness_markup.rs +381 -0
  14. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/headless.rs +49 -22
  15. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/main.rs +837 -146
  16. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/mcp.rs +140 -739
  17. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/provider.rs +18 -0
  18. sigit_code-1.6.6/src/repetition.rs +221 -0
  19. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/tools.rs +539 -110
  20. sigit_code-1.6.6/tests/acp_audio.rs +310 -0
  21. sigit_code-1.6.6/tests/acp_client_terminal.rs +504 -0
  22. {sigit_code-1.6.4 → sigit_code-1.6.6}/tests/acp_concurrent_sessions.rs +150 -3
  23. sigit_code-1.6.6/tests/acp_embedded_context.rs +274 -0
  24. sigit_code-1.6.6/tests/acp_harness_markup.rs +270 -0
  25. {sigit_code-1.6.4 → sigit_code-1.6.6}/tests/acp_permissions.rs +154 -1
  26. sigit_code-1.6.6/tests/acp_tool_call_name.rs +277 -0
  27. {sigit_code-1.6.4 → sigit_code-1.6.6}/.agents/skills/agent-client-protocol/SKILL.md +0 -0
  28. {sigit_code-1.6.4 → sigit_code-1.6.6}/.agents/skills/ai-assisted-coding/SKILL.md +0 -0
  29. {sigit_code-1.6.4 → sigit_code-1.6.6}/.agents/skills/branding/SKILL.md +0 -0
  30. {sigit_code-1.6.4 → sigit_code-1.6.6}/.agents/skills/run-sigit/SKILL.md +0 -0
  31. {sigit_code-1.6.4 → sigit_code-1.6.6}/.agents/skills/run-sigit/driver.mjs +0 -0
  32. {sigit_code-1.6.4 → sigit_code-1.6.6}/.agents/skills/run-sigit/tui-smoke.sh +0 -0
  33. {sigit_code-1.6.4 → sigit_code-1.6.6}/.agents/skills/sigit-code-release/SKILL.md +0 -0
  34. {sigit_code-1.6.4 → sigit_code-1.6.6}/.agents/skills/tool-calling/SKILL.md +0 -0
  35. {sigit_code-1.6.4 → sigit_code-1.6.6}/.cargo/config.toml +0 -0
  36. {sigit_code-1.6.4 → sigit_code-1.6.6}/.claude/skills/agent-client-protocol/SKILL.md +0 -0
  37. {sigit_code-1.6.4 → sigit_code-1.6.6}/.claude/skills/ai-assisted-coding/SKILL.md +0 -0
  38. {sigit_code-1.6.4 → sigit_code-1.6.6}/.claude/skills/branding/SKILL.md +0 -0
  39. {sigit_code-1.6.4 → sigit_code-1.6.6}/.claude/skills/run-sigit/SKILL.md +0 -0
  40. {sigit_code-1.6.4 → sigit_code-1.6.6}/.claude/skills/run-sigit/driver.mjs +0 -0
  41. {sigit_code-1.6.4 → sigit_code-1.6.6}/.claude/skills/run-sigit/tui-smoke.sh +0 -0
  42. {sigit_code-1.6.4 → sigit_code-1.6.6}/.claude/skills/sigit-code-release/SKILL.md +0 -0
  43. {sigit_code-1.6.4 → sigit_code-1.6.6}/.claude/skills/tool-calling/SKILL.md +0 -0
  44. {sigit_code-1.6.4 → sigit_code-1.6.6}/.github/workflows/ci.yml +0 -0
  45. {sigit_code-1.6.4 → sigit_code-1.6.6}/.github/workflows/release-aur.yml +0 -0
  46. {sigit_code-1.6.4 → sigit_code-1.6.6}/.github/workflows/release-crates.yml +0 -0
  47. {sigit_code-1.6.4 → sigit_code-1.6.6}/.github/workflows/release-github.yml +0 -0
  48. {sigit_code-1.6.4 → sigit_code-1.6.6}/.github/workflows/release-homebrew.yml +0 -0
  49. {sigit_code-1.6.4 → sigit_code-1.6.6}/.github/workflows/release-npm.yml +0 -0
  50. {sigit_code-1.6.4 → sigit_code-1.6.6}/.github/workflows/release-nuget.yml +0 -0
  51. {sigit_code-1.6.4 → sigit_code-1.6.6}/.github/workflows/release-pypi.yml +0 -0
  52. {sigit_code-1.6.4 → sigit_code-1.6.6}/.github/workflows/release-scoop.yml +0 -0
  53. {sigit_code-1.6.4 → sigit_code-1.6.6}/.github/workflows/release-winget.yml +0 -0
  54. {sigit_code-1.6.4 → sigit_code-1.6.6}/.gitignore +0 -0
  55. {sigit_code-1.6.4 → sigit_code-1.6.6}/.nvmrc +0 -0
  56. {sigit_code-1.6.4 → sigit_code-1.6.6}/.sigit/code-cloud-agent/config.toml +0 -0
  57. {sigit_code-1.6.4 → sigit_code-1.6.6}/.sigit/code-review/config.toml +0 -0
  58. {sigit_code-1.6.4 → sigit_code-1.6.6}/.sigit/factory/config.toml +0 -0
  59. {sigit_code-1.6.4 → sigit_code-1.6.6}/LICENSE +0 -0
  60. {sigit_code-1.6.4 → sigit_code-1.6.6}/docs/headless.md +0 -0
  61. {sigit_code-1.6.4 → sigit_code-1.6.6}/docs/hooks.md +0 -0
  62. {sigit_code-1.6.4 → sigit_code-1.6.6}/docs/mcp.md +0 -0
  63. {sigit_code-1.6.4 → sigit_code-1.6.6}/examples/settings-with-hooks.toml +0 -0
  64. {sigit_code-1.6.4 → sigit_code-1.6.6}/examples/skills/README.md +0 -0
  65. {sigit_code-1.6.4 → sigit_code-1.6.6}/examples/skills/commit-message/SKILL.md +0 -0
  66. {sigit_code-1.6.4 → sigit_code-1.6.6}/npm/README.md.tmpl +0 -0
  67. {sigit_code-1.6.4 → sigit_code-1.6.6}/npm/package-compat.json.tmpl +0 -0
  68. {sigit_code-1.6.4 → sigit_code-1.6.6}/npm/package-main.json.tmpl +0 -0
  69. {sigit_code-1.6.4 → sigit_code-1.6.6}/npm/package.json.tmpl +0 -0
  70. {sigit_code-1.6.4 → sigit_code-1.6.6}/npm/scripts/render-main-package.cjs +0 -0
  71. {sigit_code-1.6.4 → sigit_code-1.6.6}/npm/scripts/render-platform-package.cjs +0 -0
  72. {sigit_code-1.6.4 → sigit_code-1.6.6}/npm/sigit/.gitignore +0 -0
  73. {sigit_code-1.6.4 → sigit_code-1.6.6}/npm/sigit/README.md +0 -0
  74. {sigit_code-1.6.4 → sigit_code-1.6.6}/npm/sigit/package.json +0 -0
  75. {sigit_code-1.6.4 → sigit_code-1.6.6}/npm/sigit/src/index.ts +0 -0
  76. {sigit_code-1.6.4 → sigit_code-1.6.6}/npm/sigit/tsconfig.json +0 -0
  77. {sigit_code-1.6.4 → sigit_code-1.6.6}/nuget/.gitignore +0 -0
  78. {sigit_code-1.6.4 → sigit_code-1.6.6}/nuget/sigit/Program.cs +0 -0
  79. {sigit_code-1.6.4 → sigit_code-1.6.6}/nuget/sigit/README.md +0 -0
  80. {sigit_code-1.6.4 → sigit_code-1.6.6}/nuget/sigit/SiGit.Code.csproj +0 -0
  81. {sigit_code-1.6.4 → sigit_code-1.6.6}/packaging/aur/PKGBUILD.in +0 -0
  82. {sigit_code-1.6.4 → sigit_code-1.6.6}/packaging/nfpm.yaml +0 -0
  83. {sigit_code-1.6.4 → sigit_code-1.6.6}/packaging/windows/check-static-crt.ps1 +0 -0
  84. {sigit_code-1.6.4 → sigit_code-1.6.6}/packaging/winget/getSigit.siGitCode.installer.yaml.in +0 -0
  85. {sigit_code-1.6.4 → sigit_code-1.6.6}/packaging/winget/getSigit.siGitCode.locale.en-US.yaml.in +0 -0
  86. {sigit_code-1.6.4 → sigit_code-1.6.6}/packaging/winget/getSigit.siGitCode.yaml.in +0 -0
  87. {sigit_code-1.6.4 → sigit_code-1.6.6}/pypi/README.md +0 -0
  88. {sigit_code-1.6.4 → sigit_code-1.6.6}/pypi/pyproject.toml +0 -0
  89. {sigit_code-1.6.4 → sigit_code-1.6.6}/pyproject.toml +0 -0
  90. {sigit_code-1.6.4 → sigit_code-1.6.6}/rust-toolchain.toml +0 -0
  91. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/account.rs +0 -0
  92. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/browser_auth.rs +0 -0
  93. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/commands.rs +0 -0
  94. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/credentials.rs +0 -0
  95. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/frontmatter.rs +0 -0
  96. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/hooks.rs +0 -0
  97. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/inline_tool_calls.rs +0 -0
  98. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/instructions.rs +0 -0
  99. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/models.rs +0 -0
  100. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/permissions.rs +0 -0
  101. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/session_store.rs +0 -0
  102. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/settings.rs +0 -0
  103. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/setup.rs +0 -0
  104. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/skills.rs +0 -0
  105. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/subagents.rs +0 -0
  106. {sigit_code-1.6.4 → sigit_code-1.6.6}/src/workspace.rs +0 -0
  107. {sigit_code-1.6.4 → sigit_code-1.6.6}/tests/acp_client_fs.rs +0 -0
  108. {sigit_code-1.6.4 → sigit_code-1.6.6}/tests/acp_endpoint_errors.rs +0 -0
  109. {sigit_code-1.6.4 → sigit_code-1.6.6}/tests/acp_images.rs +0 -0
  110. {sigit_code-1.6.4 → sigit_code-1.6.6}/tests/acp_logout.rs +0 -0
  111. {sigit_code-1.6.4 → sigit_code-1.6.6}/tests/acp_multi_root.rs +0 -0
  112. {sigit_code-1.6.4 → sigit_code-1.6.6}/tests/acp_session_list.rs +0 -0
  113. {sigit_code-1.6.4 → sigit_code-1.6.6}/tests/acp_session_load.rs +0 -0
  114. {sigit_code-1.6.4 → sigit_code-1.6.6}/tests/acp_stop_reasons.rs +0 -0
  115. {sigit_code-1.6.4 → sigit_code-1.6.6}/tests/acp_tool_stdin.rs +0 -0
  116. {sigit_code-1.6.4 → sigit_code-1.6.6}/tests/headless_mode.rs +0 -0
  117. {sigit_code-1.6.4 → sigit_code-1.6.6}/tests/version_flag.rs +0 -0
@@ -152,6 +152,19 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
152
152
  well-formed ones. A block that doesn't parse (or never closes) is dropped from both the reply
153
153
  and history, and `OpenAiBackend::complete` retries once with a note telling the model the call
154
154
  didn't run. Leaving the raw block in history makes the model invent `<function_results>` later.
155
+ What that scanner lets through then goes through `src/harness_markup.rs`, which removes
156
+ markup the model makes up in the harness's shape (issue #122): a `<system_warning>`-style
157
+ block (any `system_*`/`system-*` tag) is dropped whole, from the reply and the history, and
158
+ other underscore-named tags like `<Option_Picker>` lose the tags but keep their text. Code is
159
+ never touched, and an opening tag only counts at the start of a line, since prose writes
160
+ placeholders like `<repo_url>` the same way. It has to run after the inline-call scanner,
161
+ because `<tool_call>` matches its underscore rule.
162
+ `src/repetition.rs` guards the same stream against a reply that degenerates into a loop over a
163
+ tiny vocabulary (issue #123): a window of 200 words with at most 40 distinct ones, fenced code
164
+ excepted, makes `consume_stream` stop reading, truncate the text to where the loop began (so
165
+ history never holds the junk), tell the user, and finish with `FinishReason::Repetition`
166
+ (`EndTurn` in ACP). `complete` then retries once with `REPETITION_RETRY`, like the malformed
167
+ tool call retry. Only the streaming path is guarded; `consume_json` and `LocalBackend` are not.
155
168
  Image attachments: ACP fixes `promptCapabilities.image` for the whole connection, while the
156
169
  model can change on any turn, so the capability is always advertised and the decision is made
157
170
  per prompt. `InferenceBackend::accepts_images` answers for the active model (on-device: no;
@@ -163,6 +176,11 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
163
176
  when its model cannot read them, which is what makes a mid-thread switch to a text-only tier
164
177
  safe. Read history text through `backend::message_text`, never `content.as_str()`.
165
178
  `IMAGE_TIERS` mirrors onde-cloud's `IMAGE_CANDIDATES`; update both together.
179
+ Audio attachments take the same path (`promptCapabilities.audio`, `accepts_audio`,
180
+ `audio_for_turn`, `without_audio`), sent as OpenAI `input_audio` parts. No cloud tier
181
+ takes audio, because onde-cloud drops `input_audio` parts when it parses a request, so
182
+ `provider::model_accepts_audio` says no for every `onde-*` id and yes for a user's own
183
+ endpoint. If onde-cloud ever routes audio, give it a tier table like `IMAGE_TIERS`.
166
184
  - **`src/provider.rs`** — decides *which* backend serves inference. Resolution order, first match
167
185
  wins: (1) override via `OPENAI_BASE_URL`+`OPENAI_API_KEY` or active profile in
168
186
  `~/.config/sigit/providers.toml`; (2) siGit Code Cloud when logged in; (3) on-device.
@@ -330,7 +348,11 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
330
348
  `execute_tool_impl` asks `route_for` before it dispatches `read_file`, `create_file`,
331
349
  `edit_file` or `multi_edit`. Each of those tools is split into a parse step and a pure
332
350
  render/apply step (`ReadFileCall`, `CreateFileCall`, `EditCall`) that the disk path and the
333
- client path share, so the model gets the same result text either way. The two capabilities are
351
+ client path share, so the model gets the same result text either way. The writing tools also
352
+ hand back the file before and after (`tools::FileChange`, via `execute_tool_with_change`), which
353
+ `handle_prompt` sends as ACP `diff` content ahead of the result text. A permission request for
354
+ one of them carries the same diff, worked out by `preview_file_change` without writing anything
355
+ and before the workspace is released, since a relative path resolves against the session's cwd. The two capabilities are
334
356
  independent: a client that only reads still gets its edits written to disk. The disk is the
335
357
  fallback throughout: nothing is registered in the TUI or headless modes, a path outside the
336
358
  session's roots is not routed, and a request the client fails or leaves unanswered for 30
@@ -338,6 +360,23 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
338
360
  inside a spawned prompt turn, never from `handle_initialize` itself (deadlock, same as
339
361
  permission requests). Existence checks and `create_file`'s parent directories still use the
340
362
  disk. `SIGIT_CLIENT_FS=off` turns the routing off. Covered by `tests/acp_client_fs.rs`.
363
+ - **`src/client_terminal.rs`** — `run_command` in the ACP client's terminal. A client that
364
+ advertises `terminal` in `initialize` runs a foreground command itself (`terminal/create`, with
365
+ the platform shell as `command` and the whole command line as one argument), and
366
+ `exec_run_command_via_client` embeds the terminal in the tool call as `terminal` content, so the
367
+ user watches the output live and can stop the command from the editor. The seam is
368
+ `client_fs`'s: `handle_initialize` registers `AcpClientTerminal`, and `execute_tool_impl` asks
369
+ `client_terminal_route` before the blocking path. Embedding needs the tool call's id, which is
370
+ why `execute_tool_with_change` takes one. What `run_command` promises stays put: the 120 s
371
+ timeout ends in `terminal/kill`, the output is capped, and `CommitWatch` still amends the
372
+ co-author trailer, checking HEAD on disk before and after. Background commands stay local
373
+ (`command_output` and `kill_command` read their pipes), and so does a command whose directory
374
+ is outside the session's roots, since nothing says the editor's machine can see it. A
375
+ `terminal/create` the client fails means nothing ran, so the command runs locally; a failure
376
+ after that is reported as the tool's error instead, because running it again could repeat its
377
+ effects. The finished card keeps the terminal and drops the result text, which still goes out
378
+ as `raw_output`. `SIGIT_CLIENT_TERMINAL=off` turns the routing off. Covered by
379
+ `tests/acp_client_terminal.rs`.
341
380
  - **`src/chat.rs`** — the Unix-only ratatui TUI. Loading-spinner phase then chat; uses
342
381
  `tokio::select!` to multiplex terminal events with streaming tokens.
343
382
  - **`src/headless.rs`** — non-interactive `sigit run` execution for scripts, CI, and Factory
@@ -407,7 +446,8 @@ verbosity with `RUST_LOG`.
407
446
  default `https://sigit.si`), `SIGIT_CLOUD_URL`, `SIGIT_CONFIG_DIR` (default `~/.config/sigit`),
408
447
  `SIGIT_MODEL`, `SIGIT_MAX_TOOL_ROUNDS` (1 to 500, default 24; the tool-round cap of a
409
448
  headless run or an ACP prompt turn), `SIGIT_MCP` (`off` disables MCP), `SIGIT_CLIENT_FS` (`off` keeps the file tools on disk even when the
410
- ACP client offers `fs/read_text_file` / `fs/write_text_file`), `SIGIT_MCP_SMBCLOUD` (`off` drops the baked-in
449
+ ACP client offers `fs/read_text_file` / `fs/write_text_file`), `SIGIT_CLIENT_TERMINAL` (`off` runs
450
+ `run_command` locally even when the ACP client offers a terminal), `SIGIT_MCP_SMBCLOUD` (`off` drops the baked-in
411
451
  smbCloud CLI server), `SIGIT_MCP_OFFICIAL` (`off` drops the baked-in
412
452
  server), `SIGIT_PERMISSIONS` (`allow`/`ask`/`deny` — overrides the default permission mode for
413
453
  mutating tools; the escape hatch for clients without permission-request support),
@@ -152,6 +152,19 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
152
152
  well-formed ones. A block that doesn't parse (or never closes) is dropped from both the reply
153
153
  and history, and `OpenAiBackend::complete` retries once with a note telling the model the call
154
154
  didn't run. Leaving the raw block in history makes the model invent `<function_results>` later.
155
+ What that scanner lets through then goes through `src/harness_markup.rs`, which removes
156
+ markup the model makes up in the harness's shape (issue #122): a `<system_warning>`-style
157
+ block (any `system_*`/`system-*` tag) is dropped whole, from the reply and the history, and
158
+ other underscore-named tags like `<Option_Picker>` lose the tags but keep their text. Code is
159
+ never touched, and an opening tag only counts at the start of a line, since prose writes
160
+ placeholders like `<repo_url>` the same way. It has to run after the inline-call scanner,
161
+ because `<tool_call>` matches its underscore rule.
162
+ `src/repetition.rs` guards the same stream against a reply that degenerates into a loop over a
163
+ tiny vocabulary (issue #123): a window of 200 words with at most 40 distinct ones, fenced code
164
+ excepted, makes `consume_stream` stop reading, truncate the text to where the loop began (so
165
+ history never holds the junk), tell the user, and finish with `FinishReason::Repetition`
166
+ (`EndTurn` in ACP). `complete` then retries once with `REPETITION_RETRY`, like the malformed
167
+ tool call retry. Only the streaming path is guarded; `consume_json` and `LocalBackend` are not.
155
168
  Image attachments: ACP fixes `promptCapabilities.image` for the whole connection, while the
156
169
  model can change on any turn, so the capability is always advertised and the decision is made
157
170
  per prompt. `InferenceBackend::accepts_images` answers for the active model (on-device: no;
@@ -163,6 +176,11 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
163
176
  when its model cannot read them, which is what makes a mid-thread switch to a text-only tier
164
177
  safe. Read history text through `backend::message_text`, never `content.as_str()`.
165
178
  `IMAGE_TIERS` mirrors onde-cloud's `IMAGE_CANDIDATES`; update both together.
179
+ Audio attachments take the same path (`promptCapabilities.audio`, `accepts_audio`,
180
+ `audio_for_turn`, `without_audio`), sent as OpenAI `input_audio` parts. No cloud tier
181
+ takes audio, because onde-cloud drops `input_audio` parts when it parses a request, so
182
+ `provider::model_accepts_audio` says no for every `onde-*` id and yes for a user's own
183
+ endpoint. If onde-cloud ever routes audio, give it a tier table like `IMAGE_TIERS`.
166
184
  - **`src/provider.rs`** — decides *which* backend serves inference. Resolution order, first match
167
185
  wins: (1) override via `OPENAI_BASE_URL`+`OPENAI_API_KEY` or active profile in
168
186
  `~/.config/sigit/providers.toml`; (2) siGit Code Cloud when logged in; (3) on-device.
@@ -330,7 +348,11 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
330
348
  `execute_tool_impl` asks `route_for` before it dispatches `read_file`, `create_file`,
331
349
  `edit_file` or `multi_edit`. Each of those tools is split into a parse step and a pure
332
350
  render/apply step (`ReadFileCall`, `CreateFileCall`, `EditCall`) that the disk path and the
333
- client path share, so the model gets the same result text either way. The two capabilities are
351
+ client path share, so the model gets the same result text either way. The writing tools also
352
+ hand back the file before and after (`tools::FileChange`, via `execute_tool_with_change`), which
353
+ `handle_prompt` sends as ACP `diff` content ahead of the result text. A permission request for
354
+ one of them carries the same diff, worked out by `preview_file_change` without writing anything
355
+ and before the workspace is released, since a relative path resolves against the session's cwd. The two capabilities are
334
356
  independent: a client that only reads still gets its edits written to disk. The disk is the
335
357
  fallback throughout: nothing is registered in the TUI or headless modes, a path outside the
336
358
  session's roots is not routed, and a request the client fails or leaves unanswered for 30
@@ -338,6 +360,23 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
338
360
  inside a spawned prompt turn, never from `handle_initialize` itself (deadlock, same as
339
361
  permission requests). Existence checks and `create_file`'s parent directories still use the
340
362
  disk. `SIGIT_CLIENT_FS=off` turns the routing off. Covered by `tests/acp_client_fs.rs`.
363
+ - **`src/client_terminal.rs`** — `run_command` in the ACP client's terminal. A client that
364
+ advertises `terminal` in `initialize` runs a foreground command itself (`terminal/create`, with
365
+ the platform shell as `command` and the whole command line as one argument), and
366
+ `exec_run_command_via_client` embeds the terminal in the tool call as `terminal` content, so the
367
+ user watches the output live and can stop the command from the editor. The seam is
368
+ `client_fs`'s: `handle_initialize` registers `AcpClientTerminal`, and `execute_tool_impl` asks
369
+ `client_terminal_route` before the blocking path. Embedding needs the tool call's id, which is
370
+ why `execute_tool_with_change` takes one. What `run_command` promises stays put: the 120 s
371
+ timeout ends in `terminal/kill`, the output is capped, and `CommitWatch` still amends the
372
+ co-author trailer, checking HEAD on disk before and after. Background commands stay local
373
+ (`command_output` and `kill_command` read their pipes), and so does a command whose directory
374
+ is outside the session's roots, since nothing says the editor's machine can see it. A
375
+ `terminal/create` the client fails means nothing ran, so the command runs locally; a failure
376
+ after that is reported as the tool's error instead, because running it again could repeat its
377
+ effects. The finished card keeps the terminal and drops the result text, which still goes out
378
+ as `raw_output`. `SIGIT_CLIENT_TERMINAL=off` turns the routing off. Covered by
379
+ `tests/acp_client_terminal.rs`.
341
380
  - **`src/chat.rs`** — the Unix-only ratatui TUI. Loading-spinner phase then chat; uses
342
381
  `tokio::select!` to multiplex terminal events with streaming tokens.
343
382
  - **`src/headless.rs`** — non-interactive `sigit run` execution for scripts, CI, and Factory
@@ -407,7 +446,8 @@ verbosity with `RUST_LOG`.
407
446
  default `https://sigit.si`), `SIGIT_CLOUD_URL`, `SIGIT_CONFIG_DIR` (default `~/.config/sigit`),
408
447
  `SIGIT_MODEL`, `SIGIT_MAX_TOOL_ROUNDS` (1 to 500, default 24; the tool-round cap of a
409
448
  headless run or an ACP prompt turn), `SIGIT_MCP` (`off` disables MCP), `SIGIT_CLIENT_FS` (`off` keeps the file tools on disk even when the
410
- ACP client offers `fs/read_text_file` / `fs/write_text_file`), `SIGIT_MCP_SMBCLOUD` (`off` drops the baked-in
449
+ ACP client offers `fs/read_text_file` / `fs/write_text_file`), `SIGIT_CLIENT_TERMINAL` (`off` runs
450
+ `run_command` locally even when the ACP client offers a terminal), `SIGIT_MCP_SMBCLOUD` (`off` drops the baked-in
411
451
  smbCloud CLI server), `SIGIT_MCP_OFFICIAL` (`off` drops the baked-in
412
452
  server), `SIGIT_PERMISSIONS` (`allow`/`ask`/`deny` — overrides the default permission mode for
413
453
  mutating tools; the escape hatch for clients without permission-request support),
@@ -1,5 +1,118 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 1.6.6
6
+
7
+ ### Added
8
+
9
+ - **A tool call carries its programmatic tool name.** The `tool_call` an editor
10
+ gets for a running tool now includes `name` (`read_file`, `run_command`,
11
+ `mcp__…`) next to the human title, sent on the first report of the call only
12
+ as the protocol asks. A client can tell a command from a file edit without
13
+ parsing the title, and a replayed thread from `session/load` carries the same
14
+ name (#196).
15
+
16
+ ### Changed
17
+
18
+ - **MCP runs on `rmcp`, the official Rust SDK, through `ed-mcp`.** siGit Code's
19
+ own JSON-RPC client for stdio and Streamable HTTP servers (about 600 lines)
20
+ is gone; `ed-mcp` is the layer every Onde agent shares. `mcp.toml`, the
21
+ baked-in `sigit` and `smbcloud` servers, tool names and `/mcp` are unchanged.
22
+ An HTTP server that stops answering is now reconnected and the call retried
23
+ once for any failure but a timeout, not only on a 404.
24
+
25
+ ### Fixed
26
+
27
+ - **A reply that degenerates into a repetition loop is cut off.** After a tool
28
+ call, a model could fall into thousands of words from a tiny vocabulary
29
+ (`find: list: check: get: …`) with no tool call and no end, and the junk went
30
+ into history for the next turn. siGit Code now watches a streamed reply for a
31
+ window of 200 words with very few distinct ones (code blocks excepted), stops
32
+ reading, keeps only the text before the loop, tells you it stopped, and asks
33
+ the model once to make its call or answer briefly (#123).
34
+ - **A linked selection written as `L10-L20` or `L10` reads just those lines.**
35
+ siGit Code read the line range of a `resource_link` only as `L10:20` or
36
+ `L10-20`, so `L10-L20` (the GitHub style) and a single line `L10` sent the
37
+ whole file instead of the selection. It now reads the same forms Zed's own
38
+ mention parser does, and a range that starts at line 0 or runs backwards
39
+ counts as no range.
40
+
41
+ ## 1.6.5
42
+
43
+ ### Added
44
+
45
+ - **An `@file` mention can carry unsaved edits.** siGit Code now advertises
46
+ `promptCapabilities.embeddedContext`, so a client can put a resource's
47
+ contents in the prompt instead of sending a link to it. Before, a client
48
+ that follows the spec only sent `resource_link`, and siGit Code read the
49
+ file from disk, missing whatever the user had not saved yet (#141).
50
+ - **A prompt can carry audio.** siGit Code now advertises
51
+ `promptCapabilities.audio` and sends an attached clip to the model as an
52
+ OpenAI `input_audio` part. It works the way image attachments do: a model on
53
+ an endpoint you configure yourself gets the clip and decides for itself,
54
+ while siGit Code Cloud tiers and on-device models, none of which take audio
55
+ today, get a note in its place and the editor says the clip was left out
56
+ (#143).
57
+ - **The README says what telemetry is sent.** A new Telemetry section covers
58
+ the model-timing events Onde Inference can report for on-device models,
59
+ what they leave out, and how to turn them off with `ONDE_DISABLE_PULSE=1`.
60
+
61
+ ### Changed
62
+
63
+ - **A turn that cannot get its session back stops.** A turn on an HTTP backend
64
+ gives up the process-wide working directory while it waits and reinstalls
65
+ its session before a tool runs. If the reinstall failed, the turn logged a
66
+ warning and carried on in whichever directory was current. Nothing makes it
67
+ fail today, since a close waits for the turn, but the turn now ends as
68
+ cancelled instead of depending on that.
69
+ - **Dependencies match the rest of the Onde stack.** Onde 1.2.2 to 1.3.1,
70
+ reqwest 0.12 to 0.13 and ratatui 0.29 to 0.30, the versions Ed, OndeCode and
71
+ SplitFire Agent use. ratatui is built with only its crossterm backend, which
72
+ drops the second copy of crossterm (0.28) from the build.
73
+
74
+ ### Fixed
75
+
76
+ - **Tags a model makes up no longer show in the reply.** After a long run of
77
+ tool rounds, a model could start writing markup that looked like it came
78
+ from siGit Code: a `<system_warning>` block telling the user the previous
79
+ turn was injected and should be ignored, or a question wrapped in
80
+ `<Option_Picker>`. Neither tag means anything to siGit Code or the editor,
81
+ so both rendered as raw text. A `system_*` block is now dropped from the
82
+ reply and from the history, and other made-up tags are removed while the
83
+ text inside them stays. Code blocks are left alone. The system prompt also
84
+ tells the model to write plain Markdown and to list choices as plain text
85
+ (#122).
86
+ - **Closing a thread that is waiting at a permission prompt no longer hangs.**
87
+ `session/close` cancels the session's running turn and waits for it to end,
88
+ but a turn stopped at a permission prompt only went on when the client
89
+ answered the request. A client that closed the thread without answering
90
+ left the close, and every later request for that thread, waiting for good.
91
+ The same held for `session/cancel` with a client that did not send the
92
+ cancelled outcome the protocol asks for. The permission wait now ends on
93
+ the cancellation as well, and an approval that arrives after the turn was
94
+ cancelled runs nothing.
95
+
96
+ - **A closed thread no longer leaves its request lock behind.** The process
97
+ kept one lock per session id for as long as it ran, for closed threads and
98
+ for ids no session ever had. The entry now goes with the last request that
99
+ used it.
100
+
101
+ - **A tool call that did not run no longer shows as completed.** When policy
102
+ or plan mode denied a call, when the user picked Deny at the permission
103
+ prompt, or when the repeat guard skipped a call the model made three times,
104
+ the editor's card still ended `completed`. Those cards now end `failed`, the
105
+ same way a call cancelled at the prompt already did. A call denied at the
106
+ prompt also gets its own title back instead of keeping the permission
107
+ request's (#139).
108
+
109
+ - **On-device inference no longer panics on some non-English text.** siGit Code
110
+ now builds on Onde 1.3.1, which fixes a panic when a message longer than 100
111
+ bytes had a multi-byte character (an accented letter, CJK, or an emoji) at
112
+ byte 100. Onde 1.3 also reports Pulse events for streaming inference, which
113
+ is the path on-device turns take, so those turns are now reported alongside
114
+ model loads. `ONDE_DISABLE_PULSE=1` turns Pulse off.
115
+
3
116
  ## 1.6.4
4
117
 
5
118
  ### Added
@@ -152,6 +152,19 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
152
152
  well-formed ones. A block that doesn't parse (or never closes) is dropped from both the reply
153
153
  and history, and `OpenAiBackend::complete` retries once with a note telling the model the call
154
154
  didn't run. Leaving the raw block in history makes the model invent `<function_results>` later.
155
+ What that scanner lets through then goes through `src/harness_markup.rs`, which removes
156
+ markup the model makes up in the harness's shape (issue #122): a `<system_warning>`-style
157
+ block (any `system_*`/`system-*` tag) is dropped whole, from the reply and the history, and
158
+ other underscore-named tags like `<Option_Picker>` lose the tags but keep their text. Code is
159
+ never touched, and an opening tag only counts at the start of a line, since prose writes
160
+ placeholders like `<repo_url>` the same way. It has to run after the inline-call scanner,
161
+ because `<tool_call>` matches its underscore rule.
162
+ `src/repetition.rs` guards the same stream against a reply that degenerates into a loop over a
163
+ tiny vocabulary (issue #123): a window of 200 words with at most 40 distinct ones, fenced code
164
+ excepted, makes `consume_stream` stop reading, truncate the text to where the loop began (so
165
+ history never holds the junk), tell the user, and finish with `FinishReason::Repetition`
166
+ (`EndTurn` in ACP). `complete` then retries once with `REPETITION_RETRY`, like the malformed
167
+ tool call retry. Only the streaming path is guarded; `consume_json` and `LocalBackend` are not.
155
168
  Image attachments: ACP fixes `promptCapabilities.image` for the whole connection, while the
156
169
  model can change on any turn, so the capability is always advertised and the decision is made
157
170
  per prompt. `InferenceBackend::accepts_images` answers for the active model (on-device: no;
@@ -163,6 +176,11 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
163
176
  when its model cannot read them, which is what makes a mid-thread switch to a text-only tier
164
177
  safe. Read history text through `backend::message_text`, never `content.as_str()`.
165
178
  `IMAGE_TIERS` mirrors onde-cloud's `IMAGE_CANDIDATES`; update both together.
179
+ Audio attachments take the same path (`promptCapabilities.audio`, `accepts_audio`,
180
+ `audio_for_turn`, `without_audio`), sent as OpenAI `input_audio` parts. No cloud tier
181
+ takes audio, because onde-cloud drops `input_audio` parts when it parses a request, so
182
+ `provider::model_accepts_audio` says no for every `onde-*` id and yes for a user's own
183
+ endpoint. If onde-cloud ever routes audio, give it a tier table like `IMAGE_TIERS`.
166
184
  - **`src/provider.rs`** — decides *which* backend serves inference. Resolution order, first match
167
185
  wins: (1) override via `OPENAI_BASE_URL`+`OPENAI_API_KEY` or active profile in
168
186
  `~/.config/sigit/providers.toml`; (2) siGit Code Cloud when logged in; (3) on-device.
@@ -330,7 +348,11 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
330
348
  `execute_tool_impl` asks `route_for` before it dispatches `read_file`, `create_file`,
331
349
  `edit_file` or `multi_edit`. Each of those tools is split into a parse step and a pure
332
350
  render/apply step (`ReadFileCall`, `CreateFileCall`, `EditCall`) that the disk path and the
333
- client path share, so the model gets the same result text either way. The two capabilities are
351
+ client path share, so the model gets the same result text either way. The writing tools also
352
+ hand back the file before and after (`tools::FileChange`, via `execute_tool_with_change`), which
353
+ `handle_prompt` sends as ACP `diff` content ahead of the result text. A permission request for
354
+ one of them carries the same diff, worked out by `preview_file_change` without writing anything
355
+ and before the workspace is released, since a relative path resolves against the session's cwd. The two capabilities are
334
356
  independent: a client that only reads still gets its edits written to disk. The disk is the
335
357
  fallback throughout: nothing is registered in the TUI or headless modes, a path outside the
336
358
  session's roots is not routed, and a request the client fails or leaves unanswered for 30
@@ -338,6 +360,23 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
338
360
  inside a spawned prompt turn, never from `handle_initialize` itself (deadlock, same as
339
361
  permission requests). Existence checks and `create_file`'s parent directories still use the
340
362
  disk. `SIGIT_CLIENT_FS=off` turns the routing off. Covered by `tests/acp_client_fs.rs`.
363
+ - **`src/client_terminal.rs`** — `run_command` in the ACP client's terminal. A client that
364
+ advertises `terminal` in `initialize` runs a foreground command itself (`terminal/create`, with
365
+ the platform shell as `command` and the whole command line as one argument), and
366
+ `exec_run_command_via_client` embeds the terminal in the tool call as `terminal` content, so the
367
+ user watches the output live and can stop the command from the editor. The seam is
368
+ `client_fs`'s: `handle_initialize` registers `AcpClientTerminal`, and `execute_tool_impl` asks
369
+ `client_terminal_route` before the blocking path. Embedding needs the tool call's id, which is
370
+ why `execute_tool_with_change` takes one. What `run_command` promises stays put: the 120 s
371
+ timeout ends in `terminal/kill`, the output is capped, and `CommitWatch` still amends the
372
+ co-author trailer, checking HEAD on disk before and after. Background commands stay local
373
+ (`command_output` and `kill_command` read their pipes), and so does a command whose directory
374
+ is outside the session's roots, since nothing says the editor's machine can see it. A
375
+ `terminal/create` the client fails means nothing ran, so the command runs locally; a failure
376
+ after that is reported as the tool's error instead, because running it again could repeat its
377
+ effects. The finished card keeps the terminal and drops the result text, which still goes out
378
+ as `raw_output`. `SIGIT_CLIENT_TERMINAL=off` turns the routing off. Covered by
379
+ `tests/acp_client_terminal.rs`.
341
380
  - **`src/chat.rs`** — the Unix-only ratatui TUI. Loading-spinner phase then chat; uses
342
381
  `tokio::select!` to multiplex terminal events with streaming tokens.
343
382
  - **`src/headless.rs`** — non-interactive `sigit run` execution for scripts, CI, and Factory
@@ -407,7 +446,8 @@ verbosity with `RUST_LOG`.
407
446
  default `https://sigit.si`), `SIGIT_CLOUD_URL`, `SIGIT_CONFIG_DIR` (default `~/.config/sigit`),
408
447
  `SIGIT_MODEL`, `SIGIT_MAX_TOOL_ROUNDS` (1 to 500, default 24; the tool-round cap of a
409
448
  headless run or an ACP prompt turn), `SIGIT_MCP` (`off` disables MCP), `SIGIT_CLIENT_FS` (`off` keeps the file tools on disk even when the
410
- ACP client offers `fs/read_text_file` / `fs/write_text_file`), `SIGIT_MCP_SMBCLOUD` (`off` drops the baked-in
449
+ ACP client offers `fs/read_text_file` / `fs/write_text_file`), `SIGIT_CLIENT_TERMINAL` (`off` runs
450
+ `run_command` locally even when the ACP client offers a terminal), `SIGIT_MCP_SMBCLOUD` (`off` drops the baked-in
411
451
  smbCloud CLI server), `SIGIT_MCP_OFFICIAL` (`off` drops the baked-in
412
452
  server), `SIGIT_PERMISSIONS` (`allow`/`ask`/`deny` — overrides the default permission mode for
413
453
  mutating tools; the escape hatch for clients without permission-request support),