winx_code_agent/utils/
orchestration.rs1use 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. Keep the exact thread_id/workspace_root pair for the user's current ",
7 "project; never borrow one from another chat. workspace_root identifies the session, not a ",
8 "path sandbox: allowed absolute targets outside it do not require rebinding. After a SEARCH ",
9 "conflict, only ReadFiles refreshes the edit guard: execute its exact next_action, then use the ",
10 "returned revision and visible lines in one corrected EditFiles line_patch. Never fall back to ",
11 "shell, sed, or Python for an ordinary source edit. If ",
12 "edit_applied=true or status=completed_with_issues, never repeat the original edit: diagnose ",
13 "verification and make only corrective changes. If Initialize ",
14 "reports initialize_workspace_already_bound or workspace_change_requires_new_session, do not ",
15 "call Initialize again in that conversation. If a reset or mode change follows adapter state ",
16 "loss, accept the recovered first_call result and continue with its returned pair. Request ",
17 "reset_shell only after BashCommand returns a shell-runtime failure; if Winx reports ",
18 "reset_skipped_healthy, continue with the preserved PTY instead of retrying. Use CodeMap ",
19 "with a concise query to locate code, ",
20 "then batch ReadFiles for exact text. Read existing targets before editing; use one EditFiles ",
21 "call for a single change or a validated cross-file batch (each file is replaced atomically; a ",
22 "commit-time failure reports which files were already written). For an existing file read by ReadFiles, ",
23 "default to revision-bound line_patch; reserve search_replace for intentional text anchoring ",
24 "copied exactly from the current read, replace for new files or intentional whole-file rewrites, ",
25 "and undo only with the returned undo_id. Combine related ",
26 "finite checks with && or EditFiles verify_command. For ",
27 "BashCommand, follow next_action only while status is running and never auto-approve a prompt ",
28 "that changes permissions, data, or system state. If command_already_running, the new command ",
29 "did not start; status_check observes the existing execution. Run independent work with ",
30 "is_background=true and an explicit working directory; the new shell does not inherit foreground ",
31 "variables or functions. Never interrupt or reset an unrelated execution. Use ReadFiles for unsupported CodeMap ",
32 "languages; never transform source solely to make CodeMap parse it. Put useful derived helpers ",
33 "only in the temporary_artifact_dir returned by Initialize (exported as WINX_TEMP_DIR). Keep ",
34 "a small stable set: overwrite or reuse the same descriptive helper per purpose, retain ",
35 "source-path/line provenance, remove obsolete files, and treat helpers as non-canonical. Never turn ",
36 "TTL-expired helpers into durable state: Winx may reclaim them near the session quota. Never ",
37 "convert command, lint, test, or search output into a parseable carrier merely to call CodeMap. CodeMap ",
38 "on helpers accepts only an existing single file and has a smaller aggregate session budget; ",
39 "canonical source maps remain available. Never encode payload in names or create .winx-* or ",
40 ".winx_tmp artifacts at the project root."
41);
42
43const 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.";
44
45pub fn server_instructions() -> String {
53 static CACHE: std::sync::Mutex<Option<(Option<String>, String)>> = std::sync::Mutex::new(None);
54 let extra = crate::config::env_text("WINX_SERVER_INSTRUCTIONS");
55 let mut cache = CACHE.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
56 if let Some((cached_extra, text)) = cache.as_ref() {
57 if *cached_extra == extra {
58 return text.clone();
59 }
60 }
61 let mut instructions = format!("{DEFAULT_INSTRUCTIONS}\n\n{DISALLOW_INSTRUCTION}");
62 if let Some(extra) = extra.as_deref() {
63 let _ = write!(instructions, "\n\nOperator instructions:\n{extra}");
64 }
65 *cache = Some((extra, instructions.clone()));
66 instructions
67}
68
69pub fn append_initialize_instructions(response: &mut String) {
72 let _ = write!(response, "\n# Winx orchestration contract\n{}\n", server_instructions());
73}
74
75#[cfg(test)]
76mod tests {
77 use super::server_instructions;
78
79 #[test]
80 fn default_contract_names_structured_recovery_before_optional_operator_text() {
81 let instructions = server_instructions();
82 let prefix = instructions.chars().take(512).collect::<String>();
83 assert!(prefix.contains("structuredContent.status"));
84 assert!(prefix.contains("Never repeat a failed tool call unchanged"));
85 assert!(prefix.contains("required_read"));
86 assert!(prefix.contains("thread_id/workspace_root pair"));
87 assert!(prefix.contains("user's current project"));
88 assert!(prefix.contains("not a path sandbox"));
89 }
90
91 #[test]
92 fn default_contract_keeps_agent_artifacts_coherent() {
93 let instructions = server_instructions();
94 assert!(instructions.contains("never transform source solely"));
95 assert!(instructions.contains("Use ReadFiles"));
96 assert!(instructions.contains("temporary_artifact_dir returned by Initialize"));
97 assert!(instructions.contains("exported as WINX_TEMP_DIR"));
98 assert!(instructions.contains("helpers as non-canonical"));
99 assert!(instructions.contains("overwrite or reuse the same descriptive helper"));
100 assert!(instructions.contains("merely to call CodeMap"));
101 assert!(instructions.contains("existing single file"));
102 assert!(instructions.contains("Never encode payload in names"));
103 assert!(instructions.contains(".winx-* or .winx_tmp artifacts"));
104 assert!(instructions.contains("Winx may reclaim them near the session quota"));
105 }
106
107 #[test]
108 fn default_contract_stops_terminal_initialize_retries() {
109 let instructions = server_instructions();
110 assert!(instructions.contains("initialize_workspace_already_bound"));
111 assert!(instructions.contains("workspace_change_requires_new_session"));
112 assert!(instructions.contains("do not call Initialize again"));
113 assert!(instructions.contains("allowed absolute targets outside it"));
114 assert!(instructions.contains("reset_shell only after BashCommand"));
115 assert!(instructions.contains("reset_skipped_healthy"));
116 }
117
118 #[test]
119 fn default_contract_distinguishes_edit_conflicts_from_verification_follow_up() {
120 let instructions = server_instructions();
121 assert!(instructions.contains("only ReadFiles refreshes the edit guard"));
122 assert!(instructions.contains("one corrected EditFiles line_patch"));
123 assert!(instructions.contains("Never fall back to shell, sed, or Python"));
124 assert!(instructions.contains("default to revision-bound line_patch"));
125 assert!(instructions.contains("edit_applied=true"));
126 assert!(instructions.contains("status=completed_with_issues"));
127 assert!(instructions.contains("never repeat the original edit"));
128 }
129}