§9 acceptance: grep-zero sweep — every internal x.ai/grok identifier renamed
The PRD's first acceptance gate now holds: grep -RinE '\bx\.ai\b|grok' crates/ --include='*.rs' → 0 matches (exempt: NOTICE and third-party license archives, README provenance, and the required 'Based on Grok Build Open Source' attribution, now sourced from version_attribution.txt). Wire-visible renames (both sides in this repo, changed in lockstep): - Auth method id 'grok.com' → 'kimi-code' (AuthMethodKind::KimiCode). - Every x.ai/* and _x.ai/* ACP ext method and meta key → kigi/* / _kigi/* (~200 names; grokShell → kigiShell). Session-file replay keeps a read-side alias for the legacy '_x.ai/session/update' method so existing updates.jsonl histories load; writes emit only the new name (both directions test-pinned). - Agent types grok-build* → kigi* with a documented legacy-prefix alias at resolution time so persisted sessions keep resolving. - ToolNamespace/BuiltinAgentName GrokBuild* → Kigi* (wire snake_case kigi/kigi_concise/kigi_hashline; schema regenerated); grok_build implementation dirs renamed to kigi*. - x-grok-* headers → x-kigi-*, __GROK_* sentinels → __KIGI_*, themes grokday/groknight → kigiday/kiginight (old persisted values fall back to the default theme), web_fetch allowlist xAI hosts → kimi.com + moonshot platforms, changelog CDN → this repo, grok-build changelog archives deleted. - BYOK default endpoint removed: [endpoints] api_base_url is now truly optional with NO default — consumers fail fast with the flag name when unset (no silent x.ai egress). Mock harnesses inject it explicitly. - System-prompt identity fixed: 'released by xAI' → 'an unofficial community CLI for Kimi' (template + regenerated encrypted form). Also repaired pre-existing grok-era test debt found by the sweep: the stale trace_classify default-model pin, the grok-pager UA label test, pty-harness stale-binary reuse and non-hermetic moonshot routing (a PTY test could previously reach the real api.moonshot.cn), and the outdated oauth fixture scope key. Gates: §9 grep 0; fmt clean; workspace check/clippy 0/0 (-D warnings); FULL cargo test --workspace: 234 suites, 21,961 passed, 0 failed; deny advisories ok.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! Parses the `_meta` JSON from `SessionNotification` into a struct with
|
||||
//! typed fields. All fields are `Option` — gracefully degrades when
|
||||
//! grok-shell hasn't been updated or meta is absent.
|
||||
//! kigi-shell hasn't been updated or meta is absent.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -154,7 +154,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_missing_new_fields() {
|
||||
// Simulate old grok-shell that doesn't send streamStartMs/turnStartMs
|
||||
// Simulate old kigi-shell that doesn't send streamStartMs/turnStartMs
|
||||
let meta_json = json!({
|
||||
"totalTokens": 1000u64,
|
||||
"agentTimestampMs": 1700000000000i64,
|
||||
|
||||
@@ -54,8 +54,8 @@ pub struct AcpConnection {
|
||||
pub rx: AcpClientRx,
|
||||
/// Available models and current selection.
|
||||
pub models: ModelState,
|
||||
/// Whether the agent is a grok-shell instance.
|
||||
pub is_grok_shell: bool,
|
||||
/// Whether the agent is a kigi-shell instance.
|
||||
pub is_kigi_shell: bool,
|
||||
/// Auth methods advertised by the agent.
|
||||
pub auth_methods: Vec<acp::AuthMethod>,
|
||||
/// Cancellation token to stop the agent.
|
||||
@@ -64,9 +64,9 @@ pub struct AcpConnection {
|
||||
/// Seeded into every new `AgentSession` so autocomplete has shell builtins
|
||||
/// and skills immediately, before any `AvailableCommandsUpdate` arrives.
|
||||
pub available_commands: Vec<acp::AvailableCommand>,
|
||||
/// Whether interactive login is required (deferred auth for `grok.com`).
|
||||
/// Whether interactive login is required (deferred auth for `kimi-code`).
|
||||
pub needs_login: bool,
|
||||
/// Login button label from `AuthMethod.name` (e.g., "grok.com", "Acme Corp").
|
||||
/// Login button label from `AuthMethod.name` (e.g., "kimi-code", "Acme Corp").
|
||||
pub login_label: Option<String>,
|
||||
/// The auth method ID to use for login (copied from the first advertised method).
|
||||
pub login_method_id: Option<acp::AuthMethodId>,
|
||||
@@ -83,7 +83,7 @@ pub struct AcpConnection {
|
||||
/// resolved by the shell (remote settings / config / env; default OFF) and
|
||||
/// advertised in `InitializeResponse.meta.sessionRecap`. The client gates
|
||||
/// its automatic away-recap poll and the manual `/recap` on this so a
|
||||
/// disabled feature produces zero `x.ai/recap` traffic. Defaults to `false`
|
||||
/// disabled feature produces zero `kigi/recap` traffic. Defaults to `false`
|
||||
/// when absent (e.g. an older shell that predates the feature).
|
||||
pub session_recap_available: bool,
|
||||
/// `AuthManager` for pager-side authenticated channels.
|
||||
@@ -183,14 +183,14 @@ pub async fn connect(cancel: &CancellationToken, flags: ConnectFlags) -> Result<
|
||||
|
||||
// Spawn the agent
|
||||
let memory_config = agent_config.memory_config.clone();
|
||||
let spawned = spawn::spawn_grok_shell(agent_config, cancel, memory_config).await?;
|
||||
let spawned = spawn::spawn_kigi_shell(agent_config, cancel, memory_config).await?;
|
||||
let auth_manager = spawned.auth_manager.clone();
|
||||
let (tx, rx) = (spawned.channel.tx, spawned.channel.rx);
|
||||
|
||||
// Initialize
|
||||
let (
|
||||
models,
|
||||
is_grok_shell,
|
||||
is_kigi_shell,
|
||||
auth_methods,
|
||||
default_auth_method_id,
|
||||
available_commands,
|
||||
@@ -218,7 +218,7 @@ pub async fn connect(cancel: &CancellationToken, flags: ConnectFlags) -> Result<
|
||||
tx,
|
||||
rx,
|
||||
models,
|
||||
is_grok_shell,
|
||||
is_kigi_shell,
|
||||
auth_methods,
|
||||
cancel: spawned.cancel,
|
||||
available_commands,
|
||||
@@ -291,7 +291,7 @@ pub async fn connect_via_leader(
|
||||
|
||||
let (
|
||||
models,
|
||||
is_grok_shell,
|
||||
is_kigi_shell,
|
||||
auth_methods,
|
||||
default_auth_method_id,
|
||||
available_commands,
|
||||
@@ -329,7 +329,7 @@ pub async fn connect_via_leader(
|
||||
tx,
|
||||
rx,
|
||||
models,
|
||||
is_grok_shell,
|
||||
is_kigi_shell,
|
||||
auth_methods,
|
||||
cancel: bridge.cancel,
|
||||
available_commands,
|
||||
@@ -439,10 +439,10 @@ fn client_capabilities_meta(flags: &ConnectFlags) -> serde_json::Value {
|
||||
let hunk_mode =
|
||||
crate::settings::canonical_hunk_tracker_mode(flags.hunk_tracker_mode.as_deref());
|
||||
serde_json::json!({
|
||||
"x.ai/incrementalBashOutput": true,
|
||||
"x.ai/hunkTracker": { "mode": hunk_mode },
|
||||
"x.ai/bashOutputNoColor": true,
|
||||
"x.ai/gitHeadChanged": true,
|
||||
"kigi/incrementalBashOutput": true,
|
||||
"kigi/hunkTracker": { "mode": hunk_mode },
|
||||
"kigi/bashOutputNoColor": true,
|
||||
"kigi/gitHeadChanged": true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -482,11 +482,11 @@ async fn initialize(
|
||||
|
||||
let resp: acp::InitializeResponse = acp_send(req, tx).await?;
|
||||
|
||||
// Check if this is a grok-shell agent
|
||||
let is_grok_shell = resp
|
||||
// Check if this is a kigi-shell agent
|
||||
let is_kigi_shell = resp
|
||||
.meta
|
||||
.as_ref()
|
||||
.and_then(|m| m.get("grokShell"))
|
||||
.and_then(|m| m.get("kigiShell"))
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false);
|
||||
|
||||
@@ -514,7 +514,7 @@ async fn initialize(
|
||||
|
||||
Ok((
|
||||
models,
|
||||
is_grok_shell,
|
||||
is_kigi_shell,
|
||||
resp.auth_methods,
|
||||
default_auth_method_id,
|
||||
available_commands,
|
||||
@@ -545,7 +545,7 @@ pub fn parse_session_recap_available(meta: Option<&acp::Meta>) -> bool {
|
||||
|
||||
/// Determine whether interactive login is needed based on the advertised auth methods.
|
||||
///
|
||||
/// Matches TUI startup behavior: if the first method is `grok.com`, defer auth
|
||||
/// Matches TUI startup behavior: if the first method is `kimi-code`, defer auth
|
||||
/// and show the login-aware welcome flow. Otherwise, authenticate eagerly.
|
||||
///
|
||||
/// Returns `(needs_login, login_label, login_method_id, auth_start_mode)`.
|
||||
@@ -589,7 +589,7 @@ pub fn startup_auth_metadata(
|
||||
///
|
||||
/// Used when eager auth (cached_token / API key) fails and we need to fall
|
||||
/// back to the welcome screen with a working login button. Scans the list
|
||||
/// for a `grok.com` or `oidc` method — these are the ones that can trigger
|
||||
/// for a `kimi-code` or `oidc` method — these are the ones that can trigger
|
||||
/// a browser-based re-auth flow.
|
||||
pub fn find_interactive_login_method(
|
||||
auth_methods: &[acp::AuthMethod],
|
||||
@@ -767,7 +767,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_available_commands_missing_key_returns_empty() {
|
||||
let meta = serde_json::json!({ "grokShell": true });
|
||||
let meta = serde_json::json!({ "kigiShell": true });
|
||||
let cmds = parse_available_commands(meta.as_object());
|
||||
assert!(cmds.is_empty());
|
||||
}
|
||||
@@ -801,7 +801,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_session_recap_available_defaults_off_when_missing() {
|
||||
let meta = serde_json::json!({ "grokShell": true, "cancelRewind": true });
|
||||
let meta = serde_json::json!({ "kigiShell": true, "cancelRewind": true });
|
||||
assert!(!parse_session_recap_available(meta.as_object()));
|
||||
assert!(!parse_session_recap_available(None));
|
||||
}
|
||||
@@ -832,28 +832,28 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn startup_auth_grok_com_no_provider_needs_login_pending() {
|
||||
let methods = vec![make_auth_method("grok.com", "grok.com", None)];
|
||||
fn startup_auth_kigi_com_no_provider_needs_login_pending() {
|
||||
let methods = vec![make_auth_method("kimi-code", "kimi-code", None)];
|
||||
let (needs, label, method_id, mode) = startup_auth_metadata(&methods);
|
||||
assert!(needs);
|
||||
assert_eq!(label.as_deref(), Some("grok.com"));
|
||||
assert_eq!(method_id.as_ref().unwrap().0.as_ref(), "grok.com");
|
||||
assert_eq!(label.as_deref(), Some("kimi-code"));
|
||||
assert_eq!(method_id.as_ref().unwrap().0.as_ref(), "kimi-code");
|
||||
assert_eq!(mode, AuthStartMode::Pending);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn startup_auth_grok_com_with_external_provider_command() {
|
||||
fn startup_auth_kigi_com_with_external_provider_command() {
|
||||
let meta = serde_json::json!({ "external_provider": true });
|
||||
let methods = vec![make_auth_method("grok.com", "Acme Corp", Some(meta))];
|
||||
let methods = vec![make_auth_method("kimi-code", "Acme Corp", Some(meta))];
|
||||
let (needs, label, method_id, mode) = startup_auth_metadata(&methods);
|
||||
assert!(needs);
|
||||
assert_eq!(label.as_deref(), Some("Acme Corp"));
|
||||
assert_eq!(method_id.as_ref().unwrap().0.as_ref(), "grok.com");
|
||||
assert_eq!(method_id.as_ref().unwrap().0.as_ref(), "kimi-code");
|
||||
assert_eq!(mode, AuthStartMode::Command);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn startup_auth_non_grok_com_no_login() {
|
||||
fn startup_auth_non_kigi_com_no_login() {
|
||||
let methods = vec![make_auth_method("api-key", "API Key", None)];
|
||||
let (needs, label, method_id, mode) = startup_auth_metadata(&methods);
|
||||
assert!(!needs);
|
||||
@@ -890,7 +890,7 @@ mod tests {
|
||||
// enterprise-style: model has `env_key` set and the env var resolves,
|
||||
// so the shell-side predicate returns true.
|
||||
has_external_api_key: true,
|
||||
// Realistic enterprise user: no cached session token, default `grok.com`
|
||||
// Realistic enterprise user: no cached session token, default `kimi-code`
|
||||
// login (no enterprise OIDC).
|
||||
has_cached_token: false,
|
||||
login_label: None,
|
||||
@@ -916,27 +916,27 @@ mod tests {
|
||||
/// `auth_methods.first()`. This locks the failure mode of the regression:
|
||||
/// if a future refactor makes the pager scan past `.first()`, this test
|
||||
/// stops being equivalent to
|
||||
/// `startup_auth_grok_com_no_provider_needs_login_pending` above and
|
||||
/// `startup_auth_kigi_com_no_provider_needs_login_pending` above and
|
||||
/// either passes or fails on a meaningful new code path.
|
||||
#[test]
|
||||
fn startup_auth_xai_api_key_not_first_still_requires_login() {
|
||||
use kigi_shell::agent::auth_method::{KIGI_COM_METHOD_ID, XAI_API_KEY_METHOD_ID};
|
||||
use kigi_shell::agent::auth_method::{KIMI_CODE_METHOD_ID, XAI_API_KEY_METHOD_ID};
|
||||
|
||||
let methods = vec![
|
||||
make_auth_method(KIGI_COM_METHOD_ID, "Grok", None),
|
||||
make_auth_method(KIMI_CODE_METHOD_ID, "Kigi", None),
|
||||
make_auth_method(XAI_API_KEY_METHOD_ID, "xai.api_key", None),
|
||||
];
|
||||
let (needs, _, _, _) = startup_auth_metadata(&methods);
|
||||
assert!(
|
||||
needs,
|
||||
"with grok.com first, the pager must require login -- pinning \
|
||||
"with kimi.com first, the pager must require login -- pinning \
|
||||
the BAD-ordering failure mode (xai.api_key not first)",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn startup_auth_method_id_is_copied_not_synthesized() {
|
||||
let methods = vec![make_auth_method("grok.com", "My Login", None)];
|
||||
let methods = vec![make_auth_method("kimi-code", "My Login", None)];
|
||||
let (_, _, method_id, _) = startup_auth_metadata(&methods);
|
||||
// Verify it's the exact same ID from the method, not hardcoded
|
||||
assert_eq!(&method_id.unwrap(), methods[0].id());
|
||||
@@ -945,7 +945,7 @@ mod tests {
|
||||
#[test]
|
||||
fn startup_auth_external_provider_false_is_pending() {
|
||||
let meta = serde_json::json!({ "external_provider": false });
|
||||
let methods = vec![make_auth_method("grok.com", "grok.com", Some(meta))];
|
||||
let methods = vec![make_auth_method("kimi-code", "kimi-code", Some(meta))];
|
||||
let (_, _, _, mode) = startup_auth_metadata(&methods);
|
||||
assert_eq!(mode, AuthStartMode::Pending);
|
||||
}
|
||||
@@ -1033,12 +1033,12 @@ mod tests {
|
||||
// Rows 1 & 2 of the truth table: nothing set, and a set-but-blank value,
|
||||
// both advertise the `agent_only` default (never `""` → AllDirty).
|
||||
let absent = client_capabilities_meta(&ConnectFlags::default());
|
||||
assert_eq!(absent["x.ai/hunkTracker"]["mode"], "agent_only");
|
||||
assert_eq!(absent["kigi/hunkTracker"]["mode"], "agent_only");
|
||||
let blank = client_capabilities_meta(&ConnectFlags {
|
||||
hunk_tracker_mode: Some(" ".into()),
|
||||
..Default::default()
|
||||
});
|
||||
assert_eq!(blank["x.ai/hunkTracker"]["mode"], "agent_only");
|
||||
assert_eq!(blank["kigi/hunkTracker"]["mode"], "agent_only");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1050,7 +1050,7 @@ mod tests {
|
||||
hunk_tracker_mode: Some(raw.into()),
|
||||
..Default::default()
|
||||
});
|
||||
assert_eq!(meta["x.ai/hunkTracker"]["mode"], "off", "raw={raw}");
|
||||
assert_eq!(meta["kigi/hunkTracker"]["mode"], "off", "raw={raw}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ impl ModelState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Machine-readable model ID string for the current model (e.g. "grok-4.5").
|
||||
/// Machine-readable model ID string for the current model (e.g. "kigi-4.5").
|
||||
pub fn current_model_id_str(&self) -> Option<&str> {
|
||||
Some(self.current.as_ref()?.0.as_ref())
|
||||
}
|
||||
@@ -94,7 +94,7 @@ impl ModelState {
|
||||
///
|
||||
/// Honors an explicit `acceptsImages` bool, else an `inputModalities` array
|
||||
/// containing `"image"`. DEFAULTS TO `true` when neither key is present:
|
||||
/// correct today (all current Grok models accept images, so nothing is
|
||||
/// correct today (all current Kigi models accept images, so nothing is
|
||||
/// suppressed) and forward-compatible (suppresses non-vision models once the
|
||||
/// ACP server populates the key). Populating that key server-side is a
|
||||
/// separate change.
|
||||
@@ -223,7 +223,7 @@ impl ModelState {
|
||||
/// Map a typed/selected effort token to its canonical value for the current
|
||||
/// model. Accepts a menu option id (case-insensitive) or a canonical level
|
||||
/// that appears as a **value** in that model's menu. Levels the model does
|
||||
/// not offer (e.g. `none` on grok-4.5) are rejected so we fail in the TUI
|
||||
/// not offer (e.g. `none` on kigi-4.5) are rejected so we fail in the TUI
|
||||
/// instead of sending a blocked effort to the API.
|
||||
pub fn resolve_effort_token(&self, token: &str) -> Option<ReasoningEffort> {
|
||||
match self.current.as_ref() {
|
||||
@@ -249,7 +249,7 @@ impl ModelState {
|
||||
}
|
||||
// Canonical level (e.g. "high", "max"→xhigh) only if the model menu
|
||||
// actually offers that value — not free-form power-user aliases that
|
||||
// would 400 on the server (e.g. `none` on grok-4.5).
|
||||
// would 400 on the server (e.g. `none` on kigi-4.5).
|
||||
let parsed = token.parse::<ReasoningEffort>().ok()?;
|
||||
options
|
||||
.iter()
|
||||
@@ -410,21 +410,17 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn update_catalog_preserves_user_effort_when_model_unchanged() {
|
||||
let id = acp::ModelId::new(Arc::from("grok-build"));
|
||||
let id = acp::ModelId::new(Arc::from("kigi"));
|
||||
let mut state = ModelState::default();
|
||||
state.available.insert(
|
||||
id.clone(),
|
||||
model_with_effort("grok-build", "Grok Build", "high"),
|
||||
);
|
||||
state
|
||||
.available
|
||||
.insert(id.clone(), model_with_effort("kigi", "Kigi", "high"));
|
||||
state.set_current(id.clone(), Some(ReasoningEffort::Xhigh));
|
||||
assert_eq!(state.reasoning_effort, Some(ReasoningEffort::Xhigh));
|
||||
|
||||
// The broadcast carries the model's static default (high) for the same model.
|
||||
let mut refreshed = IndexMap::new();
|
||||
refreshed.insert(
|
||||
id.clone(),
|
||||
model_with_effort("grok-build", "Grok Build", "high"),
|
||||
);
|
||||
refreshed.insert(id.clone(), model_with_effort("kigi", "Kigi", "high"));
|
||||
state.update_catalog(refreshed, Some(id.clone()));
|
||||
|
||||
assert_eq!(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Agent spawning — creates the agent process and ACP channels.
|
||||
//!
|
||||
//! Simplified to only support GrokShell (in-process) mode.
|
||||
//! Simplified to only support KigiShell (in-process) mode.
|
||||
//! Subprocess and remote modes can be added later if needed.
|
||||
|
||||
use std::rc::Rc;
|
||||
@@ -30,10 +30,10 @@ pub struct SpawnedAgent {
|
||||
pub auth_manager: std::sync::Arc<AuthManager>,
|
||||
}
|
||||
|
||||
/// Spawn a GrokShell agent in a background thread.
|
||||
/// Spawn a KigiShell agent in a background thread.
|
||||
///
|
||||
/// Returns the ACP client channel for communication and a cancellation token.
|
||||
pub async fn spawn_grok_shell(
|
||||
pub async fn spawn_kigi_shell(
|
||||
agent_config: AgentConfig,
|
||||
cancel: &CancellationToken,
|
||||
memory_config: Option<kigi_shell::config::MemoryConfig>,
|
||||
|
||||
@@ -255,7 +255,7 @@ pub struct AcpUpdateTracker {
|
||||
/// Tool call IDs marked as background (`is_background=true`).
|
||||
///
|
||||
/// First-detection (no scrollback entry yet): defers entry creation until
|
||||
/// `x.ai/task_backgrounded` creates a `BgTask` block.
|
||||
/// `kigi/task_backgrounded` creates a `BgTask` block.
|
||||
/// Late-detection (Execute block already exists): suppresses further output
|
||||
/// streaming; the existing block is demoted by `handle_task_backgrounded`.
|
||||
///
|
||||
@@ -2129,7 +2129,7 @@ fn task_ids_from_raw_input(raw: &serde_json::Value) -> Vec<String> {
|
||||
}
|
||||
/// Check if a tool call is a background execute (`is_background=true`).
|
||||
///
|
||||
/// These are deferred from scrollback — the `x.ai/task_backgrounded`
|
||||
/// These are deferred from scrollback — the `kigi/task_backgrounded`
|
||||
/// notification creates a `BgTask` block instead of an `Execute` block.
|
||||
///
|
||||
/// Eager ACP messages often use `kind=Other` with `title=run_terminal_command`
|
||||
@@ -2602,7 +2602,7 @@ fn make_relative_path(path: &str) -> String {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::sync::Arc;
|
||||
/// Default meta with no timestamps (simulates old grok-shell or tests that
|
||||
/// Default meta with no timestamps (simulates old kigi-shell or tests that
|
||||
/// don't care about timing).
|
||||
fn meta() -> NotificationMeta {
|
||||
NotificationMeta::default()
|
||||
@@ -4602,7 +4602,7 @@ mod tests {
|
||||
"stream A message should be finished"
|
||||
);
|
||||
}
|
||||
/// No stream_start_ms (old grok-shell) should not break anything.
|
||||
/// No stream_start_ms (old kigi-shell) should not break anything.
|
||||
#[test]
|
||||
fn no_stream_start_ms_preserves_existing_behavior() {
|
||||
let mut sb = ScrollbackState::new();
|
||||
@@ -5554,7 +5554,7 @@ mod tests {
|
||||
.status(acp::ToolCallStatus::Pending)
|
||||
}
|
||||
#[test]
|
||||
fn is_task_tool_recognizes_grok_build_variant() {
|
||||
fn is_task_tool_recognizes_kigi_variant() {
|
||||
assert!(is_task_tool(&initial_tool_call("tc1", "task")));
|
||||
let mut with_variant = initial_tool_call("tc2", "anything");
|
||||
with_variant.raw_input = Some(serde_json::json!({ "variant" : "Task" }));
|
||||
|
||||
Reference in New Issue
Block a user