ai-consultants 3.2.0 → 3.4.0

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.
@@ -36,6 +36,9 @@ START_TIME=$(get_timestamp_ms)
36
36
  TEMP_OUTPUT=$(mktemp)
37
37
  GROK_RUNTIME_DIR=""
38
38
  TRANSPORT="cli"
39
+ GROK_CLI_VERSION=""
40
+ GROK_CLI_COMPATIBILITY=""
41
+ GROK_SUPPORTS_NO_AUTO_UPDATE=false
39
42
  exit_code=1
40
43
 
41
44
  cleanup() {
@@ -67,6 +70,93 @@ run_grok_api() {
67
70
  fi
68
71
  }
69
72
 
73
+ observe_grok_cli_version() {
74
+ local version
75
+ version=$("$GROK_CMD" --version 2>/dev/null | head -1 || true)
76
+ if [[ "$version" =~ ^grok[[:space:]]+([^[:space:]]+) ]]; then
77
+ printf '%s\n' "${BASH_REMATCH[1]}"
78
+ elif [[ -n "$version" ]]; then
79
+ printf '%s\n' "$version"
80
+ else
81
+ printf 'unknown\n'
82
+ fi
83
+ }
84
+
85
+ grok_cli_supports_required_interface() {
86
+ local probe_home="$1"
87
+ local probe_grok_home="$2"
88
+ local probe_workspace="$3"
89
+ local help flag
90
+
91
+ help=$(env HOME="$probe_home" GROK_HOME="$probe_grok_home" \
92
+ "$GROK_CMD" --help 2>&1 || true)
93
+ for flag in \
94
+ --prompt-file --model --cwd --output-format --no-plan --no-subagents \
95
+ --no-memory --disable-web-search --max-turns --permission-mode \
96
+ --sandbox --tools --deny --verbatim; do
97
+ grep -q -- "$flag" <<< "$help" || return 1
98
+ done
99
+ grep -Eq '^[[:space:]]+models([[:space:]]|$)' <<< "$help" || return 1
100
+
101
+ # Exercise the complete headless argument surface under --help. This checks
102
+ # parser compatibility without starting a session or sending a prompt.
103
+ env HOME="$probe_home" GROK_HOME="$probe_grok_home" \
104
+ "$GROK_CMD" \
105
+ --prompt-file /dev/null \
106
+ -m "$GROK_MODEL" \
107
+ --cwd "$probe_workspace" \
108
+ --output-format plain \
109
+ --no-plan \
110
+ --no-subagents \
111
+ --no-memory \
112
+ --disable-web-search \
113
+ --max-turns 1 \
114
+ --permission-mode dontAsk \
115
+ --sandbox strict \
116
+ --tools "" \
117
+ --deny Bash \
118
+ --deny Edit \
119
+ --deny Read \
120
+ --deny Grep \
121
+ --deny MCPTool \
122
+ --deny WebFetch \
123
+ --deny WebSearch \
124
+ --verbatim \
125
+ --help >/dev/null 2>&1 || return 1
126
+
127
+ if grep -q -- '--no-auto-update' <<< "$help"; then
128
+ GROK_SUPPORTS_NO_AUTO_UPDATE=true
129
+ else
130
+ GROK_SUPPORTS_NO_AUTO_UPDATE=false
131
+ fi
132
+ GROK_CLI_COMPATIBILITY="capability-probed"
133
+ }
134
+
135
+ grok_cli_exposes_requested_model() {
136
+ local isolated_home="$1"
137
+ local isolated_grok_home="$2"
138
+ local models
139
+
140
+ if ! models=$(env HOME="$isolated_home" GROK_HOME="$isolated_grok_home" \
141
+ "$GROK_CMD" models 2>&1); then
142
+ printf '%s\n' "$models"
143
+ return 1
144
+ fi
145
+ grep -Fq "You are logged in with grok.com." <<< "$models" || {
146
+ printf '%s\n' "$models"
147
+ return 1
148
+ }
149
+ awk -v requested="$GROK_MODEL" '
150
+ {
151
+ line = $0
152
+ sub(/^[[:space:]*]+/, "", line)
153
+ split(line, fields, /[[:space:]]+/)
154
+ if (fields[1] == requested) found = 1
155
+ }
156
+ END { exit(found ? 0 : 1) }
157
+ ' <<< "$models"
158
+ }
159
+
70
160
  grok_cli_is_unavailable() {
71
161
  local cli_exit_code="$1"
72
162
  local error_file="$2"
@@ -85,7 +175,7 @@ grok_cli_is_unavailable() {
85
175
  # model errors, timeouts, empty output, or other post-launch failures here;
86
176
  # those must surface instead of silently creating a billable API request.
87
177
  grep -Eiq \
88
- 'not authenticated|authentication( is)? required|authentication failed|unauthorized|run .?grok login|please .*log ?in|no (valid )?(credentials|access token)|missing .*credential|token.*expired|(^|[^0-9])401([^0-9]|$)|permission denied|cannot execute|exec format error|no such file or directory|failed to (start|launch|spawn)|could not (start|launch|spawn)' \
178
+ 'not authenticated|authentication( is)? required|authentication failed|unauthorized|run .?grok login|please .*log ?in|no (valid )?(credentials|access token)|missing .*credential|token.*expired|(^|[^0-9])401([^0-9]|$)|permission denied|cannot execute|exec format error|no such file or directory|failed to (start|launch|spawn)|could not (start|launch|spawn)|lacks the required capabilities|does not expose the requested model' \
89
179
  "$error_file"
90
180
  }
91
181
 
@@ -128,51 +218,66 @@ else
128
218
  fi
129
219
  fi
130
220
 
131
- # Official Grok Build headless contract:
132
- # --prompt-file avoids argv size/process-list exposure, -m pins the
133
- # model, and plain output contains the final assistant text.
134
- # dontAsk + strict sandbox make every unapproved tool fail closed.
135
- GROK_ARGS=(
136
- env
137
- HOME="$isolated_home"
138
- GROK_HOME="$isolated_grok_home"
139
- "$GROK_CMD"
140
- --prompt-file "$prompt_file"
141
- -m "$GROK_MODEL"
142
- --cwd "$isolated_workspace"
143
- --output-format plain
144
- --no-plan
145
- --no-subagents
146
- --no-memory
147
- --disable-web-search
148
- --max-turns 1
149
- --permission-mode dontAsk
150
- --sandbox strict
151
- --tools ""
152
- --deny Bash
153
- --deny Edit
154
- --deny Read
155
- --deny Grep
156
- --deny MCPTool
157
- --deny WebFetch
158
- --deny WebSearch
159
- --verbatim
160
- )
161
-
162
- # Newer Grok Build releases document --no-auto-update for automation;
163
- # keep compatibility with older installed builds that do not expose it.
164
- if "$GROK_CMD" --help 2>&1 | grep -q -- '--no-auto-update'; then
165
- GROK_ARGS+=("--no-auto-update")
166
- fi
167
-
168
- if run_query \
169
- "$CONSULTANT_NAME" \
170
- "$TEMP_OUTPUT" \
171
- "$GROK_TIMEOUT_SECONDS" \
172
- "${GROK_ARGS[@]}" </dev/null; then
173
- exit_code=0
221
+ GROK_CLI_VERSION=$(observe_grok_cli_version)
222
+ if ! grok_cli_supports_required_interface \
223
+ "$isolated_home" "$isolated_grok_home" "$isolated_workspace"; then
224
+ GROK_CLI_COMPATIBILITY="incompatible"
225
+ printf 'Grok Build CLI lacks the required capabilities\n' > "${TEMP_OUTPUT}.err"
226
+ log_warn "[$CONSULTANT_NAME] Grok Build CLI is capability-incompatible (reported version: $GROK_CLI_VERSION)"
227
+ exit_code=69
228
+ elif ! grok_cli_exposes_requested_model \
229
+ "$isolated_home" "$isolated_grok_home" > "${TEMP_OUTPUT}.err"; then
230
+ printf 'Grok Build CLI does not expose the requested model %s\n' \
231
+ "$GROK_MODEL" >> "${TEMP_OUTPUT}.err"
232
+ log_warn "[$CONSULTANT_NAME] Grok Build CLI does not expose $GROK_MODEL"
233
+ exit_code=69
174
234
  else
175
- exit_code=$?
235
+ # Official Grok Build headless contract:
236
+ # --prompt-file avoids argv size/process-list exposure, -m pins the
237
+ # model, and plain output contains the final assistant text.
238
+ # dontAsk + strict sandbox make every unapproved tool fail closed.
239
+ GROK_ARGS=(
240
+ env
241
+ HOME="$isolated_home"
242
+ GROK_HOME="$isolated_grok_home"
243
+ "$GROK_CMD"
244
+ --prompt-file "$prompt_file"
245
+ -m "$GROK_MODEL"
246
+ --cwd "$isolated_workspace"
247
+ --output-format plain
248
+ --no-plan
249
+ --no-subagents
250
+ --no-memory
251
+ --disable-web-search
252
+ --max-turns 1
253
+ --permission-mode dontAsk
254
+ --sandbox strict
255
+ --tools ""
256
+ --deny Bash
257
+ --deny Edit
258
+ --deny Read
259
+ --deny Grep
260
+ --deny MCPTool
261
+ --deny WebFetch
262
+ --deny WebSearch
263
+ --verbatim
264
+ )
265
+
266
+ # --no-auto-update is a useful optional capability, not a version
267
+ # requirement. Compatible older or vendor-custom builds may omit it.
268
+ if [[ "$GROK_SUPPORTS_NO_AUTO_UPDATE" == "true" ]]; then
269
+ GROK_ARGS+=("--no-auto-update")
270
+ fi
271
+
272
+ if run_query \
273
+ "$CONSULTANT_NAME" \
274
+ "$TEMP_OUTPUT" \
275
+ "$GROK_TIMEOUT_SECONDS" \
276
+ "${GROK_ARGS[@]}" </dev/null; then
277
+ exit_code=0
278
+ else
279
+ exit_code=$?
280
+ fi
176
281
  fi
177
282
  else
178
283
  log_warn "[$CONSULTANT_NAME] Grok Build CLI not found: $GROK_CMD"
@@ -214,7 +319,17 @@ fi
214
319
  # Preserve which route actually answered without changing the shared schema.
215
320
  if [[ -s "$OUTPUT_FILE" ]]; then
216
321
  response_tmp=$(mktemp)
217
- if jq --arg transport "$TRANSPORT" '.metadata.transport = $transport' \
322
+ if jq --arg transport "$TRANSPORT" \
323
+ --arg cli_version "$GROK_CLI_VERSION" \
324
+ --arg cli_compatibility "$GROK_CLI_COMPATIBILITY" '
325
+ .metadata.transport = $transport |
326
+ if $transport == "cli" and $cli_version != "" then
327
+ .metadata.cli_version = $cli_version |
328
+ .metadata.cli_compatibility = $cli_compatibility
329
+ else
330
+ .
331
+ end
332
+ ' \
218
333
  "$OUTPUT_FILE" > "$response_tmp"; then
219
334
  mv "$response_tmp" "$OUTPUT_FILE"
220
335
  else
@@ -40,9 +40,43 @@ START_TIME=$(get_timestamp_ms)
40
40
 
41
41
  # --- Execution ---
42
42
  TEMP_OUTPUT=$(mktemp)
43
+ KIMI_CLI_VERSION=""
44
+
45
+ observe_kimi_cli_version() {
46
+ local version
47
+ version=$("$KIMI_CMD" --version 2>/dev/null | head -1 || true)
48
+ printf '%s\n' "${version:-unknown}"
49
+ }
50
+
51
+ kimi_cli_supports_required_interface() {
52
+ local help providers flag
53
+
54
+ help=$("$KIMI_CMD" --help 2>&1 || true)
55
+ for flag in --model --prompt --output-format; do
56
+ grep -q -- "$flag" <<< "$help" || return 1
57
+ done
58
+ grep -Eq '^[[:space:]]+provider([[:space:]]|$)' <<< "$help" || return 1
59
+
60
+ # Check the complete prompt-mode parser without running a model request.
61
+ "$KIMI_CMD" \
62
+ --model "$KIMI_MODEL" \
63
+ --prompt "" \
64
+ --output-format stream-json \
65
+ --help >/dev/null 2>&1 || return 1
66
+
67
+ providers=$("$KIMI_CMD" provider list --json 2>/dev/null || true)
68
+ jq -e --arg model "$KIMI_MODEL" '
69
+ .models[$model] != null
70
+ ' <<< "$providers" >/dev/null 2>&1
71
+ }
43
72
 
44
73
  # Check CLI prerequisite
45
74
  check_command "$KIMI_CMD" "Kimi CLI" "curl -L code.kimi.com/install.sh | bash" || exit 1
75
+ KIMI_CLI_VERSION=$(observe_kimi_cli_version)
76
+ kimi_cli_supports_required_interface || {
77
+ log_error "[Kimi] Kimi CLI lacks the required capabilities for model $KIMI_MODEL (reported version: $KIMI_CLI_VERSION)"
78
+ exit 1
79
+ }
46
80
 
47
81
  # Kimi takes the prompt as the -p argument (it does NOT read stdin). The old
48
82
  # `--quiet --input-format text` were NOT real flags on kimi 0.23.6 — commander
@@ -89,5 +123,17 @@ PERSONA_NAME=$(get_persona_name "$CONSULTANT_NAME")
89
123
  process_consultant_response "$CONSULTANT_NAME" "$MODEL_USED" "$PERSONA_NAME" \
90
124
  "$TEMP_OUTPUT" "$OUTPUT_FILE" "$exit_code" "$LATENCY_MS" "" "$FULL_QUERY"
91
125
 
126
+ if [[ -s "$OUTPUT_FILE" ]]; then
127
+ response_tmp=$(mktemp)
128
+ if jq --arg cli_version "$KIMI_CLI_VERSION" '
129
+ .metadata.cli_version = $cli_version |
130
+ .metadata.cli_compatibility = "capability-probed"
131
+ ' "$OUTPUT_FILE" > "$response_tmp"; then
132
+ mv "$response_tmp" "$OUTPUT_FILE"
133
+ else
134
+ rm -f "$response_tmp"
135
+ fi
136
+ fi
137
+
92
138
  cat "$OUTPUT_FILE"
93
139
  exit $exit_code
@@ -200,7 +200,13 @@ main() {
200
200
  continue
201
201
  fi
202
202
 
203
- ver="$(run_with_timeout 8 "$cmd" --version </dev/null 2>/dev/null | head -1 || true)"
203
+ # agy under SSH must not see SSH_* markers (Keychain credential store).
204
+ # AGY_ENV_PREFIX is exec-safe under GNU timeout (see lib/common.sh).
205
+ if [[ "$default_bin" == "agy" ]]; then
206
+ ver="$(run_with_timeout 8 "${AGY_ENV_PREFIX[@]}" "$cmd" --version </dev/null 2>/dev/null | head -1 || true)"
207
+ else
208
+ ver="$(run_with_timeout 8 "$cmd" --version </dev/null 2>/dev/null | head -1 || true)"
209
+ fi
204
210
  detect_method "$default_bin" "$cmd"
205
211
  log_info "$display ($cmd): installed [${ver:-version n/a}] -- install method: $DETECT_METHOD"
206
212