Skip to main content

winx_code_agent/utils/
orchestration.rs

1use std::fmt::Write as _;
2
3const DEFAULT_INSTRUCTIONS: &str = concat!(
4    "Initialize Winx once for the intended workspace. Treat structuredContent.status as ",
5    "authoritative. Never repeat a failed tool call unchanged: execute next_action and every ",
6    "required_read first. Before every later call, confirm workspace_root still matches ",
7    "the user's current project, then pass its exact thread_id/workspace_root pair; never borrow ",
8    "one from another chat. It identifies the session, not a path sandbox: WINX_ALLOW_PATHS and ",
9    "the active mode independently control target paths, and an allowed absolute target outside ",
10    "the workspace never requires rebinding. If Initialize reports ",
11    "initialize_workspace_already_bound or workspace_change_requires_new_session, do not call ",
12    "Initialize again in that conversation; continue with the bound pair, or start a new ",
13    "conversation for a genuinely different project. Use CodeMap to locate code, then batch ReadFiles ",
14    "calls. Read every target file and required range before editing. Use MultiFileEdit for ",
15    "atomic cross-file changes. Compose related finite fail-fast checks in one BashCommand with ",
16    "&&. When an edit tool exposes verify_command, use it for a quick finite post-edit check in ",
17    "the same round trip; the edit remains applied if verification fails. BashCommand defaults ",
18    "to wait_policy=adaptive; use until_complete for finite long commands and return_early when ",
19    "prompt control matters. Call status_check only when status is running. Do not automatically ",
20    "approve interactive prompts that change permissions, data, or system state. Use ReadFiles ",
21    "for exact text and unsupported CodeMap languages; never transform source solely to make ",
22    "CodeMap parse it. Put genuinely useful derived representations, adapters, and helpers only ",
23    "in the temporary_artifact_dir returned by Initialize. BashCommand exports that exact path ",
24    "as WINX_TEMP_DIR; if a shell command must produce a derived helper, create it only beneath ",
25    "$WINX_TEMP_DIR. Use short descriptive paths, preserve original source_path and line ",
26    "provenance, treat derived files as non-canonical, and remove them when done. Never encode ",
27    "source or tool output into filenames or directory names, and never create ad hoc .winx-* or ",
28    ".winx_tmp artifacts at the project root."
29);
30
31const DISALLOW_INSTRUCTION: &str = "As soon as you encounter \"The user has chosen to disallow the tool call.\", immediately stop doing everything and ask the user for the reason.";
32
33/// Instructions advertised in the MCP handshake. The stable defaults come
34/// first so clients that truncate this field still receive the orchestration
35/// contract; operator-provided rules are appended without requiring a rebuild.
36pub fn server_instructions() -> String {
37    let mut instructions = format!("{DEFAULT_INSTRUCTIONS}\n\n{DISALLOW_INSTRUCTION}");
38    if let Some(extra) = crate::config::env_text("WINX_SERVER_INSTRUCTIONS") {
39        let _ = write!(instructions, "\n\nOperator instructions:\n{extra}");
40    }
41    instructions
42}
43
44/// Mirror the handshake contract in Initialize output for older clients that
45/// do not surface ServerInfo.instructions to the model.
46pub fn append_initialize_instructions(response: &mut String) {
47    let _ = write!(response, "\n# Winx orchestration contract\n{}\n", server_instructions());
48}
49
50#[cfg(test)]
51mod tests {
52    use super::server_instructions;
53
54    #[test]
55    fn default_contract_names_structured_recovery_before_optional_operator_text() {
56        let instructions = server_instructions();
57        let prefix = instructions.chars().take(512).collect::<String>();
58        assert!(prefix.contains("structuredContent.status"));
59        assert!(prefix.contains("Never repeat a failed tool call unchanged"));
60        assert!(prefix.contains("required_read"));
61        assert!(prefix.contains("thread_id/workspace_root pair"));
62        assert!(prefix.contains("user's current project"));
63        assert!(prefix.contains("not a path sandbox"));
64    }
65
66    #[test]
67    fn default_contract_keeps_agent_artifacts_coherent() {
68        let instructions = server_instructions();
69        assert!(instructions.contains("never transform source solely"));
70        assert!(instructions.contains("Use ReadFiles"));
71        assert!(instructions.contains("temporary_artifact_dir returned by Initialize"));
72        assert!(instructions.contains("derived files as non-canonical"));
73        assert!(instructions.contains("exports that exact path as WINX_TEMP_DIR"));
74        assert!(instructions.contains("only beneath $WINX_TEMP_DIR"));
75        assert!(instructions.contains("Never encode source or tool output into filenames"));
76        assert!(instructions.contains("never create ad hoc .winx-* or .winx_tmp"));
77    }
78
79    #[test]
80    fn default_contract_stops_terminal_initialize_retries() {
81        let instructions = server_instructions();
82        assert!(instructions.contains("initialize_workspace_already_bound"));
83        assert!(instructions.contains("workspace_change_requires_new_session"));
84        assert!(instructions.contains("do not call Initialize again"));
85        assert!(instructions.contains("allowed absolute target outside the workspace"));
86    }
87}