sigit-code 1.6.3__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.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.3 → sigit_code-1.6.4}/CHANGELOG.md +20 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/Cargo.lock +1 -1
- {sigit_code-1.6.3 → sigit_code-1.6.4}/Cargo.toml +1 -1
- {sigit_code-1.6.3 → sigit_code-1.6.4}/PKG-INFO +1 -1
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/inline_tool_calls.rs +262 -1
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/main.rs +2 -1
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.agents/AGENTS.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.agents/skills/agent-client-protocol/SKILL.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.agents/skills/ai-assisted-coding/SKILL.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.agents/skills/branding/SKILL.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.agents/skills/run-sigit/SKILL.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.agents/skills/run-sigit/driver.mjs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.agents/skills/run-sigit/tui-smoke.sh +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.agents/skills/sigit-code-release/SKILL.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.agents/skills/tool-calling/SKILL.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.cargo/config.toml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.claude/skills/agent-client-protocol/SKILL.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.claude/skills/ai-assisted-coding/SKILL.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.claude/skills/branding/SKILL.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.claude/skills/run-sigit/SKILL.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.claude/skills/run-sigit/driver.mjs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.claude/skills/run-sigit/tui-smoke.sh +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.claude/skills/sigit-code-release/SKILL.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.claude/skills/tool-calling/SKILL.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.github/workflows/ci.yml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.github/workflows/release-aur.yml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.github/workflows/release-crates.yml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.github/workflows/release-github.yml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.github/workflows/release-homebrew.yml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.github/workflows/release-npm.yml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.github/workflows/release-nuget.yml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.github/workflows/release-pypi.yml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.github/workflows/release-scoop.yml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.github/workflows/release-winget.yml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.gitignore +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.nvmrc +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/.sigit/factory/config.toml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/AGENTS.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/CLAUDE.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/LICENSE +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/README.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/docs/headless.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/docs/hooks.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/docs/mcp.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/examples/settings-with-hooks.toml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/examples/skills/README.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/examples/skills/commit-message/SKILL.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/npm/README.md.tmpl +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/npm/package-compat.json.tmpl +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/npm/package-main.json.tmpl +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/npm/package.json.tmpl +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/npm/scripts/render-main-package.cjs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/npm/scripts/render-platform-package.cjs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/npm/sigit/.gitignore +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/npm/sigit/README.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/npm/sigit/package.json +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/npm/sigit/src/index.ts +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/npm/sigit/tsconfig.json +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/nuget/.gitignore +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/nuget/sigit/Program.cs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/nuget/sigit/README.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/nuget/sigit/SiGit.Code.csproj +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/packaging/aur/PKGBUILD.in +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/packaging/nfpm.yaml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/packaging/windows/check-static-crt.ps1 +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/packaging/winget/getSigit.siGitCode.installer.yaml.in +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/packaging/winget/getSigit.siGitCode.locale.en-US.yaml.in +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/packaging/winget/getSigit.siGitCode.yaml.in +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/pypi/README.md +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/pypi/pyproject.toml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/pyproject.toml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/rust-toolchain.toml +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/account.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/backend.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/browser_auth.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/chat.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/client_fs.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/commands.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/credentials.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/frontmatter.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/headless.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/hooks.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/instructions.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/mcp.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/models.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/permissions.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/provider.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/session_store.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/settings.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/setup.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/skills.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/subagents.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/tools.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/src/workspace.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/tests/acp_client_fs.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/tests/acp_concurrent_sessions.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/tests/acp_endpoint_errors.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/tests/acp_images.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/tests/acp_logout.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/tests/acp_multi_root.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/tests/acp_permissions.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/tests/acp_session_list.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/tests/acp_session_load.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/tests/acp_stop_reasons.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/tests/acp_tool_stdin.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/tests/headless_mode.rs +0 -0
- {sigit_code-1.6.3 → sigit_code-1.6.4}/tests/version_flag.rs +0 -0
|
@@ -1,5 +1,25 @@
|
|
|
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
|
+
|
|
3
23
|
## 1.6.3
|
|
4
24
|
|
|
5
25
|
### Added
|
|
@@ -53,7 +53,20 @@ const K3_THINK_OPEN: &str = "<|open|>think<|sep|>";
|
|
|
53
53
|
const K3_THINK_CLOSE: &str = "<|close|>think<|sep|>";
|
|
54
54
|
const K3_SEP: &str = "<|sep|>";
|
|
55
55
|
|
|
56
|
-
const
|
|
56
|
+
const DSML_TOOL_CALLS_OPEN: &str = "<|DSML|tool_calls>";
|
|
57
|
+
const DSML_TOOL_CALLS_CLOSE: &str = "<|DSML|/tool_calls>";
|
|
58
|
+
const DSML_INVOKE_OPEN: &str = "<|DSML|invoke";
|
|
59
|
+
const DSML_INVOKE_CLOSE: &str = "<|DSML|/invoke>";
|
|
60
|
+
const DSML_PARAMETER_OPEN: &str = "<|DSML|parameter";
|
|
61
|
+
const DSML_PARAMETER_CLOSE: &str = "<|DSML|/parameter>";
|
|
62
|
+
|
|
63
|
+
const START_MARKERS: &[&str] = &[
|
|
64
|
+
XML_OPEN_TAG,
|
|
65
|
+
K3_TOOLS_OPEN,
|
|
66
|
+
K3_RESPONSE_OPEN,
|
|
67
|
+
K3_THINK_OPEN,
|
|
68
|
+
DSML_TOOL_CALLS_OPEN,
|
|
69
|
+
];
|
|
57
70
|
|
|
58
71
|
/// One tool call recovered from inline text.
|
|
59
72
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
@@ -275,6 +288,78 @@ fn decode_k3_attribute(value: &str) -> String {
|
|
|
275
288
|
value.replace(""", "\"").replace("&", "&")
|
|
276
289
|
}
|
|
277
290
|
|
|
291
|
+
/// Parse a DeepSeek DSML `<|DSML|tool_calls>` envelope's inner text: zero or
|
|
292
|
+
/// more `<|DSML|invoke name="$TOOL">...</|DSML|invoke>` blocks, each
|
|
293
|
+
/// holding `<|DSML|parameter name="$KEY" string="true|false">...` entries.
|
|
294
|
+
fn parse_dsml_block(inner: &str, tools: &[ToolSpec]) -> Option<Vec<Recovered>> {
|
|
295
|
+
let mut calls = Vec::new();
|
|
296
|
+
let mut rest = inner;
|
|
297
|
+
|
|
298
|
+
while !rest.trim().is_empty() {
|
|
299
|
+
rest = rest.trim_start();
|
|
300
|
+
let header = rest.strip_prefix(DSML_INVOKE_OPEN)?;
|
|
301
|
+
let (attr_text, after_header) = split_once(header, ">")?;
|
|
302
|
+
let attrs = parse_k3_attributes(attr_text)?;
|
|
303
|
+
let name = attrs.get("name")?;
|
|
304
|
+
if name.is_empty() || !offered_tool(tools, name) {
|
|
305
|
+
return None;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
let (call_inner, after_call) = split_once(after_header, DSML_INVOKE_CLOSE)?;
|
|
309
|
+
let arguments = parse_dsml_parameters(call_inner, tools, name)?;
|
|
310
|
+
calls.push(Recovered {
|
|
311
|
+
name: name.to_string(),
|
|
312
|
+
arguments: serde_json::Value::Object(arguments).to_string(),
|
|
313
|
+
});
|
|
314
|
+
rest = after_call;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
Some(calls)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
fn parse_dsml_parameters(
|
|
321
|
+
mut rest: &str,
|
|
322
|
+
tools: &[ToolSpec],
|
|
323
|
+
tool_name: &str,
|
|
324
|
+
) -> Option<serde_json::Map<String, serde_json::Value>> {
|
|
325
|
+
let mut args = serde_json::Map::new();
|
|
326
|
+
|
|
327
|
+
while !rest.trim().is_empty() {
|
|
328
|
+
rest = rest.trim_start();
|
|
329
|
+
let header = rest.strip_prefix(DSML_PARAMETER_OPEN)?;
|
|
330
|
+
let (attr_text, after_header) = split_once(header, ">")?;
|
|
331
|
+
let attrs = parse_k3_attributes(attr_text)?;
|
|
332
|
+
let key = attrs.get("name")?.trim();
|
|
333
|
+
if key.is_empty() {
|
|
334
|
+
return None;
|
|
335
|
+
}
|
|
336
|
+
// DSML marks each parameter's encoding with `string="true|false"`. If
|
|
337
|
+
// an endpoint omits it, fall back to the schema-based typing the
|
|
338
|
+
// other recovery paths use instead of mistyping the value.
|
|
339
|
+
let is_string = match attrs.get("string").map(String::as_str) {
|
|
340
|
+
Some("true") => true,
|
|
341
|
+
Some("false") => false,
|
|
342
|
+
_ => {
|
|
343
|
+
declared_type(tools, tool_name, key)
|
|
344
|
+
.as_deref()
|
|
345
|
+
.unwrap_or("string")
|
|
346
|
+
== "string"
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
let (raw_value, after_param) = split_once(after_header, DSML_PARAMETER_CLOSE)?;
|
|
351
|
+
let value = if is_string {
|
|
352
|
+
serde_json::Value::String(raw_value.to_string())
|
|
353
|
+
} else {
|
|
354
|
+
serde_json::from_str(raw_value).ok()?
|
|
355
|
+
};
|
|
356
|
+
args.insert(key.to_string(), value);
|
|
357
|
+
rest = after_param;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
Some(args)
|
|
361
|
+
}
|
|
362
|
+
|
|
278
363
|
fn split_once<'a>(s: &'a str, delim: &str) -> Option<(&'a str, &'a str)> {
|
|
279
364
|
let idx = s.find(delim)?;
|
|
280
365
|
Some((&s[..idx], &s[idx + delim.len()..]))
|
|
@@ -382,6 +467,9 @@ impl<'a> StreamScanner<'a> {
|
|
|
382
467
|
|| rest
|
|
383
468
|
.strip_prefix(XML_OPEN_TAG)
|
|
384
469
|
.is_some_and(looks_like_xml_call)
|
|
470
|
+
|| rest
|
|
471
|
+
.strip_prefix(DSML_TOOL_CALLS_OPEN)
|
|
472
|
+
.is_some_and(looks_like_dsml_call)
|
|
385
473
|
{
|
|
386
474
|
ScanEvent::Malformed(rest)
|
|
387
475
|
} else {
|
|
@@ -421,6 +509,10 @@ impl<'a> StreamScanner<'a> {
|
|
|
421
509
|
Some(mut recovered) => events.append(&mut recovered),
|
|
422
510
|
None => break,
|
|
423
511
|
},
|
|
512
|
+
DSML_TOOL_CALLS_OPEN => match self.drain_dsml() {
|
|
513
|
+
Some(mut recovered) => events.append(&mut recovered),
|
|
514
|
+
None => break,
|
|
515
|
+
},
|
|
424
516
|
K3_RESPONSE_OPEN => {
|
|
425
517
|
let Some(mut unwrapped) =
|
|
426
518
|
self.drain_wrapped_text(K3_RESPONSE_OPEN, K3_RESPONSE_CLOSE, true)
|
|
@@ -474,6 +566,22 @@ impl<'a> StreamScanner<'a> {
|
|
|
474
566
|
})
|
|
475
567
|
}
|
|
476
568
|
|
|
569
|
+
fn drain_dsml(&mut self) -> Option<Vec<ScanEvent>> {
|
|
570
|
+
let close_rel = self.pending[DSML_TOOL_CALLS_OPEN.len()..].find(DSML_TOOL_CALLS_CLOSE)?;
|
|
571
|
+
let close_idx = DSML_TOOL_CALLS_OPEN.len() + close_rel;
|
|
572
|
+
let block: String = self
|
|
573
|
+
.pending
|
|
574
|
+
.drain(..close_idx + DSML_TOOL_CALLS_CLOSE.len())
|
|
575
|
+
.collect();
|
|
576
|
+
let inner = &block[DSML_TOOL_CALLS_OPEN.len()..block.len() - DSML_TOOL_CALLS_CLOSE.len()];
|
|
577
|
+
|
|
578
|
+
Some(match parse_dsml_block(inner, self.tools) {
|
|
579
|
+
Some(calls) => calls.into_iter().map(ScanEvent::ToolCall).collect(),
|
|
580
|
+
None if looks_like_dsml_call(inner) => vec![ScanEvent::Malformed(block)],
|
|
581
|
+
None => vec![ScanEvent::Text(block)],
|
|
582
|
+
})
|
|
583
|
+
}
|
|
584
|
+
|
|
477
585
|
fn drain_wrapped_text(
|
|
478
586
|
&mut self,
|
|
479
587
|
open: &str,
|
|
@@ -535,6 +643,14 @@ fn looks_like_k3_call(after_marker: &str) -> bool {
|
|
|
535
643
|
rest.is_empty() || rest.starts_with(K3_CALL_OPEN)
|
|
536
644
|
}
|
|
537
645
|
|
|
646
|
+
/// Whether the text after a DeepSeek `<|DSML|tool_calls>` marker reads as
|
|
647
|
+
/// the start of a call: a `<|DSML|invoke` marker after any whitespace, or
|
|
648
|
+
/// nothing at all. A quoted marker, like the constant in this file, fails.
|
|
649
|
+
fn looks_like_dsml_call(after_marker: &str) -> bool {
|
|
650
|
+
let rest = after_marker.trim_start();
|
|
651
|
+
rest.is_empty() || rest.starts_with(DSML_INVOKE_OPEN)
|
|
652
|
+
}
|
|
653
|
+
|
|
538
654
|
fn scan_complete_text(text: &str, tools: &[ToolSpec]) -> Vec<ScanEvent> {
|
|
539
655
|
let mut scanner = StreamScanner::new(tools);
|
|
540
656
|
let mut events = scanner.push(text);
|
|
@@ -1231,4 +1347,149 @@ mod tests {
|
|
|
1231
1347
|
assert_eq!(calls.len(), 1);
|
|
1232
1348
|
assert_eq!(calls[0].name, "run_command");
|
|
1233
1349
|
}
|
|
1350
|
+
|
|
1351
|
+
#[test]
|
|
1352
|
+
fn dsml_multiple_invokes_are_recovered_in_order() {
|
|
1353
|
+
let tools = vec![command_output_spec()];
|
|
1354
|
+
let Extracted { text, calls, .. } = extract(
|
|
1355
|
+
"Working <|DSML|tool_calls><|DSML|invoke name=\"command_output\"><|DSML|parameter name=\"task_id\" string=\"false\">1<|DSML|/parameter><|DSML|/invoke><|DSML|invoke name=\"command_output\"><|DSML|parameter name=\"task_id\" string=\"false\">2<|DSML|/parameter><|DSML|/invoke><|DSML|/tool_calls>",
|
|
1356
|
+
&tools,
|
|
1357
|
+
);
|
|
1358
|
+
assert_eq!(text, "Working ");
|
|
1359
|
+
assert_eq!(calls.len(), 2);
|
|
1360
|
+
let first: serde_json::Value = serde_json::from_str(&calls[0].arguments).unwrap();
|
|
1361
|
+
let second: serde_json::Value = serde_json::from_str(&calls[1].arguments).unwrap();
|
|
1362
|
+
assert_eq!(first["task_id"], 1);
|
|
1363
|
+
assert_eq!(second["task_id"], 2);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
#[test]
|
|
1367
|
+
fn dsml_string_true_is_kept_verbatim_and_string_false_is_json_typed() {
|
|
1368
|
+
let tools = vec![run_command_spec()];
|
|
1369
|
+
let Extracted { calls, .. } = extract(
|
|
1370
|
+
"<|DSML|tool_calls><|DSML|invoke name=\"run_command\"><|DSML|parameter name=\"command\" string=\"true\">echo {\"a\":1}<|DSML|/parameter><|DSML|parameter name=\"run_in_background\" string=\"false\">true<|DSML|/parameter><|DSML|/invoke><|DSML|/tool_calls>",
|
|
1371
|
+
&tools,
|
|
1372
|
+
);
|
|
1373
|
+
let args: serde_json::Value = serde_json::from_str(&calls[0].arguments).unwrap();
|
|
1374
|
+
assert_eq!(args["command"], "echo {\"a\":1}");
|
|
1375
|
+
assert_eq!(args["run_in_background"], true);
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
#[test]
|
|
1379
|
+
fn dsml_missing_string_attribute_uses_the_tool_schema() {
|
|
1380
|
+
let tools = vec![command_output_spec()];
|
|
1381
|
+
let Extracted { calls, .. } = extract(
|
|
1382
|
+
"<|DSML|tool_calls><|DSML|invoke name=\"command_output\"><|DSML|parameter name=\"task_id\">2<|DSML|/parameter><|DSML|/invoke><|DSML|/tool_calls>",
|
|
1383
|
+
&tools,
|
|
1384
|
+
);
|
|
1385
|
+
let args: serde_json::Value = serde_json::from_str(&calls[0].arguments).unwrap();
|
|
1386
|
+
assert_eq!(args["task_id"], 2);
|
|
1387
|
+
assert!(args["task_id"].is_number());
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
#[test]
|
|
1391
|
+
fn unterminated_dsml_tool_calls_block_is_dropped() {
|
|
1392
|
+
let text = "<|DSML|tool_calls><|DSML|invoke name=\"run_command\"><|DSML|parameter name=\"command\" string=\"true\">echo hi<|DSML|/parameter>";
|
|
1393
|
+
let Extracted {
|
|
1394
|
+
text: out,
|
|
1395
|
+
calls,
|
|
1396
|
+
malformed,
|
|
1397
|
+
} = extract(text, &[run_command_spec()]);
|
|
1398
|
+
assert_eq!(out, "");
|
|
1399
|
+
assert_eq!(malformed, 1);
|
|
1400
|
+
assert!(calls.is_empty());
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
#[test]
|
|
1404
|
+
fn unknown_dsml_tool_is_dropped() {
|
|
1405
|
+
let text = "<|DSML|tool_calls><|DSML|invoke name=\"delete_everything\"><|DSML|/invoke><|DSML|/tool_calls>";
|
|
1406
|
+
let Extracted {
|
|
1407
|
+
text: out,
|
|
1408
|
+
calls,
|
|
1409
|
+
malformed,
|
|
1410
|
+
} = extract(text, &[run_command_spec()]);
|
|
1411
|
+
assert_eq!(out, "");
|
|
1412
|
+
assert_eq!(malformed, 1);
|
|
1413
|
+
assert!(calls.is_empty());
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
#[test]
|
|
1417
|
+
fn invalid_dsml_json_typed_parameter_is_dropped() {
|
|
1418
|
+
let text = "<|DSML|tool_calls><|DSML|invoke name=\"command_output\"><|DSML|parameter name=\"task_id\" string=\"false\">not-json<|DSML|/parameter><|DSML|/invoke><|DSML|/tool_calls>";
|
|
1419
|
+
let Extracted {
|
|
1420
|
+
text: out,
|
|
1421
|
+
calls,
|
|
1422
|
+
malformed,
|
|
1423
|
+
} = extract(text, &[command_output_spec()]);
|
|
1424
|
+
assert_eq!(out, "");
|
|
1425
|
+
assert_eq!(malformed, 1);
|
|
1426
|
+
assert!(calls.is_empty());
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
#[test]
|
|
1430
|
+
fn a_dsml_marker_mentioned_in_prose_stays_text() {
|
|
1431
|
+
let tools = vec![run_command_spec()];
|
|
1432
|
+
for text in [
|
|
1433
|
+
"The marker is `<|DSML|tool_calls>` and it never closes here.",
|
|
1434
|
+
"Quote `<|DSML|tool_calls>` then `<|DSML|/tool_calls>` in docs.",
|
|
1435
|
+
] {
|
|
1436
|
+
assert_eq!(
|
|
1437
|
+
extract(text, &tools),
|
|
1438
|
+
Extracted {
|
|
1439
|
+
text: text.to_string(),
|
|
1440
|
+
..Extracted::default()
|
|
1441
|
+
},
|
|
1442
|
+
"{text}"
|
|
1443
|
+
);
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
#[test]
|
|
1448
|
+
fn scanner_recovers_dsml_tool_calls_split_across_chunk_boundaries_including_inside_the_marker()
|
|
1449
|
+
{
|
|
1450
|
+
let tools = vec![run_command_spec()];
|
|
1451
|
+
let mut scanner = StreamScanner::new(&tools);
|
|
1452
|
+
let mut text = String::new();
|
|
1453
|
+
let mut calls = Vec::new();
|
|
1454
|
+
|
|
1455
|
+
for chunk in [
|
|
1456
|
+
"pre ",
|
|
1457
|
+
"<|DSML",
|
|
1458
|
+
"|tool_calls><|DSML|invoke name=\"run_command\"><|DSML|paramet",
|
|
1459
|
+
"er name=\"command\" string=\"true\">echo hi<|DSML|/parameter><|DSML|/invoke><|DSML|/tool_calls>",
|
|
1460
|
+
" post",
|
|
1461
|
+
] {
|
|
1462
|
+
for event in scanner.push(chunk) {
|
|
1463
|
+
match event {
|
|
1464
|
+
ScanEvent::Text(t) => text.push_str(&t),
|
|
1465
|
+
ScanEvent::ToolCall(c) => calls.push(c),
|
|
1466
|
+
ScanEvent::Malformed(block) => panic!("unexpected malformed block: {block}"),
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
if let Some(ScanEvent::Text(rest)) = scanner.finish() {
|
|
1471
|
+
text.push_str(&rest);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
assert_eq!(text, "pre post");
|
|
1475
|
+
assert_eq!(calls.len(), 1);
|
|
1476
|
+
assert_eq!(calls[0].name, "run_command");
|
|
1477
|
+
let args: serde_json::Value = serde_json::from_str(&calls[0].arguments).unwrap();
|
|
1478
|
+
assert_eq!(args["command"], "echo hi");
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
#[test]
|
|
1482
|
+
fn scanner_reports_an_unclosed_dsml_tag_at_stream_end_as_malformed() {
|
|
1483
|
+
let tools = vec![run_command_spec()];
|
|
1484
|
+
let mut scanner = StreamScanner::new(&tools);
|
|
1485
|
+
let events =
|
|
1486
|
+
scanner.push("partial <|DSML|tool_calls><|DSML|invoke name=\"run_command\">");
|
|
1487
|
+
assert_eq!(events, vec![ScanEvent::Text("partial ".to_string())]);
|
|
1488
|
+
assert_eq!(
|
|
1489
|
+
scanner.finish(),
|
|
1490
|
+
Some(ScanEvent::Malformed(
|
|
1491
|
+
"<|DSML|tool_calls><|DSML|invoke name=\"run_command\">".to_string()
|
|
1492
|
+
))
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1234
1495
|
}
|
|
@@ -308,7 +308,8 @@ fn stop_reason_for(round_cap_reached: bool, finish: backend::FinishReason) -> St
|
|
|
308
308
|
/// when nothing follows it (issue #120).
|
|
309
309
|
fn round_cap_stop_message(max_tool_rounds: usize) -> String {
|
|
310
310
|
format!(
|
|
311
|
-
"I stopped here because this turn used all {max_tool_rounds} of its tool rounds,
|
|
311
|
+
"I stopped here because this turn used all {max_tool_rounds} of its tool rounds, \
|
|
312
|
+
so the task may not be finished. Reply \"continue\" to pick it back up."
|
|
312
313
|
)
|
|
313
314
|
}
|
|
314
315
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sigit_code-1.6.3 → sigit_code-1.6.4}/packaging/winget/getSigit.siGitCode.locale.en-US.yaml.in
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|