sigit-code 1.6.2__tar.gz → 1.6.4__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.
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.agents/AGENTS.md +33 -7
- {sigit_code-1.6.2/.claude → sigit_code-1.6.4/.agents}/skills/agent-client-protocol/SKILL.md +1 -0
- {sigit_code-1.6.2/.agents → sigit_code-1.6.4/.claude}/skills/agent-client-protocol/SKILL.md +1 -0
- sigit_code-1.6.4/.sigit/code-cloud-agent/config.toml +6 -0
- sigit_code-1.6.4/.sigit/code-review/config.toml +3 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/AGENTS.md +33 -7
- {sigit_code-1.6.2 → sigit_code-1.6.4}/CHANGELOG.md +80 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/CLAUDE.md +33 -7
- {sigit_code-1.6.2 → sigit_code-1.6.4}/Cargo.lock +1 -1
- {sigit_code-1.6.2 → sigit_code-1.6.4}/Cargo.toml +1 -1
- {sigit_code-1.6.2 → sigit_code-1.6.4}/PKG-INFO +1 -1
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/backend.rs +64 -12
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/inline_tool_calls.rs +262 -1
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/main.rs +1026 -186
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/tools.rs +52 -0
- sigit_code-1.6.4/tests/acp_concurrent_sessions.rs +343 -0
- sigit_code-1.6.4/tests/acp_logout.rs +179 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/tests/acp_permissions.rs +138 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/tests/acp_session_load.rs +170 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.agents/skills/ai-assisted-coding/SKILL.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.agents/skills/branding/SKILL.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.agents/skills/run-sigit/SKILL.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.agents/skills/run-sigit/driver.mjs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.agents/skills/run-sigit/tui-smoke.sh +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.agents/skills/sigit-code-release/SKILL.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.agents/skills/tool-calling/SKILL.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.cargo/config.toml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.claude/skills/ai-assisted-coding/SKILL.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.claude/skills/branding/SKILL.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.claude/skills/run-sigit/SKILL.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.claude/skills/run-sigit/driver.mjs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.claude/skills/run-sigit/tui-smoke.sh +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.claude/skills/sigit-code-release/SKILL.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.claude/skills/tool-calling/SKILL.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.github/workflows/ci.yml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.github/workflows/release-aur.yml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.github/workflows/release-crates.yml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.github/workflows/release-github.yml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.github/workflows/release-homebrew.yml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.github/workflows/release-npm.yml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.github/workflows/release-nuget.yml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.github/workflows/release-pypi.yml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.github/workflows/release-scoop.yml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.github/workflows/release-winget.yml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.gitignore +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.nvmrc +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/.sigit/factory/config.toml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/LICENSE +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/README.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/docs/headless.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/docs/hooks.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/docs/mcp.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/examples/settings-with-hooks.toml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/examples/skills/README.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/examples/skills/commit-message/SKILL.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/npm/README.md.tmpl +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/npm/package-compat.json.tmpl +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/npm/package-main.json.tmpl +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/npm/package.json.tmpl +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/npm/scripts/render-main-package.cjs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/npm/scripts/render-platform-package.cjs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/npm/sigit/.gitignore +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/npm/sigit/README.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/npm/sigit/package.json +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/npm/sigit/src/index.ts +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/npm/sigit/tsconfig.json +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/nuget/.gitignore +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/nuget/sigit/Program.cs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/nuget/sigit/README.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/nuget/sigit/SiGit.Code.csproj +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/packaging/aur/PKGBUILD.in +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/packaging/nfpm.yaml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/packaging/windows/check-static-crt.ps1 +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/packaging/winget/getSigit.siGitCode.installer.yaml.in +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/packaging/winget/getSigit.siGitCode.locale.en-US.yaml.in +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/packaging/winget/getSigit.siGitCode.yaml.in +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/pypi/README.md +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/pypi/pyproject.toml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/pyproject.toml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/rust-toolchain.toml +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/account.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/browser_auth.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/chat.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/client_fs.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/commands.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/credentials.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/frontmatter.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/headless.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/hooks.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/instructions.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/mcp.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/models.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/permissions.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/provider.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/session_store.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/settings.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/setup.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/skills.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/subagents.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/src/workspace.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/tests/acp_client_fs.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/tests/acp_endpoint_errors.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/tests/acp_images.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/tests/acp_multi_root.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/tests/acp_session_list.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/tests/acp_stop_reasons.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/tests/acp_tool_stdin.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/tests/headless_mode.rs +0 -0
- {sigit_code-1.6.2 → sigit_code-1.6.4}/tests/version_flag.rs +0 -0
|
@@ -113,9 +113,20 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
113
113
|
- **`src/main.rs`** — entry point, mode dispatch, the full ACP `Agent` impl (session lifecycle:
|
|
114
114
|
new/load/fork/prompt/cancel, config options, slash-command advertisement), and the `SYSTEM_PROMPT`.
|
|
115
115
|
ACP session state owns its roots, conversation, and selected model even though the process has
|
|
116
|
-
one live backend; activating a thread parks and restores all
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
one working directory and one live backend slot; activating a thread parks and restores all
|
|
117
|
+
three. A thread on an HTTP backend keeps a backend of its own (`InferenceBackend::fresh`,
|
|
118
|
+
`SessionState::remote`), so its turn can wait on the endpoint while another thread is
|
|
119
|
+
installed. Unknown session ids are rejected instead of silently borrowing the active thread's
|
|
120
|
+
cwd. Prompt cancellation takes no lock, which lets a client cancel a turn whatever it holds.
|
|
121
|
+
`session/load` and `session/resume` share `restore_session`; the only difference is that
|
|
122
|
+
resume must not replay the history as `session/update`. `session/close` is the one place a
|
|
123
|
+
`SessionState` is dropped. It runs in two halves: `begin_close` signals the session's turn
|
|
124
|
+
from the dispatch loop (the turn holds the session's lock, so the signal cannot wait for it) and
|
|
125
|
+
marks the id in `closing_sessions`, which makes `handle_prompt` cancel a prompt that was
|
|
126
|
+
still queued; `handle_close_session` then runs under the session and workspace locks and
|
|
127
|
+
removes the state, the permission grants and the background commands
|
|
128
|
+
(`tools::kill_session_tasks`). It leaves `session_store` alone, so a closed thread still lists
|
|
129
|
+
and reopens. The
|
|
119
130
|
`SYSTEM_PROMPT` bakes in smbCloud-specific context the agent should use when the repo is clearly
|
|
120
131
|
smbCloud, and stay general otherwise.
|
|
121
132
|
- **`src/backend.rs`** — the `InferenceBackend` trait and neutral types (`ToolSpec`, `ToolCall`,
|
|
@@ -268,10 +279,21 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
268
279
|
announced, because it sets the announced status: a call that will ask starts `pending`, the
|
|
269
280
|
permission request carries that call's own id, and an `in_progress` update follows approval.
|
|
270
281
|
That update also puts the card's title back, since the permission request overwrites it
|
|
271
|
-
with the full arguments.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
282
|
+
with the full arguments. The Manual/Auto/Plan choice reaches an ACP client twice: as the
|
|
283
|
+
`sigit-permission-mode` config option, and as session `modes` with `session/set_mode`, which
|
|
284
|
+
the spec is retiring but some clients still render instead. Both are built from
|
|
285
|
+
`PERMISSION_MODES` in `main.rs` and read the same state, so whatever changes the mode has to
|
|
286
|
+
refresh both (`ConfigOptionUpdate` and `send_current_mode`). Note: ACP turn-affecting handlers run in `cx.spawn`ed tasks
|
|
287
|
+
so the dispatch loop can route the client's permission answer mid-turn — don't move them back
|
|
288
|
+
inline, and don't await client requests from inline handlers (deadlock). Two locks order them.
|
|
289
|
+
A per-session lock (`SiGitAgent::session_lock`) is held for a whole request, so requests on one
|
|
290
|
+
thread keep their order. `SiGitAgent::workspace_lock` guards what a session installs
|
|
291
|
+
process-wide (cwd, workspace roots, session MCP servers, the live backend slot, the engine's
|
|
292
|
+
conversation); lifecycle and config handlers hold it throughout, and `handle_prompt` holds it
|
|
293
|
+
through a `WorkspaceHold` that it releases while waiting on an HTTP endpoint or a permission
|
|
294
|
+
answer and retakes (reinstalling its session via `resume_workspace`) before any tool runs. An
|
|
295
|
+
on-device turn never releases it. Take the session lock first, never the other way round. Tool
|
|
296
|
+
execution, subagents included, still runs one session at a time.
|
|
275
297
|
- **`src/instructions.rs`** — project instruction files, the always-on counterpart to skills.
|
|
276
298
|
Reads `AGENTS.md` (the cross-tool [agents.md](https://agents.md) standard) and `CLAUDE.md`,
|
|
277
299
|
walking from the session cwd up to the repo root (nearest ancestor with `.git`, never above it),
|
|
@@ -337,6 +359,10 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
337
359
|
those read env vars once at init.
|
|
338
360
|
- **`src/account.rs`** — siGit Code Cloud auth (`/login`, `/logout`, `/whoami`); authenticates
|
|
339
361
|
against the account API and stores a session token. Performs no console I/O.
|
|
362
|
+
ACP `logout` (the editor's sign out button, shown only because `handle_initialize` advertises
|
|
363
|
+
`agentCapabilities.auth.logout`) and `/logout` both go through `SiGitAgent::sign_out` in
|
|
364
|
+
`main.rs`. The request names no session, so `leave_cloud` moves the parked threads off their
|
|
365
|
+
cloud tier along with the live one and each of them gets a fresh model picker.
|
|
340
366
|
- **`src/browser_auth.rs`** — browser sign-in, the path the editor's "Sign in to siGit Code"
|
|
341
367
|
button takes. sigit is a public OAuth client (client id `sigit-code-cli`, no secret, PKCE
|
|
342
368
|
S256) against the authorization server at `$SIGIT_API_URL/oauth`. Two ways the code gets
|
|
@@ -205,6 +205,7 @@ the connection and the agent anymore. Don't reintroduce the mpsc forwarder patte
|
|
|
205
205
|
| `ListSessionsRequest` | `handle_list_sessions` | the editor's "Import Threads" picker; gated by `SessionListCapabilities` |
|
|
206
206
|
| `PromptRequest` | `handle_prompt` | the turn: parse blocks → slash commands or tool-calling loop |
|
|
207
207
|
| `SetSessionConfigOptionRequest` | `handle_set_session_config_option` | the Zed model picker — switches/downloads models |
|
|
208
|
+
| `SetSessionModeRequest` | `handle_set_session_mode` | legacy mode selector; same Manual/Auto/Plan state as the Permissions config option |
|
|
208
209
|
| `CancelNotification` | `handle_cancel` | notification, no response |
|
|
209
210
|
|
|
210
211
|
Everything else is left to the SDK default (method not found).
|
|
@@ -205,6 +205,7 @@ the connection and the agent anymore. Don't reintroduce the mpsc forwarder patte
|
|
|
205
205
|
| `ListSessionsRequest` | `handle_list_sessions` | the editor's "Import Threads" picker; gated by `SessionListCapabilities` |
|
|
206
206
|
| `PromptRequest` | `handle_prompt` | the turn: parse blocks → slash commands or tool-calling loop |
|
|
207
207
|
| `SetSessionConfigOptionRequest` | `handle_set_session_config_option` | the Zed model picker — switches/downloads models |
|
|
208
|
+
| `SetSessionModeRequest` | `handle_set_session_mode` | legacy mode selector; same Manual/Auto/Plan state as the Permissions config option |
|
|
208
209
|
| `CancelNotification` | `handle_cancel` | notification, no response |
|
|
209
210
|
|
|
210
211
|
Everything else is left to the SDK default (method not found).
|
|
@@ -113,9 +113,20 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
113
113
|
- **`src/main.rs`** — entry point, mode dispatch, the full ACP `Agent` impl (session lifecycle:
|
|
114
114
|
new/load/fork/prompt/cancel, config options, slash-command advertisement), and the `SYSTEM_PROMPT`.
|
|
115
115
|
ACP session state owns its roots, conversation, and selected model even though the process has
|
|
116
|
-
one live backend; activating a thread parks and restores all
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
one working directory and one live backend slot; activating a thread parks and restores all
|
|
117
|
+
three. A thread on an HTTP backend keeps a backend of its own (`InferenceBackend::fresh`,
|
|
118
|
+
`SessionState::remote`), so its turn can wait on the endpoint while another thread is
|
|
119
|
+
installed. Unknown session ids are rejected instead of silently borrowing the active thread's
|
|
120
|
+
cwd. Prompt cancellation takes no lock, which lets a client cancel a turn whatever it holds.
|
|
121
|
+
`session/load` and `session/resume` share `restore_session`; the only difference is that
|
|
122
|
+
resume must not replay the history as `session/update`. `session/close` is the one place a
|
|
123
|
+
`SessionState` is dropped. It runs in two halves: `begin_close` signals the session's turn
|
|
124
|
+
from the dispatch loop (the turn holds the session's lock, so the signal cannot wait for it) and
|
|
125
|
+
marks the id in `closing_sessions`, which makes `handle_prompt` cancel a prompt that was
|
|
126
|
+
still queued; `handle_close_session` then runs under the session and workspace locks and
|
|
127
|
+
removes the state, the permission grants and the background commands
|
|
128
|
+
(`tools::kill_session_tasks`). It leaves `session_store` alone, so a closed thread still lists
|
|
129
|
+
and reopens. The
|
|
119
130
|
`SYSTEM_PROMPT` bakes in smbCloud-specific context the agent should use when the repo is clearly
|
|
120
131
|
smbCloud, and stay general otherwise.
|
|
121
132
|
- **`src/backend.rs`** — the `InferenceBackend` trait and neutral types (`ToolSpec`, `ToolCall`,
|
|
@@ -268,10 +279,21 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
268
279
|
announced, because it sets the announced status: a call that will ask starts `pending`, the
|
|
269
280
|
permission request carries that call's own id, and an `in_progress` update follows approval.
|
|
270
281
|
That update also puts the card's title back, since the permission request overwrites it
|
|
271
|
-
with the full arguments.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
282
|
+
with the full arguments. The Manual/Auto/Plan choice reaches an ACP client twice: as the
|
|
283
|
+
`sigit-permission-mode` config option, and as session `modes` with `session/set_mode`, which
|
|
284
|
+
the spec is retiring but some clients still render instead. Both are built from
|
|
285
|
+
`PERMISSION_MODES` in `main.rs` and read the same state, so whatever changes the mode has to
|
|
286
|
+
refresh both (`ConfigOptionUpdate` and `send_current_mode`). Note: ACP turn-affecting handlers run in `cx.spawn`ed tasks
|
|
287
|
+
so the dispatch loop can route the client's permission answer mid-turn — don't move them back
|
|
288
|
+
inline, and don't await client requests from inline handlers (deadlock). Two locks order them.
|
|
289
|
+
A per-session lock (`SiGitAgent::session_lock`) is held for a whole request, so requests on one
|
|
290
|
+
thread keep their order. `SiGitAgent::workspace_lock` guards what a session installs
|
|
291
|
+
process-wide (cwd, workspace roots, session MCP servers, the live backend slot, the engine's
|
|
292
|
+
conversation); lifecycle and config handlers hold it throughout, and `handle_prompt` holds it
|
|
293
|
+
through a `WorkspaceHold` that it releases while waiting on an HTTP endpoint or a permission
|
|
294
|
+
answer and retakes (reinstalling its session via `resume_workspace`) before any tool runs. An
|
|
295
|
+
on-device turn never releases it. Take the session lock first, never the other way round. Tool
|
|
296
|
+
execution, subagents included, still runs one session at a time.
|
|
275
297
|
- **`src/instructions.rs`** — project instruction files, the always-on counterpart to skills.
|
|
276
298
|
Reads `AGENTS.md` (the cross-tool [agents.md](https://agents.md) standard) and `CLAUDE.md`,
|
|
277
299
|
walking from the session cwd up to the repo root (nearest ancestor with `.git`, never above it),
|
|
@@ -337,6 +359,10 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
337
359
|
those read env vars once at init.
|
|
338
360
|
- **`src/account.rs`** — siGit Code Cloud auth (`/login`, `/logout`, `/whoami`); authenticates
|
|
339
361
|
against the account API and stores a session token. Performs no console I/O.
|
|
362
|
+
ACP `logout` (the editor's sign out button, shown only because `handle_initialize` advertises
|
|
363
|
+
`agentCapabilities.auth.logout`) and `/logout` both go through `SiGitAgent::sign_out` in
|
|
364
|
+
`main.rs`. The request names no session, so `leave_cloud` moves the parked threads off their
|
|
365
|
+
cloud tier along with the live one and each of them gets a fresh model picker.
|
|
340
366
|
- **`src/browser_auth.rs`** — browser sign-in, the path the editor's "Sign in to siGit Code"
|
|
341
367
|
button takes. sigit is a public OAuth client (client id `sigit-code-cli`, no secret, PKCE
|
|
342
368
|
S256) against the authorization server at `$SIGIT_API_URL/oauth`. Two ways the code gets
|
|
@@ -1,5 +1,85 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.6.4
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **DeepSeek DSML tool calls written into the reply text are recovered.** A
|
|
8
|
+
DeepSeek-family endpoint can render a tool call as literal
|
|
9
|
+
`<|DSML|tool_calls>` text instead of the structured `tool_calls` field,
|
|
10
|
+
which streamed the tag into the editor and ended the turn with nothing run.
|
|
11
|
+
The inline tool-call scanner that already recovered the GLM XML and Kimi K3
|
|
12
|
+
shapes now recovers the DSML shape too, including a marker split across
|
|
13
|
+
stream chunks. A block that fails to parse is still kept out of the reply
|
|
14
|
+
and the history, and the model is told the call did not run (#181).
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **The round-cap stop message no longer carries a stray run of spaces.**
|
|
19
|
+
When the tool-round cap ended a turn, the closing message could collapse a
|
|
20
|
+
space run into the wrong spot, which the editor then rendered verbatim
|
|
21
|
+
(#177).
|
|
22
|
+
|
|
23
|
+
## 1.6.3
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **A client can resume a session and close one.** siGit Code now advertises
|
|
28
|
+
`sessionCapabilities.resume` and `sessionCapabilities.close`.
|
|
29
|
+
`session/resume` restores a saved thread the way `session/load` does, but
|
|
30
|
+
sends none of it back: it is for a client that still has the thread on
|
|
31
|
+
screen, after an agent restart for example. `session/close` cancels the
|
|
32
|
+
session's running turn and drops what the process held for it, which is its
|
|
33
|
+
state, its permission grants and plan mode, the MCP servers the client named
|
|
34
|
+
for it, and the background commands it started. The saved history is left
|
|
35
|
+
alone, so a closed thread still lists and reopens (#149).
|
|
36
|
+
|
|
37
|
+
- **The permission modes are also offered as ACP session modes.** Manual, Auto
|
|
38
|
+
and Plan were only reachable as a config option, which is what the protocol
|
|
39
|
+
now recommends and what Zed reads. A client that draws the older mode
|
|
40
|
+
selector saw nothing. `session/new`, `session/load` and `session/fork` now
|
|
41
|
+
return the same three in `modes`, `session/set_mode` switches between them,
|
|
42
|
+
and a change made any other way (the config option, `/plan`, `/clear`,
|
|
43
|
+
`/reload`) is announced with `current_mode_update`. A client that reads
|
|
44
|
+
config options ignores `modes`, so nothing changes there (#148).
|
|
45
|
+
|
|
46
|
+
- **An editor can sign out of siGit Code.** The `initialize` reply carried an
|
|
47
|
+
empty `auth` object, so a client drew no sign out button, and typing
|
|
48
|
+
`/logout` was the only way out of an account. The reply now advertises
|
|
49
|
+
`agentCapabilities.auth.logout`, and a `logout` request ends the account
|
|
50
|
+
session through the method `/logout` calls. The request names no session,
|
|
51
|
+
so it moves every open thread that used the account, not only the live one:
|
|
52
|
+
a thread parked on a cloud tier goes on-device with the live thread instead
|
|
53
|
+
of failing to restore its tier on its next prompt, and every thread that
|
|
54
|
+
moved gets a fresh model picker. Local inference is switched back on if it
|
|
55
|
+
was off, so a thread opened after signing out asks for no cloud tier
|
|
56
|
+
(#150).
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- **Two threads can run a turn at the same time.** An editor runs one siGit
|
|
61
|
+
Code process for every thread it has open, and a single lock ordered every
|
|
62
|
+
turn-affecting request in it, so a second prompt waited for the first
|
|
63
|
+
thread's whole turn and two prompts never overlapped. The lock held what a
|
|
64
|
+
session installs process-wide — the working directory, the workspace roots,
|
|
65
|
+
the MCP servers the client named for it, the live backend — none of which a
|
|
66
|
+
turn touches while it waits on an HTTP endpoint, which is most of its time.
|
|
67
|
+
A session on an HTTP backend now keeps a backend of its own, so its
|
|
68
|
+
conversation is no longer swapped in and out of a shared one, and a turn
|
|
69
|
+
releases the process-wide lock while it waits on the endpoint or on a
|
|
70
|
+
permission answer and takes it back, reinstalling its session, before any
|
|
71
|
+
tool runs. On-device turns are unchanged, since the engine keeps one
|
|
72
|
+
conversation, and tool execution still runs one session at a time (#176).
|
|
73
|
+
|
|
74
|
+
### Fixed
|
|
75
|
+
|
|
76
|
+
- **A prompt whose inference failed is kept through a model switch.** A turn
|
|
77
|
+
that ended in an endpoint error stayed in the client's thread, but the
|
|
78
|
+
carryover into a new model dropped its trailing user message, and the error
|
|
79
|
+
path never saved the session. Only a cancelled prompt should be taken back
|
|
80
|
+
out, so cancellation now removes it from the live history, and the error
|
|
81
|
+
path persists the session (#124).
|
|
82
|
+
|
|
3
83
|
## 1.6.2
|
|
4
84
|
|
|
5
85
|
### Added
|
|
@@ -113,9 +113,20 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
113
113
|
- **`src/main.rs`** — entry point, mode dispatch, the full ACP `Agent` impl (session lifecycle:
|
|
114
114
|
new/load/fork/prompt/cancel, config options, slash-command advertisement), and the `SYSTEM_PROMPT`.
|
|
115
115
|
ACP session state owns its roots, conversation, and selected model even though the process has
|
|
116
|
-
one live backend; activating a thread parks and restores all
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
one working directory and one live backend slot; activating a thread parks and restores all
|
|
117
|
+
three. A thread on an HTTP backend keeps a backend of its own (`InferenceBackend::fresh`,
|
|
118
|
+
`SessionState::remote`), so its turn can wait on the endpoint while another thread is
|
|
119
|
+
installed. Unknown session ids are rejected instead of silently borrowing the active thread's
|
|
120
|
+
cwd. Prompt cancellation takes no lock, which lets a client cancel a turn whatever it holds.
|
|
121
|
+
`session/load` and `session/resume` share `restore_session`; the only difference is that
|
|
122
|
+
resume must not replay the history as `session/update`. `session/close` is the one place a
|
|
123
|
+
`SessionState` is dropped. It runs in two halves: `begin_close` signals the session's turn
|
|
124
|
+
from the dispatch loop (the turn holds the session's lock, so the signal cannot wait for it) and
|
|
125
|
+
marks the id in `closing_sessions`, which makes `handle_prompt` cancel a prompt that was
|
|
126
|
+
still queued; `handle_close_session` then runs under the session and workspace locks and
|
|
127
|
+
removes the state, the permission grants and the background commands
|
|
128
|
+
(`tools::kill_session_tasks`). It leaves `session_store` alone, so a closed thread still lists
|
|
129
|
+
and reopens. The
|
|
119
130
|
`SYSTEM_PROMPT` bakes in smbCloud-specific context the agent should use when the repo is clearly
|
|
120
131
|
smbCloud, and stay general otherwise.
|
|
121
132
|
- **`src/backend.rs`** — the `InferenceBackend` trait and neutral types (`ToolSpec`, `ToolCall`,
|
|
@@ -268,10 +279,21 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
268
279
|
announced, because it sets the announced status: a call that will ask starts `pending`, the
|
|
269
280
|
permission request carries that call's own id, and an `in_progress` update follows approval.
|
|
270
281
|
That update also puts the card's title back, since the permission request overwrites it
|
|
271
|
-
with the full arguments.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
282
|
+
with the full arguments. The Manual/Auto/Plan choice reaches an ACP client twice: as the
|
|
283
|
+
`sigit-permission-mode` config option, and as session `modes` with `session/set_mode`, which
|
|
284
|
+
the spec is retiring but some clients still render instead. Both are built from
|
|
285
|
+
`PERMISSION_MODES` in `main.rs` and read the same state, so whatever changes the mode has to
|
|
286
|
+
refresh both (`ConfigOptionUpdate` and `send_current_mode`). Note: ACP turn-affecting handlers run in `cx.spawn`ed tasks
|
|
287
|
+
so the dispatch loop can route the client's permission answer mid-turn — don't move them back
|
|
288
|
+
inline, and don't await client requests from inline handlers (deadlock). Two locks order them.
|
|
289
|
+
A per-session lock (`SiGitAgent::session_lock`) is held for a whole request, so requests on one
|
|
290
|
+
thread keep their order. `SiGitAgent::workspace_lock` guards what a session installs
|
|
291
|
+
process-wide (cwd, workspace roots, session MCP servers, the live backend slot, the engine's
|
|
292
|
+
conversation); lifecycle and config handlers hold it throughout, and `handle_prompt` holds it
|
|
293
|
+
through a `WorkspaceHold` that it releases while waiting on an HTTP endpoint or a permission
|
|
294
|
+
answer and retakes (reinstalling its session via `resume_workspace`) before any tool runs. An
|
|
295
|
+
on-device turn never releases it. Take the session lock first, never the other way round. Tool
|
|
296
|
+
execution, subagents included, still runs one session at a time.
|
|
275
297
|
- **`src/instructions.rs`** — project instruction files, the always-on counterpart to skills.
|
|
276
298
|
Reads `AGENTS.md` (the cross-tool [agents.md](https://agents.md) standard) and `CLAUDE.md`,
|
|
277
299
|
walking from the session cwd up to the repo root (nearest ancestor with `.git`, never above it),
|
|
@@ -337,6 +359,10 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
337
359
|
those read env vars once at init.
|
|
338
360
|
- **`src/account.rs`** — siGit Code Cloud auth (`/login`, `/logout`, `/whoami`); authenticates
|
|
339
361
|
against the account API and stores a session token. Performs no console I/O.
|
|
362
|
+
ACP `logout` (the editor's sign out button, shown only because `handle_initialize` advertises
|
|
363
|
+
`agentCapabilities.auth.logout`) and `/logout` both go through `SiGitAgent::sign_out` in
|
|
364
|
+
`main.rs`. The request names no session, so `leave_cloud` moves the parked threads off their
|
|
365
|
+
cloud tier along with the live one and each of them gets a fresh model picker.
|
|
340
366
|
- **`src/browser_auth.rs`** — browser sign-in, the path the editor's "Sign in to siGit Code"
|
|
341
367
|
button takes. sigit is a public OAuth client (client id `sigit-code-cli`, no secret, PKCE
|
|
342
368
|
S256) against the authorization server at `$SIGIT_API_URL/oauth`. Two ways the code gets
|
|
@@ -445,6 +445,15 @@ pub trait InferenceBackend: Send + Sync {
|
|
|
445
445
|
/// local model while requests actually go to the cloud.
|
|
446
446
|
fn is_remote(&self) -> bool;
|
|
447
447
|
|
|
448
|
+
/// A backend for the same endpoint and model with an empty conversation,
|
|
449
|
+
/// or `None` when the conversation cannot be separated from the backend
|
|
450
|
+
/// (on-device, the engine holds it). An editor keeps several threads open
|
|
451
|
+
/// in one process; this is what lets each of them own its history instead
|
|
452
|
+
/// of taking turns on a shared one.
|
|
453
|
+
fn fresh(&self) -> Option<Arc<dyn InferenceBackend>> {
|
|
454
|
+
None
|
|
455
|
+
}
|
|
456
|
+
|
|
448
457
|
/// A serializable snapshot of the conversation history, one JSON object per
|
|
449
458
|
/// message (`{"role": ..., "content": ...}` at minimum). The snapshot is
|
|
450
459
|
/// what the session store persists; it includes any seeded system message
|
|
@@ -1554,6 +1563,19 @@ impl InferenceBackend for OpenAiBackend {
|
|
|
1554
1563
|
true
|
|
1555
1564
|
}
|
|
1556
1565
|
|
|
1566
|
+
fn fresh(&self) -> Option<Arc<dyn InferenceBackend>> {
|
|
1567
|
+
Some(Arc::new(Self {
|
|
1568
|
+
base_url: self.base_url.clone(),
|
|
1569
|
+
api_key: self.api_key.clone(),
|
|
1570
|
+
model: self.model.clone(),
|
|
1571
|
+
// The client is a handle on one connection pool; sharing it keeps
|
|
1572
|
+
// connections to the endpoint warm across sessions.
|
|
1573
|
+
http: self.http.clone(),
|
|
1574
|
+
history: Mutex::new(Vec::new()),
|
|
1575
|
+
accepts_images: self.accepts_images,
|
|
1576
|
+
}))
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1557
1579
|
async fn history_snapshot(&self) -> Vec<serde_json::Value> {
|
|
1558
1580
|
self.history.lock().await.clone()
|
|
1559
1581
|
}
|
|
@@ -1944,18 +1966,25 @@ pub fn carryover_history(snapshot: Vec<serde_json::Value>) -> Vec<serde_json::Va
|
|
|
1944
1966
|
}
|
|
1945
1967
|
}
|
|
1946
1968
|
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1969
|
+
carried
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
/// Take a cancelled prompt's user message back out of the live history.
|
|
1973
|
+
///
|
|
1974
|
+
/// The client drops a cancelled prompt from its thread, so the model must not
|
|
1975
|
+
/// keep answering it. Only the first inference round can cancel with the user
|
|
1976
|
+
/// message as the last entry; later rounds end on tool or assistant output.
|
|
1977
|
+
/// A prompt that fails with an error is not taken back: the client keeps it,
|
|
1978
|
+
/// so the history keeps it too and a model switch carries it over.
|
|
1979
|
+
pub async fn forget_trailing_user_message(backend: &dyn InferenceBackend) {
|
|
1980
|
+
let mut history = backend.history_snapshot().await;
|
|
1981
|
+
if history
|
|
1952
1982
|
.last()
|
|
1953
1983
|
.is_some_and(|message| message["role"] == "user")
|
|
1954
1984
|
{
|
|
1955
|
-
|
|
1985
|
+
history.pop();
|
|
1986
|
+
backend.restore_history(history).await;
|
|
1956
1987
|
}
|
|
1957
|
-
|
|
1958
|
-
carried
|
|
1959
1988
|
}
|
|
1960
1989
|
|
|
1961
1990
|
/// Replay `carried` (from [`carryover_history`]) into `backend`, on top of the
|
|
@@ -2161,16 +2190,39 @@ mod tests {
|
|
|
2161
2190
|
}
|
|
2162
2191
|
|
|
2163
2192
|
#[test]
|
|
2164
|
-
fn
|
|
2193
|
+
fn carryover_keeps_a_trailing_user_message_from_a_failed_turn() {
|
|
2194
|
+
// The client keeps a prompt whose inference errored, so a switch must
|
|
2195
|
+
// not drop it (issue #124).
|
|
2165
2196
|
let carried = carryover_history(vec![
|
|
2166
2197
|
serde_json::json!({ "role": "system", "content": "prompt" }),
|
|
2167
2198
|
serde_json::json!({ "role": "user", "content": "completed question" }),
|
|
2168
2199
|
serde_json::json!({ "role": "assistant", "content": "completed answer" }),
|
|
2169
|
-
serde_json::json!({ "role": "user", "content": "
|
|
2200
|
+
serde_json::json!({ "role": "user", "content": "failed question" }),
|
|
2170
2201
|
]);
|
|
2171
2202
|
|
|
2172
|
-
assert_eq!(carried.len(),
|
|
2173
|
-
assert_eq!(carried.last().unwrap()["content"], "
|
|
2203
|
+
assert_eq!(carried.len(), 3, "{carried:#?}");
|
|
2204
|
+
assert_eq!(carried.last().unwrap()["content"], "failed question");
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
#[tokio::test]
|
|
2208
|
+
async fn forget_trailing_user_message_drops_only_a_cancelled_prompt() {
|
|
2209
|
+
let backend = OpenAiBackend::new("http://localhost", "", "m", None);
|
|
2210
|
+
backend
|
|
2211
|
+
.restore_history(vec![
|
|
2212
|
+
serde_json::json!({ "role": "user", "content": "completed question" }),
|
|
2213
|
+
serde_json::json!({ "role": "assistant", "content": "completed answer" }),
|
|
2214
|
+
serde_json::json!({ "role": "user", "content": "cancelled question" }),
|
|
2215
|
+
])
|
|
2216
|
+
.await;
|
|
2217
|
+
|
|
2218
|
+
forget_trailing_user_message(&backend).await;
|
|
2219
|
+
|
|
2220
|
+
let history = backend.history_snapshot().await;
|
|
2221
|
+
assert_eq!(history.len(), 2, "{history:#?}");
|
|
2222
|
+
assert_eq!(history[1]["content"], "completed answer");
|
|
2223
|
+
|
|
2224
|
+
forget_trailing_user_message(&backend).await;
|
|
2225
|
+
assert_eq!(backend.history_snapshot().await.len(), 2);
|
|
2174
2226
|
}
|
|
2175
2227
|
|
|
2176
2228
|
#[test]
|