תיעוד 38
מצב headless וסקריפטים
מצב headless מריץ את Grok באופן לא אינטראקטיבי משורת הפקודה. הוא מקבל פרומפט יחיד, מריץ אותו עם גישה מלאה לכלים, ומחזיר את התוצאה. השתמשו בו כדי להפוך משימות לאוטומטיות, לכתוב סקריפטים לזרימות עבודה, לבנות אינטגרציות, ולנתח פלט באופן תכנותי.
#שימוש בסיסי
העברת פרומפט באופן לא אינטראקטיבי מפעילה את מצב headless. הדרך הנפוצה ביותר היא הדגל -p (קיצור של --single). גם --prompt-json ו---prompt-file מפעילים אותו:
grok -p "Your prompt here"Grok מעבד את הפרומפט, מריץ כלים נדרשים, ומדפיס את התוצאה ל-stdout. התהליך יוצא כשהתשובה הושלמה.
#אפשרויות שורת פקודה
| דגל | תיאור |
|---|---|
-p, --single <PROMPT> | הפרומפט לשליחה (או שימוש ב---prompt-json / --prompt-file) |
-m, --model <MODEL> | המודל לשימוש (לדוגמה, grok-4.6) |
-s, --session-id <ID> | יוצר סשן חדש עם UUID זה (שגיאה אם ה-UUID לא תקין או כבר בשימוש תחת תיקיית הסשנים היעד. לא ממשיך סשן, השתמשו ב--r/-c) |
--fork-session | עם -r/-c, מפצל למזהה סשן חדש במקום לצרף לסשן המקורי |
-r, --resume <ID_OR_TITLE> | ממשיך סשן קיים לפי מזהה, או לפי כותרת עבור התיקייה הנוכחית, בלי התחשבות ברישיות (בלי התאמה מדויקת, אותן מילים שמחוברות במפרידים כלשהם גם מתאימות, כך ש-fix-login-bug מוצא את "Fix login bug". התאמה יחידה ששמה שונה ידנית מנצחת בין כפילויות. כפילויות שנותרות מחזירות שגיאה עם עד עשר מהן, החדשות קודם, עם מזהים וזמני עדכון אחרונים. החמצה מציעה עד חמש כותרות דומות עם המזהים שלהן. ערכים בצורת UUID תמיד הולכים בנתיב המזהה. סקריפטים צריכים להעדיף מזהים) |
-c, --continue | ממשיך את הסשן האחרון בתיקייה הנוכחית |
--cwd <PATH> | מגדיר תיקיית עבודה |
--add-dir <DIR> | גם עובד בתיקייה הזו (ניתן לחזור). נתיב יחסי מתחיל מתיקיית העבודה. נדחה אם התיקייה לא מהימנה או שהסוכן עובד רק בתיקיית העבודה שלו. אי אפשר לשלב עם -r/-c: סשן שהמשיכו שומר את התיקיות שאיתן נוצר. |
--output-format <FMT> | פורמט פלט: plain, json, streaming-json, streaming-messages-json |
--include-partial-messages | פולט דלתאות גולמיות של stream_event. משפיע רק על --output-format streaming-messages-json. אחרת מתעלמים (עם אזהרה). |
--yolo | מאשר אוטומטית את כל הרצות הכלים |
--rules <TEXT> | כללים מותאמים לפרומפט המערכת |
--tools <TOOLS> | רשימת היתר של כלים מובנים (מופרדים בפסיקים). כלי מטא של MCP נשארים זמינים אלא אם נדחו. |
--disallowed-tools <TOOLS> | רשימת חסימה של כלים מובנים להסרה (מופרדים בפסיקים). תומך ברשומות Agent. |
--max-turns <N> | מספר מרבי של תורות סוכן לפני עצירה. רק ב-headless. |
--reasoning-effort / --effort <LEVEL> | מאמץ חשיבה למודלי חשיבה. רמות קנוניות: none, minimal, low, medium, high, xhigh, max (כל אחת שכבה נפרדת. מודל מקבל רק את הרמות שהתפריט שלו מפרסם). מקבל גם מזהי אפשרויות תפריט לפי מודל (לדוגמה deep שממופה לערך wire), כמו /effort. עובד ב-TUI וב-headless. |
--permission-mode <MODE> | מצב הרשאות. bypassPermissions מפעיל אישור תמידי (ראו הרשאות ובטיחות). plan מתחיל את הסשן במצב תוכנית. לדחייה כברירת מחדל השתמשו ב-defaultMode בקובץ .claude/settings.json. |
--allow <RULE> | כלל הרשאת היתר עם תבניות glob (ניתן לחזור). עובד ב-TUI וב-headless. |
--deny <RULE> | כלל הרשאת דחייה עם תבניות glob (ניתן לחזור). עובד ב-TUI וב-headless. |
--prompt-json <JSON> | פרומפט כבלוקי תוכן JSON |
--prompt-file <PATH> | פרומפט מקובץ |
--verbatim | שולח את הפרומפט בדיוק כפי שניתן |
--no-auto-update | מבטל בדיקות עדכון לסשן זה |
--sandbox <PROFILE> | פרופיל sandbox לגישה למערכת קבצים ולרשת |
הערה:
--max-turnsו---agentsהם דגלים רק ל-headless. אם משתמשים בהם ב-TUI האינטראקטיבי, מודפסת אזהרה והדגל מתעלם.--tools,--disallowed-tools,--reasoning-effort/--effort,--permission-mode,--allowו---denyעובדים בשני המצבים. לדגלים נוספים (סוכנים ו-worktrees), ראו דגלי headless נוספים.
#סינון כלים
השתמשו ב---tools כדי להגביל את הסוכן לקבוצת כלים מפורשת (רשימת היתר), או ב---disallowed-tools כדי להסיר כלים מסוימים מקבוצת ברירת המחדל (רשימת חסימה). שניהם מקבלים שמות כלים מופרדים בפסיקים.
שמות הכלים הם מזהי כלים פנימיים (לדוגמה כלי המעטפת הוא run_terminal_cmd, לא bash). בנתיב ה-daemon, גם השמות שהנתיב הזה מציג עובדים: Shell, Await, StrReplace, EditNotebook, Delete, ReadLints, SwitchMode, AskQuestion, CreatePlan, CallMcpTool, GetMcpTools, FetchMcpResource, ListMcpResources, GenerateImage, GenerateVideo, CreateGoal, UpdateGoal ו-XSearch.
StrReplace ו-EditNotebook שניהם שמות של כלי העריכה האחד, כך ש---tools EditNotebook מתיר עריכות לכל קובץ.
# Only allow read-only tools
grok -p "Explain this codebase" --tools "read_file,grep,list_dir"
# Remove web access and file editing
grok -p "Review this code" --disallowed-tools "web_search,web_fetch,write,search_replace"
# Remove shell access
grok -p "Review this code" --disallowed-tools "run_terminal_cmd"רשומת --disallowed-tools יכולה להיות גם השם שמוצג למודל, וזה מה שתמליל מציג. חמישה כלים מוצעים תחת שם שונה מהמזהה שלהם: run_terminal_cmd בתור run_terminal_command, task בתור spawn_subagent, get_task_output בתור get_command_or_subagent_output, wait_tasks בתור wait_commands_or_subagents, ו-kill_task בתור kill_command_or_subagent. כל אחד משני האיותים מסיר את הכלי בנתיב ה-shell.
בנתיב ה-daemon, רשומה לא מסירה כלום, עם אזהרה, כשלכלים שהיא מציינת אין מקבילה ב-daemon, כמו get_task_output, wait_tasks, lsp, enter_plan_mode, monitor, וכלי scheduler_*. גם workflow לא מסיר שם כלום, עם אותה אזהרה: למודל אין כלי workflow בנתיב הזה, והוא מתחיל workflows עם Execute. כיבוי workflows ([workflows] enabled = false או GROK_WORKFLOWS=0) מסיר את Execute. רשומת --disallowed-tools שמציינת סוג תת-סוכן כמו Agent(explore) לא מסירה כלום ומזהירה גם כן, אלא אם הרשימה כוללת גם Agent או Task בלבד, שכבר מסירים את כלי המשימה.
run_terminal_cmd ו-task מסירים את כלי ה-shell ואת כלי המשימה של ה-daemon.
StrReplace, EditNotebook או search_replace מסירים את כלי העריכה, ו-Delete הולך איתם. write מסיר את Write לבד. במקום שבו ה-bundle בנתיב ה-daemon לא יכול לציין את Write לבד, write מסיר את כל כלי העריכה ואת Delete איתו, כפי ש-grok -p מזהיר.
read_file ו-hashline_read, grep ו-hashline_grep, ו-search_replace ו-hashline_edit מציינים את אותו כלי קבצים, לפי ערכת כלי הקבצים שפעילה.
בנתיב ה-daemon image_edit מציין את אותו כלי תמונה כמו image_gen.
CreateGoal יוצא כש-UpdateGoal יוצא, ו-/goal הולך איתו.
UpdateGoal שנשמר נשאר, כך ש---tools CreateGoal צריך גם UpdateGoal.
הנתיב הזה גם מציג את Read, Grep, Glob, TodoWrite, WebSearch, WebFetch, ואת כלי העריכה בתור Write. רשומה של אחד מהשמות האלה לא מסירה כלום ומזהירה. השתמשו ב-StrReplace לכלי העריכה.
Await ו-kill_command_or_subagent מוסרים ברגע שכלי ה-shell וכלי המשימה שניהם הוסרו.
רשומה שלא מציינת שום כלי לא מסירה כלום, ו-grok -p מדפיס עבורה אזהרה ל-stderr.
רשומת --tools שלא מציינת שום כלי משאירה את שאר הכלים זמינים. גם הרשומה הזו מזהירה.
רשומת --tools שמציינת כלי מחוץ לערכות הכלים המובנות לא מעניקה כלום אלא אם הסוכן מוסיף אותו, ובנתיב ה-shell היא מזהירה.
ה-TUI מדפיס את האזהרות האלה ל-stderr לפני שהוא נפתח, ובאנר הפתיחה שלו גם מציג אותן מעל כל אזהרת הפעלה אחרת. ב-TUI רשימה שהוגדרה שומרת את הסשן מחוץ ל-leader המשותף. בנתיב ה-daemon אי אפשר לפתוח סשן במחשב אחר כל עוד אחת מהרשימות מוגדרת, ואי אפשר אז להתחיל או להמשיך workflow. כשהמחשב הזה הוא יעד Remote Control (/remote), תור שמכשיר שולט מתחיל כאן עוקב אחרי --tools ו---disallowed-tools של חלון grok שה-worker שלו מחזיק את החיבור.
בנתיב ה-daemon רשומת --tools שמציינת סוג תת-סוכן, כמו Agent(explore), עוצרת את grok -p ואת ה-TUI עם שגיאה.
--disallowed-tools תומך גם ברשומות Agent מיוחדות כדי לשלוט ביצירת תת-סוכנים:
| רשומה | השפעה |
|---|---|
Agent | חוסם את כל יצירת תת-הסוכנים |
Agent(explore) | חוסם רק את סוג תת-הסוכן explore |
Agent(explore, plan) | חוסם כמה סוגים מסוימים |
# Prevent the agent from spawning any subagents
grok -p "Fix this bug" --disallowed-tools "Agent"
# Block only the explore subagent
grok -p "Refactor this module" --disallowed-tools "Agent(explore)"--tools שומר את מדיניות ההזרקה של פרופיל הסוכן שנבחר: פרופילי stock מזריקים כלים אופציונליים מופעלים לפני החלת רשימת ההיתר, בעוד פרופילים curated נשארים מחמירים. ערכת הכלים הסופית שומרת את הכלים שנתבקשו ועוד כלי מטא של MCP שפועלים תמיד. כששני הדגלים נוכחים, --disallowed-tools מנצח.
#כללי הרשאות (--allow / --deny)
כללי הרשאות שולטים אם הפעלות כלים מסוימות מאושרות אוטומטית, נדחות, או דורשות אישור משתמש. בניגוד ל---disallowed-tools (שמסיר כלים לגמרי), כללי הרשאות משאירים את הכלים זמינים אבל שמים שער על ההרצה שלהם.
הכללים משתמשים בתחביר ToolPrefix(glob_pattern):
| קידומת | במה זה שולט |
|---|---|
Bash(...) | הרצת פקודות מעטפת |
Edit(...) | עריכת קבצים (glob של נתיב) |
Write(...) | כתיבת קבצים (glob של נתיב) |
Read(...) | קריאת קבצים (glob של נתיב) |
Grep(...) | פעולות חיפוש (glob של נתיב) |
WebFetch(...) | שליפת URL (glob או domain:host) |
MCPTool(...) | הפעלות כלי MCP |
לכללי נתיב (Read, Edit, Write, Grep), * הוא תו כללי ברמה אחת ו-** הוא רקורסיבי. לכללי Bash, * מתאים לכל תווים כולל רווחים. קידומת חשופה בלי סוגריים מתאימה לכל ההפעלות מהסוג הזה, ו-Bash(cmd:*) שקול להתאמת קידומת על cmd. ראו 22-permissions-and-safety.md לסמנטיקת ההתאמה המלאה.
# Deny shell commands matching "rm*"
grok -p "Clean up this project" --deny "Bash(rm*)"
# Allow npm commands, deny sudo
grok -p "Set up the project" --allow "Bash(npm*)" --deny "Bash(sudo*)"
# Allow all bash commands (auto-approve without prompting)
grok -p "Build the project" --allow "Bash"אפשר לחזור על --allow ו---deny. כללי דחייה גוברים על כללי היתר.
ערך אחד יכול גם להחזיק כמה כללים, מופרדים בפסיקים או ברווח אחרי סוגר סוגר: --deny "Bash(rm *) Edit(.env)".
כתבו ) בתוך תבנית בתור \) כדי שהוא לא ייקרא כסוף הכלל.
--allowedTools ו---allowed-tools הם כינויים של --allow.
--disallowedTools הוא כינוי של --deny.
--disallowed-tools מסיר כלים מהרשימה המוצעת (ראו סינון כלים).
#פורמטי פלט
מצב headless תומך בארבעה פורמטי פלט, שנבחרים עם --output-format.
#plain (ברירת מחדל)
טקסט קריא לאדם, מתאים להצגה ישירה או להעברה בצינור:
Here's a summary of the codebase...#json
אובייקט JSON יחיד שנפלט אחרי שהתשובה הושלמה: טקסט התשובה, סיבת עצירה, מזהה סשן, מזהה בקשה, והתיקייה שבה רץ הסשן (ועוד thought כשיש חשיבה).
כשהפרומפט הגיע למודל, אותו אובייקט נושא גם שדות הוצאה (usage, num_turns, modelUsage, עלות). stopReason הוא אסימון snake_case של ACP/Messages (end_turn, max_tokens, …).
{
"text": "Here's a summary of the codebase...",
"stopReason": "end_turn",
"sessionId": "abc123",
"requestId": "xyz789",
"cwd": "/repo",
"num_turns": 7,
"usage": {
"input_tokens": 7210,
"cache_read_input_tokens": 41000,
"cache_creation_input_tokens": 0,
"output_tokens": 1893,
"reasoning_tokens": 412,
"total_tokens": 50103
},
"modelUsage": {
"grok-4.6": {
"inputTokens": 7210,
"outputTokens": 1893,
"cacheReadInputTokens": 41000,
"modelCalls": 7,
"costUSD": 0.01268905
}
},
"total_cost_usd": 0.01268905,
"total_cost_usd_ticks": 126890500
}הערות שימוש:
usageמסכם טוקנים לפרומפט, כולל, בנתיב ה-shell, תת-סוכנים שסיימו לפני סוף התור (גם תחת מפתחותmodelUsageמשלהם). דחיסה וקריאות מודל צד אחרות לא נכללות.- שדות ההוצאה מכסים כל תור שהריצה עשתה: של הפרומפט וכל תור מאוחר יותר בסשן, כמו תור wake או תור goal. הם מצטברים כמו שני התורות של תוכנית שנשמרה (ראו תוכניות שנשמרו). בנתיב ה-daemon, ההוצאה הסוגרת מגדירה
usage_is_incompleteומשמיטה את העלות כשהיא עלולה לפספס קריאת מודל: תור מאוחר יותר הופסק או נכשל, או הסתיים לפני שהשימוש שלו הגיע לריצה. יציאה עצרה תור שהתחיל, או לא יכלה לעצור את העבודה אחרי אות או בלי ש-wait נענה. או ש-goal עדיין רץ ביציאה. בנתיב ה-shell, תור wake שעדיין רץ ביציאה נשאר בחוץ בלי הדגל הזה. בכל אחד מהנתיבים, חיבור שנסגר מגדיר את זה גם כן. - מדיניות שדות טוקנים (תוצאת headless /
end/ הוצאת שגיאה):usage.input_tokensו-modelUsage.*.inputTokensהם רק לא מהמטמון.cache_read_input_tokens/cacheReadInputTokensהם פגיעות מטמון.total_tokensהוא קלט מלא + פלט (כולל את שני דליי המטמון):total_tokens = input_tokens + cache_read_input_tokens + cache_creation_input_tokens + output_tokens._meta.usage.inputTokensשל ACP (PromptUsage) עדיין הסכום המלא של הפרומפט. רק המקרן של headless מחסיר מטמון. העדיפו שדות headless לאוטומציה של הוצאה.
num_turnsסופר סבבי מודל של הסוכן הראשי שנרשמו בפנקס הפרומפט (סבבי לולאת כלים שדיווחו שימוש). קריאות דגימה של תת-סוכן לא מגדילות אותו. ספירות קריאות לפי מודל (כולל תת-סוכנים) נשארות ב-modelUsage.*.modelCalls. זו אותה משפחת מונים כמו--max-turns, בלי הבטחה לשוויון מדויק כשסבבים חסרי שימוש או נתקלים בשערים.total_cost_usdמופיע רק כשהשרת דיווח עלות מלאה. היעדר פירושו שלא דווח או לא שלם, אף פעם לא בחינם. עלות מוחתמת היום לתעבורת מפתח API. נתיבי pool/OAuth לעתים קרובות משמיטים אותה עד שהשרת מחתים עלות. כשחלק מהקריאות חסרו עלות,cost_is_partialהוא true וכל מספרי העלות הצפים מושמטים (total_cost_usdוכלmodelUsage.*.costUSD) כדי שצרכנים לא יוכלו לסכם שורות מודל לחשבון מלא מזויף.total_cost_usd_ticksהוא אותו ערך בטיקים שלמים מדויקים (1 USD = 10^10 טיקים) ומופיע באותם תנאים. השתמשו בו להתאמת חיוב: סיכום טיקים לפי הפעלה תואם בדיוק לייצוא השימוש של השרת, דבר שדולרים צפים לא יכולים להבטיח.- כשלא היה אפשר להחיל שימוש של תת-סוכן, שימוש של תת-סוכן מקונן היה לא שלם, או שפקיעת הניקוז בנתיב ההצלחה חלה (עד 120 שניות במשימת התור),
usage_is_incompleteהוא true ומספרי עלות צפים מושמטים באותו אופן (סכומי טוקנים עלולים לספור תת-סוכנים בחסר). ביטול מצלם בלי הניקוז הארוך הזה ומסמן לא שלם בזמן שתת-סוכנים עדיין חיים. לא שלם בלי טוקנים שנרשמו פולט רקusage_is_incomplete(בלי אובייקטusageאפס). - פרומפט שמעולם לא הגיע למודל לא מוסיף הוצאה. השדות מושמטים אלא אם תור אחר או סיבה מהאמור למעלה מגדירים אותם.
השדה sessionId שימושי להמשך השיחה אחר כך.
cwd הוא התיקייה שבה רץ הסשן.
עם --worktree NAME, הוא בתוך ה-worktree החדש.
כשקיים worktree בשם NAME, Grok יוצא עם שגיאה שמציינת את התיקייה שלו ולא יוצר כלום.
NAME שהתיקייה שלו נמחקה ידנית משמש שוב, אלא אם git עדיין מחזיק את הרישום שלו. השגיאה אז אומרת איך לשחרר אותו.
במאגר jj, NAME תפוס עדיין הופך ל-NAME-2, NAME-3, וכן הלאה.
בכישלון, Grok פולט אובייקט שגיאה (יציאת תהליך שונה מאפס). כשלונות ברמת הפרומפט יכולים גם לכלול שדות הוצאה קפואים כשנרשם שימוש:
{"type":"error","message":"Couldn't start session: ..."}אחרי שהסשן התחיל, אובייקט השגיאה נושא גם cwd.
#streaming-json
JSON מופרד בשורות חדשות, אובייקט אחד עם תגית type בכל שורה, נגזר מעדכוני סשן ACP של הסוכן. שמות שדות עלה (toolCallId, kind, rawInput, rawOutput) עוקבים אחרי ACP. toolName ושורה usage הן תוספות של xAI. צורכים את זה על ידי מעבר על type.
{"type":"thought","data":"Analyzing the directory structure..."}
{"type":"tool_call","toolCallId":"call_1","title":"Read","kind":"read","status":"in_progress","toolName":"read_file","rawInput":{"path":"src/main.rs"},"content":[],"locations":[]}
{"type":"tool_call_update","toolCallId":"call_1","status":"completed","content":[],"rawOutput":{"lines":42},"locations":[]}
{"type":"text","data":"Here's a summary"}
{"type":"usage","messageId":"resp_1","stopReason":"end_turn","usage":{"input_tokens":812,"output_tokens":45,"cache_read_input_tokens":0,"cache_creation_input_tokens":0,"reasoning_tokens":0},"signature":"..."}
{"type":"end","stopReason":"end_turn","sessionId":"abc123","requestId":"xyz789","cwd":"/repo","usage":{...},"num_turns":7,"modelUsage":{...}}סוגי אירועים:
| Type | Description |
|---|---|
text | מקטע מטקסט התשובה של הסוכן |
thought | נימוק פנימי (טוקני חשיבה) |
tool_call | קריאת כלי שהסוכן התחיל (toolCallId, toolName, kind, status, rawInput, content, locations) |
tool_call_update | התקדמות או תוצאה לקריאת כלי (status, rawOutput, content, locations) |
usage | גבול לכל תשובה (messageId, stopReason, usage, signature), אחד לכל תשובת מודל. במסלול הדמון, אחד לכל תור, כולל תורות של תת סוכן, עם usage הכולל של אותו תור בלבד |
plan | התוכנית הנוכחית של הסוכן (entries) |
available_commands | רשימות כלים ופקודות סלאש (tools, commands) |
end | האירוע הסופי עם תיקיית הסשן, מטא נתונים, ושדות הוצאה כשהם זמינים |
error | אירעה שגיאה (נושא message, את תיקיית הסשן אחרי שהסשן התחיל, ושדות הוצאה אם יש) |
end הוא האירוע האחרון, או error כשההרצה נכשלת, גם כש SIGINT או SIGTERM עוצרים את ההרצה אחרי שהפרומפט שלה נשלח (ראו Interrupted Headless Runs). שדות ההוצאה ב end תואמים לצורת אובייקט ה json (input_tokens לא במטמון ב snake_case, מספרים עשרוניים בטוחים לעלות). end.stopReason הוא סיבת עצירת התור ב snake_case (end_turn, max_tokens, max_turn_requests, refusal, cancelled). הסיבה המילולית לכל תשובה מהספק (למשל tool_use, pause_turn) נמצאת ב stopReason של שורת usage. message_id/stopReason/signature לכל תשובה מאוכלסים בבקאנד של Messages API. בקאנדים אחרים מדווחים את מה שהם נושאים.
ההוצאה של end כבר מסכמת כל תור שההרצה ביצעה, אז אל תוסיפו אליה את שורות usage. ל text של תור מאוחר יותר, כמו תשובה של תור התעוררות, אין מעבר פסקה משלו, ושורה usage לא מסמנת איפה הוא מתחיל: תורות של תת סוכן שולחים גם שורות usage, ורק end של מסלול המעטפת סופר אותן.
גרוק עשוי גם לפלוט אירועי max_turns_reached ו auto_compact_*. התייחסו לרשימה כלא ממצה ועברו על type.
#streaming-messages-json
JSON מופרד בשורות חדשות בפורמט החוט stream-json של Messages API. המשטח שנושא נתונים תואם במדויק לצורת Messages. זה כולל גופי הודעות assistant/user, usage, tool_use/tool_result, חיפוש ווב מוטמע, stop_reason, ומסגור האירוע --include-partial-messages. צרכן שבונה מחדש הודעות, קורא הוצאה, או מזהה שגיאות עובד בלי שינויים.
שורות system/init ושורה סופית result נושאות מטא נתונים. גרוק פולט את השדות שיש לו עבורם נתונים אמיתיים ומשמיט שדות שהם placeholder טהור שאי אפשר למלא, במקום למלא אותם באפס. כתוצאה, שתי השורות האלה עשויות לא לעבור אימות סכמה מחמיר של init/result. השדות הבודדים מפורטים למטה. קראו את הערות הנאמנות לפני שמתייחסים לשדה כלשהו כסמכותי. לזרם xAI מקורי נקי בלי צורת placeholder, השתמשו ב streaming-json.
הזרם נפתח בשורת system/init, ואז הודעות assistant ש message.content[] שלהן מחזיק בלוקי text, thinking, ו tool_use, הודעות user שנושאות בלוקי tool_result, ו result סופי:
{"type":"system","subtype":"init","session_id":"abc123","apiKeySource":"user","model":"grok-4.6","cwd":"/repo","permissionMode":"default","tools":["read_file","bash"],"slash_commands":["review"],"mcp_servers":[{"name":"linear","status":"connected"}],"skills":[],"uuid":"..."}
{"type":"assistant","message":{"id":"msg_0","type":"message","role":"assistant","model":"grok-4.6","content":[{"type":"text","text":"Let me read the file."},{"type":"tool_use","id":"call_1","name":"read_file","input":{"path":"src/main.rs"}}],"stop_reason":"tool_use","stop_sequence":null,"usage":{...}},"parent_tool_use_id":null,"session_id":"abc123","uuid":"..."}
{"type":"user","message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"call_1","content":"fn main() {}","is_error":false}]},"parent_tool_use_id":null,"session_id":"abc123","uuid":"..."}
{"type":"result","subtype":"success","is_error":false,"duration_ms":0,"duration_api_ms":0,"num_turns":7,"result":"Here's a summary...","stop_reason":"end_turn","total_cost_usd":0.0127,"usage":{"input_tokens":812,"output_tokens":210,"cache_read_input_tokens":0,"cache_creation_input_tokens":0,"server_tool_use":{"web_search_requests":0}},"modelUsage":{},"session_id":"abc123","uuid":"..."}סוגי הודעות:
| Type | Description |
|---|---|
system | הקדמת סשן (subtype: "init") עם מודל, cwd, מצב הרשאה, כלים, פקודות סלאש, ושרתי MCP. subtype: "compact_boundary" מסמן דחיסה אוטומטית |
assistant | הודעת מודל. message.content[] מחזיק text/thinking/tool_use, ובנוסף server_tool_use/web_search_tool_result לחיפוש ווב מוטמע של הבקאנד |
user | תוצאות כלים, כבלוקי tool_result בתוך message.content[] |
result | הודעה סופית עם הטקסט הסופי, סיבת עצירה, ושדות הוצאה |
הודעות assistant ו user נושאות session_id, uuid, ו parent_tool_use_id (null לשיחה הראשית). שורות system/init ו result הסופי נושאות session_id ו uuid אבל בלי parent_tool_use_id.
ה uuid בכל שורה נוצר מחדש לכל שורה שנפלטת. הוא לא מזהה ספק, הודעה, או אירוע, ולא מפתח מתאם. הוא לא תואם ל message.id של הספק (הערך הזה נוסע ב assistant.message.id). הוא ייחודי לכל שורה, גם לשורות שמתארות את אותה הודעה, והוא לא נושא זהות בין שורות או בין הרצות. אל תשתמשו בו למתאם או להסרת כפילויות.
מקטעי טקסט ונימוק מקובצים להודעת assistant אחת לכל תשובת מודל. בלוקי tool_result מקבילים של תשובה מקובצים להודעת user אחת. result.result הוא טקסט הודעת ה assistant הסופית. תשובת מודל שלא מייצרת בלוקי תוכן לא פולטת שורת assistant במצב ברירת המחדל. רק --include-partial-messages חושף תשובה כזו, כמעטפת ריקה message_start … message_stop.
ב init, skills חי. הוא מפרט את שמות הכישורים שהמשתמש יכול להפעיל בסשן, תת קבוצה של slash_commands שמקורה בפקודות שהסשן מפרסם, או [] כשהסשן לא מציג כישורים. שורת init נפלטת פעם אחת, נדחית עד שורת הפלט הראשונה כדי שתתפוס את tools, slash_commands, ו skills שהסשן מפרסם. סכמת Messages לא מגדירה init שני, אז רשימת פקודות שמשתנה אחרי שהסטרימינג מתחיל לא מפורסמת מחדש.
שאר שדות init נושאים נתונים אמיתיים:
apiKeySourceהואuserלאימות מפתח API וoauthאחרת. גרוק לא מבדיל בין מקורות הסכמהproject,org, וtemporary.permissionModeהוא מצב ה headless האפקטיבי שממופה ל enum של Messages: הערך של--permission-mode, אוbypassPermissionsתחת--yolo, אחרתdefault. מצבים של גרוק בלבד כמוautoמתכווצים לdefault.mcp_servers[].statusהוא צילום אחד שלx.ai/mcp/list, שנפלט רק לstreaming-messages-json:connected,failed,needs-auth,pending, אוdisabled. שרתים שעדיין ב handshake הםpending.disabledנחתם רק אחרי שהסשן מדווחsessionMcpResolved. שורת רשימה שלא נפתרה היאpendingגם כשenabledעדיין false. הצילום לא מחכה לחסד ההפעלה Blocking. החסד הזה עדיין חל על ערכת הכלים של הפרומפט. פורמטי פלט אחרים משמיטים את המערך ולא קוראים לx.ai/mcp/list.
גרוק משמיט את שדות init שהם placeholder טהור בסכמה ואין לו נתונים עבורם, במקום לפלוט ערכי דמה: claude_code_version, output_style, ו plugins.
result כולל duration_ms, duration_api_ms, num_turns, stop_reason, total_cost_usd, usage (צורת message.usage של Messages API), ו modelUsage. הוא כולל גם errors[] בתתי סוגי השגיאה. גרוק משמיט את permission_denials של הסכמה, שתמיד ריק, כי הוא לא אוסף סירובי הרשאה. structured_output (עם --json-schema) הוא snake_case, בהתאם לסכמה.
model מופיע ב init ובכל מסגרת assistant. זה מזהה המודל האמיתי כשהוא ידוע, והמילולי "unknown" רק כשאף מודל לא ידוע בזמן הפליטה.
stop_sequence של מסגרת ה assistant מחובר מקצה לקצה. הוא נושא את רצף העצירה התואם של הספק כשהמודל עצר על רצף מוגדר (stop_reason: "stop_sequence"), והוא null בכל סיבת עצירה אחרת ובכל בקאנד אחר. במסגור --include-partial-messages, הרצף התואם נוסע גם במסגרת assistant שנשטפה וגם ב message_delta.stop_sequence החלקי, כך שבנייה חלקית מחדש תואמת למסגרת. רק message_start.stop_sequence החלקי נשאר null, כי הרצף התואם לא ידוע בפתיחת ההודעה.
תתי סוגי השגיאה שנפלטים הם error_max_turns, error_during_execution, ו error_max_structured_output_retries. תת הסוג error_max_budget_usd של הסכמה אף פעם לא נפלט, כי לגרוק אין תכונת תקציב. הרצה ש SIGINT או SIGTERM עוצרים אחרי שהפרומפט שלה נשלח מסתיימת ב result מתת סוג error_during_execution, עם stop_reason cancelled ו errors ["cancelled"], או, אחרי פרומפט שנכשל, בתוצאת השגיאה של אותו פרומפט. תוצאה אחרת שהוכרעה לפני האות, כמו error_max_turns או תור שנעצר בגלל refusal או max_tokens, שומרת את ה result שלה (ראו Interrupted Headless Runs).
result.usage מדווח את צורת message.usage של Messages עם שלושת דליי הטוקנים זרים זה לזה: input_tokens (לא במטמון), cache_read_input_tokens, ו cache_creation_input_tokens. גרוק גוזר אותם מפנקס המצטבר של כל תור שההרצה ביצעה, כמו ש end עושה, ומעצב אותם מחדש לדליים האלה. יצירת מטמון של תת סוכן כלולה ב cache_creation_input_tokens. הפנקס המצטבר עוקב אחריה כדלי משלה, אז היא כבר לא מקופלת לתוך input_tokens.
result.usage תמיד פולט דליים מספריים, גם כשחסרים נתונים. זה קורה כשפנקס ה usage של התור לא שלם (אותו תנאי שחושף usage_is_incomplete בפורמט json), או כשאף פנקס מצטבר לא הגיע ל reducer בכלל. כל דלי שגרוק לא יכול לחשב נופל חזרה ל 0, כי לסכמת Messages API אין סמן ל usage לא שלם או חסר. ה reducer רושם אזהרה ל stderr בשני המקרים. קראו usage שכולו אפסים כאן כ "unknown", לא כ "free".
מונה server_tool_use המקונן מאוכלס. web_search_requests הוא מספר חיפושי הווב המוצלחים של הבקאנד שנפלטו בהרצה הזו. חיפושים שנכשלו ופעולות WebSearch שאינן חיפוש כמו open_page לא נכללים, בהתאם ל Messages API, שלא מחייב חיפושים ששגו. חיפוש בקאנד שנכשל עדיין פולט web_search_tool_result בצורת שגיאה (content.type: "web_search_tool_result_error"), אבל לא נספר. error_code שלו הוא placeholder קבוע "unavailable", לא קוד שהועבר מהבקאנד. אין מפתח web_fetch_requests, כי לגרוק אין web_fetch בצד השרת, אז ה placeholder מושמט.
חיפוש ווב של הבקאנד הוא מוטמע. הוא מתקפל לאותה מסגרת assistant כמו הטקסט שמסביב. המסגרת נושאת בלוק server_tool_use (name: "web_search", input.query) ומיד אחריו בלוק web_search_tool_result. tool_use_id של בלוק התוצאה תואם ל server_tool_use.id, ו content שלו הוא מערך פגיעות web_search_result של {type, url, title}. זה תואם לצורת כלי השרת המוטמע של Messages API, במקום לפצל את התשובה בין מסגרות.
חיפוש X ומפרש קוד הם סטייה מתועדת. הם נשארים גנריים, מוצגים כבלוק tool_use של הלקוח ועוד tool_result של user, כי Messages API לא מגדיר סוג בלוק מוטמע עבורם. כל כלי לקוח אחר שומר באותו אופן על הפיצול tool_use/tool_result.
--include-partial-messages פולט את מסגור האירוע הגולמי כדי שצרכן יוכל לבנות מחדש כל הודעה עם צובר הסטרימינג של Messages. המסגור הוא message_start, content_block_start/content_block_delta/content_block_stop, message_delta, ו message_stop. הוא נושא את האירועים המבניים שצובר צריך. הדלתאות גסות יותר מסטרימינג ברמת טוקן של Messages API: קלט כלי מגיע כ input_json_delta יחיד, ו citations_delta אף פעם לא מיוצר (ראו למטה). התוצאה היא שחזור נאמן של כל הודעה, במקום השמעה מחדש טוקן אחרי טוקן.
בבקאנד של Messages API, המסגור נאמן. message_start נושא את message.id האמיתי של הספק ואת usage בצד הקלט. בלוק thinking פולט את signature_delta שלו לפי הסדר, לפני content_block_stop של הבלוק. צד הקלט של message_start.usage מדווח את כל שלושת דליי צד הפרומפט הידועים בפתיחת ההודעה: input_tokens (החלק שלא במטמון), cache_read_input_tokens, ו cache_creation_input_tokens. פגיעת מטמון גלויה אפוא ב message_start, במקום להופיע רק מאוחר יותר ב message_delta/result. output_tokens נזרע שם כ 0 ומסוכם סופית ב message_delta. תשובה שמתחילה אבל לא מייצרת תוכן עדיין פולטת את המעטפת message_start … message_stop בלי בלוקי תוכן.
חלק מהבקאנדים חושפים מטא נתונים לכל תשובה רק בסוף התור. הבקאנדים האלה נופלים חזרה ל message_start.id מסונתז ול usage קלט שנזרע באפס. הם דוחים את signature של הנימוק לשורת assistant הסופית, שהיא הסמכותית במקרה הזה.
קלט של קריאת כלי נפלט כ input_json_delta יחיד שנושא את JSON הארגומנטים המלא, ואחריו content_block_stop. זה לא רצף של מקטעים ברמת טוקן. זו סטייה מכוונת מסטרימינג partial_json המצטבר של Messages API. מסלול קריאת הכלים ACP של גרוק מוסר כל קריאת כלי כאובייקט JSON מאומת אחד אחרי שהארגומנטים מפורשים במלואם, אז דלתא יחידה היא הייצוג המדויק. צרכן שמשרשר partial_json מרכיב מחדש את אותו אובייקט בכל מקרה. input.query של בלוק server_tool_use לחיפוש ווב של הבקאנד נפלט באותו אופן, כ input_json_delta אחד.
citations_delta של Messages API נושא ציטוטים מוטמעים לטווחי טקסט מצוטטים, כמו אלה מחיפוש ווב. הזרם הזה לא מייצר אותו. דלתאות התוכן של Messages של גרוק מוגבלות לטקסט, thinking, signature, ו JSON של קלט כלי, אז אין נתוני ציטוט לחשוף כ citations_delta. כתובות URL של מקורות חיפוש ווב של הבקאנד מדווחות מוטמעות בבלוק web_search_tool_result שהושלם (ראו למעלה), לא כציטוטי טקסט לכל טווח.
הערות נאמנות חלות על כמה שדות.
duration_ms הוא שעון הקיר של ביצוע הפרומפט. duration_api_ms הוא סכום זמן המודל המדווח לכל קריאה. קריאת מודל שלא מדווחת את משך הזמן שלה תורמת 0, אז duration_api_ms יכול לספור בחסר את זמן ה API האמיתי.
num_turns ו total_cost_usd סמכותיים כשהם ידועים. כשהם לא, num_turns נופל חזרה לספירת תשובות המודל שהושלמו בתור הזה, ו total_cost_usd נופל חזרה ל 0. תשובה שהושלמה אבל בלי תוכן לא פולטת שורת assistant, ועדיין נספרת כתור. הוצאה אף פעם לא מדווחת ביתר.
modelUsage נושא את שדות הטוקנים והעלות לכל מודל שגרוק עוקב אחריהם, ועוד webSearchRequests שמיוחסים למודל הפעיל. ה reducer עוקב אחרי ספירת חיפוש ווב גלובלית אחת, במקום לכל מודל, אז כל הספירה נוחתת על המודל הנוכחי או האחרון ושורות אחרות נשארות 0. modelUsage.*.costUSD לכל מודל הוא 0 כשעלות המודל הזה לא ידועה או מוסתרת. זו אותה התנהגות של כשל סגור לאפס כמו total_cost_usd ברמה העליונה. פורמט json משמיט לגמרי מספרים עשרוניים של עלות כשהם חלקיים, אבל הזרם הזה שומר את השדה נוכח ו 0. contextWindow הוא חלון ההקשר הכולל האמיתי של המודל הנוכחי (אותו ערך שגרוק משתמש בו לדחיסה אוטומטית), והוא מופיע רק בשורת המודל הנוכחי. שורות אחרות משמיטות אותו, וכך גם השורה הנוכחית כשהחלון לא ידוע. ל maxOutputTokens אין קטלוג של גרוק, אז המפתח הזה מושמט לגמרי. modelUsage הוא {} כשאין פירוט לכל מודל.
כמו streaming-json, הזרם הזה הוא לקריאה בלבד. אישורי כלים וזרימות דו כיווניות אחרות משתמשים בממשק ACP (grok agent).
#ניהול סשנים במצב headless
כברירת מחדל, כל הפעלה של grok -p יוצרת סשן חדש. כדי לשמור הקשר בין קריאות, השתמשו בדגלי סשן.
#סשנים עם שם (-s)
כדי לשאת הקשר בין קריאות headless, השתמשו ב--r/--resume או ב--c/--continue. השתמשו ב--s/--session-id רק עבור סשן חדש עם UUID (שגיאה אם זה לא UUID או אם הוא כבר בשימוש תחת תיקיית היעד). התנהגות ה-upsert/resume הנסתרת הישנה של -s אינה קיימת עוד. השתמשו ב--r/-c כדי להמשיך. עם -r/-c, -s דורש --fork-session:
# Start a headless session and capture its ID
grok -p "Review the changes in this PR" --output-format json | jq -r '.sessionId'
# Continue in the same session
grok -p "Now check for security issues" --resume "<id>"
# Optional: create with a client-chosen UUID (must not already exist)
grok -p "hello" --session-id "$(uuidgen | tr '[:upper:]' '[:lower:]')" --output-format jsonהערה:
-s/--session-idיוצר סשן חדש בלבד (UUID תקין, שגיאה אם כבר בשימוש). השתמשו ב--rכדי לחדש.
#חידוש (-r)
דגל -r/--resume מחדש סשן מסוים לפי מזהה, או לפי כותרת עבור התיקייה הנוכחית כאשר הערך אינו מזהה, תוך התעלמות מרישיות האותיות (בלי התאמה מדויקת, אותן מילים שמחוברות על ידי מפרידים כלשהם גם מתאימות, כך ש-fix-login-bug מוצא את "Fix login bug". התאמה יחידה ששמה שונה ידנית גוברת בין כפילויות. כפילויות שנותרות מחזירות שגיאה עם עד עשר מהן, החדשות קודם, עם מזהים וזמני עדכון אחרונים. החמצה מציעה עד חמש כותרות דומות עם המזהים שלהן. ערכים בצורת UUID תמיד הולכים בנתיב המזהה, ולכן סקריפטים צריכים להעדיף מזהים). מתקבלת שגיאה אם הסשן אינו קיים:
# Get the session ID from a previous JSON response
grok -p "Remember: the secret number is 42" --output-format json
# Output includes "sessionId": "abc123"
# Resume that exact session
grok -p "What's the secret number?" --resume abc123#המשך (-c)
דגל -c/--continue ממשיך את הסשן האחרון בתיקיית העבודה הנוכחית:
grok -p "Continue where we left off" -c#חילוץ מזהי סשן
השתמשו ב---output-format json ונתחו את השדה sessionId:
grok -p "Hello" --output-format json | jq -r '.sessionId'#צינורות קלט ופלט
מצב headless עובד באופן טבעי עם צינורות Unix והפניה.
#פלט סטנדרטי
# Pipe output to a file
grok -p "Generate a README" > README.md
# Parse JSON output with jq
grok -p "List files" --output-format json | jq -r '.text'#קלט סטנדרטי
מצב headless אינו קורא stdin מצינור אל תוך הפרומפט. העבירו תוכן חיצוני דרך החלפת פקודה או --prompt-file:
# Include git diff as context via command substitution
grok -p "Write a concise commit message for these changes:
$(git diff --staged)"
# Or read the prompt from a file
grok --prompt-file ./prompt.txt#דוגמאות שילוב CI/CD
#סקירת קוד אוטומטית
grok -p "Review changes for bugs and security issues." \
--output-format json --yolo | jq -r '.text' > review.md#הוק Pre-Commit
grok -p "Review staged changes for obvious bugs. Reply OK if fine, or list issues." \
--yolo --output-format json | jq -r '.text' | grep -q "^OK" || exit 1#עיבוד באצווה
for file in src/*.js; do
grok -p "Migrate $file from CommonJS to ES modules." --yolo
done#דפוסי סקריפטים
#מעטפת Python
אפשר לעטוף את מצב ה-headless של Grok כ-API להשלמת צ'אט תואם OpenAI:
import asyncio
import json
import os
class GrokChat:
"""Simple OpenAI-compatible wrapper using headless mode."""
def __init__(self, cwd="."):
self.cwd = cwd
self.env = {**os.environ}
def _build_cmd(self, prompt, model, stream):
return ["grok", "-p", prompt, "-m", model, "--cwd", self.cwd,
"--output-format", "streaming-json" if stream else "json",
"--yolo"]
async def create(self, messages, model="grok-4.6", stream=False):
prompt = messages[-1]["content"] if len(messages) == 1 else "\n".join(
f"{m['role']}: {m['content']}" for m in messages
)
cmd = self._build_cmd(prompt, model, stream)
if stream:
return self._stream(cmd)
proc = await asyncio.create_subprocess_exec(
*cmd, env=self.env, stdout=asyncio.subprocess.PIPE
)
stdout, _ = await proc.communicate()
data = json.loads(stdout.decode()) if stdout else {"text": ""}
return {
"choices": [{
"message": {"role": "assistant", "content": data.get("text", "")},
"finish_reason": "stop"
}]
}
async def _stream(self, cmd):
proc = await asyncio.create_subprocess_exec(
*cmd, env=self.env, stdout=asyncio.subprocess.PIPE
)
async for line in proc.stdout:
if not line.strip():
continue
event = json.loads(line)
if event.get("type") == "text":
yield {"choices": [{"delta": {"content": event["data"]}}]}
elif event.get("type") == "end":
yield {"choices": [{"delta": {}, "finish_reason": "stop"}]}
async def main():
client = GrokChat(cwd=".")
response = await client.create(
[{"role": "user", "content": "What files are here?"}]
)
print(response["choices"][0]["message"]["content"])
asyncio.run(main())#סקריפט Shell
#!/bin/bash
# Run a code review and exit with failure if issues are found
RESULT=$(grok -p "Review this PR for bugs. Output JSON with 'issues' array." \
--output-format json --yolo | jq -r '.text')
ISSUE_COUNT=$(echo "$RESULT" | jq '.issues | length' 2>/dev/null || echo "0")
if [ "$ISSUE_COUNT" -gt 0 ]; then
echo "Found $ISSUE_COUNT issues"
echo "$RESULT" | jq '.issues[]'
exit 1
fi
echo "No issues found"#אישור תמידי לאוטומציה
--always-approve (כינוי --yolo, זהה ל---permission-mode bypassPermissions) מריץ קריאות כלים בלי בקשות הרשאה אינטראקטיביות. כללי דחייה, הוקים ונעילות מנהל עדיין חלים (ראו הרשאות ובטיחות).
בלי אישור תמידי, כולל --permission-mode plan, קריאת כלי שצריכה אישור נדחית, מכיוון שהרצה headless אינה יכולה לשאול, ו-stderr מציין את הקריאה פעם אחת: permission required: Edit calc.py was not run, as a headless run cannot ask for approval. ההערה מודפסת רק כאשר ההרצה מקבלת בקשת אישור, ודחיות במצב plan קורות לפני כן. קוד היציאה נשאר 0.
בנתיב ה-daemon, אישור שקריאת host של הרצת workflow ברקע צריכה מוכרע באותו אופן. בזמן שהרצה headless ממתינה לעבודה ברקע אחרי התור שלה, stderr מציין את ה-workflow: permission required: Edit REVIEW.md (workflow "review") was not run, as a headless run cannot ask for approval. בזמן שהתור רץ, האישור של ההרצה נשאל באותו תור כמו קריאות הכלים של התור עצמו, והשורה מציינת רק את הקריאה. ההרצה מקבלת את הדחייה, והרצה שנכשלת אחר כך קובעת את קוד היציאה (ראו Workflows ועבודה ברקע).
grok -p "Format all files" --always-approve
grok -p "Run the tests and fix any failures" --cwd ~/projects/my-app --always-approveעבור שרתי סוכן ו-SDK, ראו מצב סוכן.
#תוכניות שנשמרו בנתיב ה-daemon
בנתיב ה-daemon, תור grok -p ששומר תוכנית שולח המשך אחד שמבקש מהסוכן לבנות את התוכנית. ההמשך נשלח כאשר התור מסתיים ב-end_turn. --permission-mode plan, --json-schema ו---max-turns אינם שולחים המשך, והתוכנית שנשמרה נשארת לא בנויה. תוכנית שנשמרת במהלך הבנייה נשארת לא בנויה.
כאשר ההמשך נשלח, usage של התוצאה מסכם את שני התורות ו-requestId מציין את תור הבנייה. התוצאה כוללת עלות כאשר שני התורות מציינים אחת ואף חשבון אינו חלקי או לא שלם. כאשר תור אחד מציין עלות והאחר חייב טוקנים ולא ציין אף אחת, או שאחד החשבונות כבר חלקי, התוצאה משמיטה את העלות ומגדירה cost_is_partial. כאשר שני התורות חייבו טוקנים ואף אחד לא ציין עלות, ואף חשבון אינו חלקי או לא שלם, התוצאה משמיטה את העלות ואינה מגדירה אף דגל. חשבון לא שלם משמיט את העלות ומגדיר usage_is_incomplete. חשבון תכנון שנרשם משמיט את העלות ומגדיר usage_is_incomplete כאשר תור הבנייה לא מביא חשבון. תור תכנון שלא מביא חשבון משאיר את התוצאה לא שלמה, בלי עלות, בין אם תור הבנייה מביא חשבון ובין אם לא. כאשר אף תור לא מביא חשבון, התוצאה מגדירה usage_is_incomplete ואין לה עלות. התוצאה משמיטה את num_turns כאשר רק תור אחד מציין אותו. מודל שרק תור אחד השתמש בו שומר את ספירת הקריאות של אותו תור. מודל ששני התורות השתמשו בו שומר ספירת קריאות רק כאשר שני התורות ציינו אחת.
#משתני סביבה ל-headless
משתני סביבה מרכזיים שמשפיעים על מצב headless:
| משתנה | תיאור |
|---|---|
XAI_API_KEY | מפתח API לאימות (נדרש כאשר אין התחברות בדפדפן) |
GROK_HOME | דריסת תיקיית ההגדרות (ברירת מחדל: ~/.grok) |
GROK_LOG_FILE | נתיב לקובץ לוג (בשימוש כלשונו כנתיב, עובד ב-headless וב-TUI, מכבד את RUST_LOG) |
RUST_LOG | מסנן רמת לוג (לדוגמה debug). לוגים של headless נכתבים ל-stderr. |
עבור סביבות CI בלי גישה לדפדפן, הגדירו את XAI_API_KEY עם מפתח API מ-console.x.ai:
export XAI_API_KEY="xai-..."
grok -p "Run the test suite" --yolo#Workflows ועבודה ברקע
הרצה headless ממתינה לעבודה ברקע שהיא התחילה לפני שהיא יוצאת, עד 10 דקות אחרי שתור אחרון מסתיים. --background-wait-timeout <secs> קובע מגבלה אחרת. כאשר משימת shell ברקע או subagent מסתיימים, המודל מקבל את התוצאה בתור חדש (תור auto-wake, או תור ההתראה ש-idle drain מריץ כאשר auto-wake כבוי). ההרצה headless מדפיסה את התשובה הזו אחרי הטקסט של התור הראשון, כפסקה משלה, וממתינה שהתור הזה יסתיים. מגבלת ההמתנה אף פעם לא קוטעת תור שרץ באמצע: היא נספרת מסוף התור האחרון וחלה רק בזמן שההרצה ממתינה לעבודה ברקע.
עבודה שעדיין רצה כאשר ההמתנה נגמרת נעצרת, וההרצה אז יוצאת 1 ואומרת מה היא עצרה, כגון the wait for background work ran out after 600 s and exit stopped 1 background task (…). העבירו --no-wait-for-background כדי לצאת ברגע שהתור הראשון מסתיים. היציאה עוצרת את העבודה שעדיין רצה. בנתיב ה-daemon, ההרצה אז מפרטת ב-stderr מה היא עצרה: שורה לכל הרצת workflow, כגון workflow slow (run wf_…) was still running at exit and was stopped: the run did not wait …, ואז אחת לשאר, כגון the run did not wait for background work (--no-wait-for-background) and exit stopped 1 other background job. היא לא מדפיסה כלום כאשר שום דבר לא רץ, ומציינת זאת כאשר היציאה לא יכלה לעצור את העבודה, או לא יכלה לקרוא את הרצות ה-workflow מבין מה שהיא עצרה. העבודה הזו התבקשה ואינה משנה את קוד היציאה: לא נכתב אירוע error, ושורת הסגירה שומרת את סיבת העצירה של התור.
--background-shell-timeout <secs> מגביל רק shells ברקע, ורק כאשר הסשן רץ על ה-daemon: ברגע ש-shells רצו כל כך הרבה אחרי שהתור האחרון הסתיים, בלי תור חדש או משימת רקע שהסתיימה מאז, ההרצה עוצרת אותם בלי להתחיל תור עבורם וממשיכה להמתין ל-subagents ברקע בלי מגבלה. ההרצה לא נכשלת בגלל ה-shells שנעצרו. זה מחליף את מגבלת ההמתנה הכוללת, ולכן אי אפשר לשלב אותו עם --background-wait-timeout, והוא לא מגביל את ההמתנה על מטרה פעילה.
בנתיב ה-daemon, הרצת workflow היא גם עבודה ברקע. כאשר הרצה שהמודל התחיל ברקע מסתיימת, המודל מקבל את תוצאת ההרצה בתור חדש. ההרצה headless מדפיסה את התשובה הזו אחרי הטקסט של התור הראשון, ואז יוצאת. עם --output-format json, התשובה היא חלק מ-text. ההמתנה גם מסתיימת כאשר העבודה ברקע היחידה שנשארה היא הרצות שהושהו בתקציב הסוכן שלהן: הרצה headless אינה יכולה להעלות תקציב. היציאה אז עוצרת את ההרצות האלה, כך שחידוש מאוחר יותר גם אינו יכול להעלות אותו.
פקודות workflow עובדות בהרצה headless בנתיב ה-daemon:
grok -p '/<name> [--agent-budget N] [args]'ו-grok -p '/workflow <name> [args]'מתחילים workflow מהרשימה ישירות, עם התקציב שאתם מעבירים, וממתינים לו כמו לעיל. התחלה שנדחית, כגון/deep-researchבלי שאילתה, מכשילה את ההרצה headless עם הסיבה. workflow מסוג.rhaiשה-daemon אינו יכול להתחיל עדיין עובר למודל כטקסט, ו-/workflow listמציין זאת.grok -p '/workflow list'ו-grok -p '/workflow help'עונים מיד, בלי תור מודל. כך גם/workflow runs,stopו-resume, אבל כל הרצה headless מתחילה daemon משלה, ולכן הן לא רואות הרצה של הרצה headless קודמת. פקודת/workflowשנכשלת, כגוןstopבלי הרצה לעצור, מכשילה את ההרצה headless עם הסיבה.- בתיקייה שלא נתתם בה אמון, Grok אינו קורא את
.grok/workflowsשל הפרויקט./workflow listמציין את ה-workflows שהושמטו, ו-/<name>או/workflow <name>עבור אחד מהם מכשיל את ההרצה headless עם הסיבה הזו. העבירו--trustכדי לתת אמון בתיקייה.
הרצת הרקע החדשה ביותר של כל workflow קובעת את קוד היציאה, אלא אם הרצה חדשה יותר שלו הושלמה. אם ההרצה הזו לא הושלמה, ההרצה headless יוצאת 1, ו-stderr אומר איזו הרצה ולמה:
| ההרצה החדשה ביותר | מה stderr אומר |
|---|---|
| נכשלה, כולל הרצה שנכשלה בהתחלה | workflow gate (run wf_…) failed: Error: GATE-FAILED: 2 checks red |
| הושהתה בתקציב הסוכן שלה, שהרצה headless אינה יכולה להעלות | workflow deep-research (run wf_…) paused at its agent budget (3 of 3 agents used) |
| עדיין רצה כאשר ההמתנה נגמרה | workflow slow (run wf_…) was stopped after 600 s: the wait for background work ran out. … |
| עדיין רצה ביציאה אחרי שההמתנה הסתיימה, כגון הרצה שתור מאוחר יותר של מטרה התחיל | workflow slow (run wf_…) was still active at exit and was stopped: it was still going after the wait for background work ended |
הרצה שנכשלה ואז הורצה מחדש עד השלמה באותה הרצה headless, ברקע או בחזית, אינה מכשילה אותה. הרצה שהמודל הריץ בחזית אף פעם לא מכשילה אותה: התוצאה שלה הגיעה למודל באותו תור. אם אי אפשר לקרוא את ההרצות אחרי שלסשן היו הרצות workflow, אחרי שמטרה המשיכה לתור נוסף לאחר שההמתנה הסתיימה, או אחרי שההמתנה נגמרה, ההרצה יוצאת 1 ומציינת זאת. אחרת היציאה אינה מפרטת אותן. עבודת רקע אחרת שנעצרת כאשר ההמתנה נגמרת, כגון shell, גם מכשילה את ההרצה (… exit stopped 2 background jobs). הרצת workflow שהיציאה עצרה (עדיין רצה או מושהית) מצוינת בשורה משלה ומושמטת מהספירה, שאז אומרת other. רק ההרצה החדשה ביותר של כל workflow מקבלת שורה, ולכן הרצה ישנה יותר של אותו workflow שעדיין נמשכת נספרת עם שאר העבודות. מחוץ למטרה, אם תור שהסוכן מריץ אחרי הפרומפט נכשל, כגון התור שעונה עם תוצאת workflow או עם משימת רקע שהסתיימה, ההרצה יוצאת 1 ו-stderr אומר למה: a turn the agent ran after the prompt failed: …. תור שנקטע במגבלת הפלט שלו גם מכשיל את ההרצה, כמו פרומפט קטוע: a turn the agent ran after the prompt stopped at its output limit, so its reply is cut short. תור שמסתיים כרגיל אינו עושה זאת. התורות של מטרה עצמה מדווחים לפי תוצאת המטרה.
עם --output-format json או פורמט streaming, הפלט מסתיים באובייקט השגיאה במקום התוצאה, בלי מזהה הסשן. כדי לחדש סשן כזה, בחרו את המזהה שלו מראש עם -s <uuid>. הרצה שנעצרת כאשר ההמתנה נגמרת אינה משאירה תוצאה.
#קודי יציאה
| קוד | משמעות |
|---|---|
0 | הצלחה. הפרומפט הושלם כרגיל |
1 | שגיאה. כשל אימות, שגיאת רשת, או שגיאת זמן ריצה. בנתיב ה-daemon, גם הרצת workflow שלא הסתיימה, או, מחוץ למטרה, תור כושל שהסוכן הריץ אחרי הפרומפט (ראו Workflows ועבודה ברקע) |
130 | הופרע על ידי SIGINT (Ctrl+C) |
143 | הופסק על ידי SIGTERM |
#אימות לסביבות headless
לשימוש headless, אמתו באחת מהאפשרויות:
XAI_API_KEY: הפשוט ביותר ל-CI. ראו משתני סביבה למעלה.grok login --device-auth(או--device-code): אין צורך בדפדפן במכונת היעד. ראו אימות > זרימת קוד מכשיר.grok login: OAuth2 מבוסס דפדפן במכונות עם GUI.
אם התחברתם בעבר, פרטי התחברות שנשמרו במטמון משמשים אוטומטית.
#טיפים
- מצב headless מתחיל סשן חדש כברירת מחדל. השתמשו ב-
-r/--resumeאו ב--c/--continueכדי לשמור הקשר בין קריאות. - התשובה של
--output-format jsonתמיד כוללתsessionIdשאפשר להשתמש בו עם--resumeלקריאות המשך. - שלבו
--yoloעם--rulesכדי לקבוע גדרות:grok -p "..." --yolo --rules "Never delete files". - לניפוי שגיאות, העלו את רמת הלוג ולכדו את stderr:
RUST_LOG=debug grok -p "..." 2> debug.log.
#גילוי שורש הפרויקט
כאשר Grok מתחיל, הוא מגלה את שורש הפרויקט על ידי הליכה כלפי מעלה מ---cwd (או מהתיקייה הנוכחית) עד שהוא מוצא תיקיית .git.
הערה: אם --cwd מקונן בתוך מאגר גדול (כגון monorepo), Grok מגלה את המאגר הזה כשורש הפרויקט ומגביל את הגילוי שלו (AGENTS.md, skills, היסטוריית git) אליו, מה שיכול להאט את ההפעלה. כוונו את --cwd אל תת הפרויקט הספציפי שבו אתם רוצים לעבוד כדי לשמור על היקף קטן.
#מיקומי קבצים
גרוק שומר נתונים ב-~/.grok (דריסה באמצעות GROK_HOME. ראו משתני סביבה ל-Headless):
| נתיב | תוכן |
|---|---|
config.toml | הגדרות משתמש |
auth.json | אישורי OAuth2/API שמורים במטמון |
version.json | מטמון גרסה לבדיקות עדכון |
sessions/ | תמלילי סשנים (SQLite) |
memory/ | מאגר זיכרון בין סשנים |
logs/ | קובצי לוג פנימיים (למשל unified.jsonl) |
logs/mcp/ | לוגי שרת MCP |
skills/ | הגדרות skill של המשתמש |
personas/ | פרסונות סוכן בהיקף המשתמש |
crash/ | דוחות קריסה |
trace-exports/ | ייצואי מעקב של סשנים |
worktrees/ | מטא-נתונים של עצי עבודה של Git |
#~/.grok לקריאה בלבד
למכולות או ל-CI, עגנו את ~/.grok לקריאה בלבד:
- מלאו מראש את
auth.jsonאו השתמשו ב-XAI_API_KEY - שמירת הסשן נכשלת בשקט (ארעי)
- בדיקות עדכון רושמות אזהרה ומדלגות
export XAI_API_KEY="xai-..."
export GROK_DISABLE_AUTOUPDATER=1
grok -p "..." --no-auto-update#דיכוי בדיקת עדכונים
| שיטה | היקף |
|---|---|
--no-auto-update | סשן |
GROK_DISABLE_AUTOUPDATER=1 | תהליך |
| stderr שאינו TTY (מזוהה אוטומטית) | אוטומטי |
[cli] auto_update = false | קבוע |
הגדרת GROK_DISABLE_AUTOUPDATER לערך שקרי (0, false, off, no, או ריק, בכל רישיות) נחשבת כלא מוגדר. ערכות ה-SDK של הסוכן מזריקות GROK_DISABLE_AUTOUPDATER=1 לסוכנים שאינם המוביל שהן מפעילות (ערך שקרי בסביבת הבידוד של ה-SDK משאיר את העדכונים פעילים), וסוכן ה-stdio מדלג על עדכון הרקע שלו אלא אם הוא רץ מההתקנה המנוהלת ($GROK_HOME/bin/grok).
הודעות עדכון הולכות ל-stderr. stdout נשאר נקי עבור --output-format json. ראו גם משתני סביבה ל-Headless.
#דגלים נוספים למצב Headless
הדגלים האלה משלימים את טבלת אפשרויות שורת הפקודה למעלה. דגלים שכבר רשומים שם (--prompt-json, --prompt-file, --verbatim, --sandbox, --no-auto-update) לא חוזרים כאן.
| דגל | תיאור |
|---|---|
--agent <NAME> | שם סוכן או נתיב לקובץ הגדרה |
--agents <JSON> | הגדרות תת-סוכנים מוטמעות כ-JSON |
--system-prompt-override | דריסת הנחיית המערכת של הסוכן |
--no-plan | השבתת מצב תוכנית |
--no-subagents | השבתת יצירת תת-סוכנים |
GROK_MEMORY=0 | השבתת זיכרון בין סשנים עבור התהליך |
--disable-web-search | השבתת חיפוש באינטרנט וכלי שליפה |
--no-alt-screen | הרצה בתוך השורה (בלי מסך חלופי) |
--worktree [NAME] | יוצר עץ עבודה של git מה-checkout הנוכחי (כולל שינויים שלא נשמרו) ומריץ את הסשן שם. הפעלה מתת-ספרייה נוחתת באותה תת-ספרייה של עץ העבודה. בסשן חדש, אם לעץ העבודה חסרה הספרייה הזאת, למשל ספרייה שמתעלמים ממנה או ספרייה שהקומיט של --worktree-ref קודם לה, הסשן נפתח בשורש עץ העבודה ו-grok מדפיס הערה שאומרת זאת. עם -r, הסשן ממשיך לתוך עץ העבודה החדש. אי אפשר לשלב עם --fork-session. תחת פרופיל sandbox שלא יכול ליצור עצי עבודה, למשל strict, grok יוצא עם שגיאה לפני שהסשן מתחיל (ראו מצב Sandbox). |
--ref <REF> / --worktree-ref <REF> | ענף, תג או קומיט שעליו לבסס את עץ העבודה (עם --worktree). checkout נקי, בלי שכבת שינויים שלא נשמרו |
#ריצות Headless שהופסקו
ב-SIGINT או ב-SIGTERM:
- מצב הסשן נשמר עד קריאת הכלי האחרונה שהושלמה
- שינויי קבצים על ידי כלים לא מגולגלים לאחור
- קוד היציאה הוא 130 עבור SIGINT (
128 + 2) ו-143 עבור SIGTERM (128 + 15). צינורות CI יכולים להבחין ביניהם לבין שגיאה רגילה (קוד יציאה1) - אחרי שהפרומפט נשלח, הריצה עוצרת את עבודת הרקע שהתחילה, מסיימת את הפלט שלה, ואז יוצאת. עצירת העבודה הזאת יכולה להחזיק את שורת הסגירה עד 10 שניות, והיציאה יכולה להגיע מאוחר יותר בזמן שהסוכן נכבה. במסלול המעטפת הסוכן קודם מסיים את בקשות העצירה שנשלחו אליו. רתמה ששולחת SIGTERM צריכה להמתין יותר מברירת המחדל של 10 שניות של
docker stopלפני שהיא שולחת SIGKILL. תוצאה שהוכרעה לפני האות מסיימת את הפלט בשורה שהייתה לה בלי האות, למשל שגיאה של פרומפט שנכשל, תור שנעצר בגללrefusalאוmax_tokens, מגבלת התורות, תור שנכשל שהסוכן הריץ אחרי הפרומפט, ריצת יעד או זרימת עבודה שכבר הסתיימה בלי להשלים, גם לפני שההמתנה ענתה, המתנה שכבר נגמרה, עם שורה לכל ריצת זרימת עבודה שהיא קצצה, או ריצה שכבר סיימה להמתין. ריצת זרימת עבודה שהיציאה לא יכלה לעצור שומרת גם על השורה שלה. ריצות שאי אפשר לקרוא אחרי אות שהגיע לפני שההמתנה ענתה לא מכשילות את הריצה. כל דבר אחר מסתיים ב-cancelled:streaming-jsonעםendש-stopReasonשלו הואcancelled,streaming-messages-jsonעםresultמתת-סוגerror_during_executionו-stop_reasoncancelled, ו-jsonעםstopReasoncancelledשל האובייקט שלו.jsonלא מדפיס דבר כשהפרומפט מעולם לא ענה, ופלט רגיל לא מדפיס דבר נוסף. תחת--no-wait-for-backgroundבמסלול ה-daemon, הריצה עדיין מפרטת ב-stderr מה היציאה עצרה כשאות מגיע אחרי שהתור הסתיים - נעצר במהלך התור: ל-
endהזה ישrequestIdריק ו-usage_is_incomplete: true, ו-usageשל ה-resultמשמיט את התור שקוצץ (אפסים בכל השדות כשזה היה התור היחיד של הריצה), לכן יש לקרוא אותו כחסם תחתון. נעצר בזמן המתנה לעבודת רקע: שורת הסגירה נושאת את שדות ההוצאה של כל תור שהסתיים, ו-endואובייקט ה-json נושאים אתrequestIdשל התור שענה. במסלול ה-daemon, תור שהעצירה קצצה אחרי שהתחיל, יעד שעדיין רץ, או עצירה שנכשלה, גורמים להם להיותusage_is_incomplete: trueבלי עלות - אות בזמן שהסשן עדיין מתחיל יוצא מיד, בלי שורת סגירה. אות שני כופה את היציאה: הריצה לא ממתינה שעבודת הרקע שלה תיעצר, והפלט עלול להסתיים בלי שורת הסגירה שלו
- חידוש:
grok -p "continue" --resume "<id>"אוgrok -p "continue" --continue
ראו ניהול סשנים במצב Headless לפרטים על סשנים בעלי שם ועל הדגלים -s/-r/-c.