mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
feat(kimi): Kimi K3 provider on the official kimi-code CLI (#713)
* feat(kimi): Kimi K3 provider on the official kimi-code CLI (Wave 1) ModelProvider.KIMI routes through KimiCliProvider driving Moonshot's kimi CLI on a Kimi subscription (OAuth device-code, no metered key). One-shot delivery roles only (V1), interactive ban wired in both guard lists. Auth: one shared RW auth mount; containers symlink credentials/ and oauth/ (the CLI's cross-process refresh-lock dir) into a container-local KIMI_CODE_HOME so every container and the host redeem the SAME rotating refresh chain - live-verified that per-copy chains cross-invalidate after the reuse-grace window. No orchestrator refresh daemon; an expires_at preflight exits 78. Config renderer mirrors the login-managed provider/model blocks field-for-field (live-captured; the model value is the CLI-side name, never the raw API id), plus per-role deny rules and the bash-guard as a PreToolUse hook via a wrapper script (an env key on a hooks entry makes the CLI silently drop ALL hooks - live-verified). Usage capture sums wire.jsonl usage.record 4-bucket events; sniff classifies rate-limit/auth from structured error text only, mapped to the shared 75/78 park contract. Image installs the CLI latest-at-build (no version pin, by policy) with the resolved version stamped as provenance, binary split to /usr/local away from mutable state. Migrations 090 (enum) + 091 (provider seed); catalog, pricing, routing mode, and orchestrator park/usage wiring mirror the codex integration. * feat(kimi): surface sweep + fleet-wide pin drop (Wave 2) Compose x3 gain the agent-kimi-image service and the orchestrator's read-write ~/.kimi-code mount + kimi-usage dir; .env.example documents the Kimi block. Panel mirrors ModelProvider.KIMI and adds the kimi routing mode (catalog filter, mode button, mix-picker group, badge) with tests; provider routes gain the kimi remediation entry. CLAUDE.md and docs/map document the runtime. Per the no-pins policy, agent-grok/ gemini/codex Dockerfiles drop their version pins for latest-at-build with resolved-version provenance stamps (grok resolves 0.2.112 vs the old 0.2.56 pin - verified by real builds of all four images). --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
@@ -19,17 +19,17 @@ FROM roboco-agent-base
|
||||
|
||||
USER root
|
||||
|
||||
# Install the official codex CLI globally via npm. Pinned — untrusted model
|
||||
# output runs under it, so bump the version deliberately, never float. The
|
||||
# npm route (not chatgpt.com/codex/install.sh, which the CDN denies to
|
||||
# non-browser clients) has no postinstall network fetch: the native binary
|
||||
# rides an optionalDependency (@openai/codex-linux-x64) served from the
|
||||
# public npm registry. Global install symlinks `codex` onto PATH for the
|
||||
# agent user; verify it runs so a broken install fails the build, not spawn.
|
||||
ARG CODEX_CLI_VERSION=0.145.0
|
||||
RUN npm install -g @openai/codex@${CODEX_CLI_VERSION} \
|
||||
# Install the official codex CLI globally via npm. NO version pin (2026-07-28
|
||||
# policy: latest-at-build, always adapt — fleet-wide across grok/gemini/codex/
|
||||
# kimi). The npm route (not chatgpt.com/codex/install.sh, which the CDN denies
|
||||
# to non-browser clients) has no postinstall network fetch: the native binary
|
||||
# rides an optionalDependency (@openai/codex-linux-x64) served from the public
|
||||
# npm registry. Global install symlinks `codex` onto PATH for the agent user;
|
||||
# verify it runs so a broken install fails the build, not spawn; the resolved
|
||||
# version is stamped to /etc/codex-cli-version for per-image provenance.
|
||||
RUN npm install -g @openai/codex \
|
||||
&& command -v codex \
|
||||
&& codex --version
|
||||
&& codex --version | tee /etc/codex-cli-version
|
||||
|
||||
# Entrypoint: render ~/.codex/config.toml + execpolicy rules + the per-role
|
||||
# sandbox flag, then run codex headless (overrides the base image's `claude`
|
||||
@@ -47,5 +47,6 @@ ENV PATH="/home/agent/.codex/bin:/home/agent/.local/bin:/app/.venv/bin:$PATH"
|
||||
|
||||
LABEL role="codex-cli-runtime"
|
||||
LABEL description="Codex (OpenAI) agent runtime — Codex Build via the official codex CLI"
|
||||
LABEL codex.cli.pinned="false"
|
||||
|
||||
ENTRYPOINT ["/app/scripts/codex-cli-agent-entrypoint.sh"]
|
||||
|
||||
@@ -17,15 +17,16 @@ FROM roboco-agent-base
|
||||
|
||||
USER root
|
||||
|
||||
# Install the official Gemini CLI. Pinned — untrusted model output runs under
|
||||
# it, so bump the version deliberately, never float (spike verified 0.52.0 at
|
||||
# github.com/google-gemini/gemini-cli @ 9681621c). npm installs to the global
|
||||
# node_modules the base image's Node 22 already resolves onto PATH.
|
||||
ARG GEMINI_CLI_VERSION=0.52.0
|
||||
RUN npm install -g "@google/gemini-cli@${GEMINI_CLI_VERSION}" \
|
||||
# Install the official Gemini CLI. NO version pin (2026-07-28 policy:
|
||||
# latest-at-build, always adapt — fleet-wide across grok/gemini/codex/kimi).
|
||||
# npm installs to the global node_modules the base image's Node 22 already
|
||||
# resolves onto PATH; the resolved version is stamped to /etc/gemini-cli-version
|
||||
# for per-image provenance (a record, not a pin).
|
||||
RUN npm install -g @google/gemini-cli \
|
||||
&& npm cache clean --force \
|
||||
&& rm -rf /root/.npm /tmp/* \
|
||||
&& gemini --version
|
||||
&& command -v gemini \
|
||||
&& gemini --version | tee /etc/gemini-cli-version
|
||||
|
||||
# Entrypoint: copy the staged OAuth credential into a writable ~/.gemini,
|
||||
# render settings.json + policy TOML, then run gemini headless (overrides the
|
||||
@@ -39,6 +40,7 @@ USER agent
|
||||
|
||||
LABEL role="gemini-cli-runtime"
|
||||
LABEL description="Gemini (Google) agent runtime — Gemini Build via the official gemini CLI"
|
||||
LABEL gemini.cli.pinned="false"
|
||||
|
||||
# advanced.autoConfigureMemory=false (rendered into settings.json) pins Node's
|
||||
# heap sizing away from auto-detection against a shared host; this bounds it
|
||||
|
||||
@@ -17,17 +17,21 @@ USER root
|
||||
# Install the official grok CLI (Grok Build) for the agent user. The installer's
|
||||
# default is $HOME/.grok/bin, so the binary lands at ~/.grok/bin/grok alongside
|
||||
# its runtime (downloads / bundled / skills) under ~/.grok, all agent-owned.
|
||||
# Pinned — untrusted model output runs under it, so bump the version deliberately,
|
||||
# never float. Download the installer to a file first (a `curl | bash` pipe hides
|
||||
# a curl failure as a silent no-op) and verify the binary installed AND runs, so
|
||||
# a broken install fails the build here, not at spawn. (curl/bash from the base.)
|
||||
ARG GROK_CLI_VERSION=0.2.56
|
||||
# NO version pin (2026-07-28 policy: latest-at-build, always adapt — fleet-wide
|
||||
# across grok/gemini/codex/kimi). Download the installer to a file first (a
|
||||
# `curl | bash` pipe hides a curl failure as a silent no-op) and verify the
|
||||
# binary installed AND runs, so a broken install fails the build here, not at
|
||||
# spawn; the resolved version is stamped to /etc/grok-cli-version (build-log +
|
||||
# on-disk provenance, not a pin — the next build reinstalls whatever's latest).
|
||||
RUN su agent -s /bin/bash -c "set -euo pipefail; export HOME=/home/agent; \
|
||||
curl -fsSL https://x.ai/cli/install.sh -o /tmp/grok-install.sh; \
|
||||
bash /tmp/grok-install.sh ${GROK_CLI_VERSION}; \
|
||||
test -x /home/agent/.grok/bin/grok; \
|
||||
/home/agent/.grok/bin/grok --version" \
|
||||
&& rm -rf /tmp/*
|
||||
bash /tmp/grok-install.sh; \
|
||||
test -x /home/agent/.grok/bin/grok" \
|
||||
&& rm -rf /tmp/* \
|
||||
# Provenance stamp runs as root (outside the su subshell — /etc is
|
||||
# root-writable only) with root's HOME; fine while `grok --version`
|
||||
# touches no $HOME-relative state.
|
||||
&& /home/agent/.grok/bin/grok --version | tee /etc/grok-cli-version
|
||||
|
||||
# Entrypoint: render ~/.grok/config.toml + the per-role flags, then run grok
|
||||
# headless (overrides the base image's `claude` entrypoint). ~/.grok is already
|
||||
@@ -43,5 +47,6 @@ ENV PATH="/home/agent/.grok/bin:/app/.venv/bin:$PATH"
|
||||
|
||||
LABEL role="grok-cli-runtime"
|
||||
LABEL description="Grok (xAI) agent runtime — Grok Build via the official grok CLI"
|
||||
LABEL grok.cli.pinned="false"
|
||||
|
||||
ENTRYPOINT ["/app/scripts/grok-cli-agent-entrypoint.sh"]
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# Kimi (Moonshot) Agent Image
|
||||
# =============================================================================
|
||||
# Runs Kimi K3 through Moonshot's official `kimi` (kimi-code) CLI,
|
||||
# authenticated by a Kimi subscription via a symlinked-in
|
||||
# ~/.kimi-code/credentials/kimi-code.json + oauth/ (the shared RW auth mount
|
||||
# — see roboco.llm.providers.kimi's module docstring) — the parity analogue
|
||||
# of the Claude Code path's mounted ~/.claude and the codex/gemini paths'
|
||||
# subscription mounts (no metered API key). Reuses the base image's roboco
|
||||
# venv + uv + the RoboCo MCP gateway servers. The entrypoint symlinks the
|
||||
# mounted credential in, renders ~/.kimi-code/config.toml + mcp.json +
|
||||
# AGENTS.md from the mounted mcp-config.json (see
|
||||
# roboco.llm.providers.kimi_cli_config), and runs the CLI headless. One
|
||||
# runtime image serves every one-shot delivery role — role behaviour comes
|
||||
# from the mounted system prompt / manifest / mcp-config, exactly as on the
|
||||
# Claude/grok/codex/gemini paths.
|
||||
#
|
||||
# V1 scope: no interactive intake/secretary variant of this image exists
|
||||
# (unlike grok's agent-grok-prompter / agent-grok-secretary) — Kimi is
|
||||
# one-shot delivery roles only for now.
|
||||
# =============================================================================
|
||||
|
||||
FROM roboco-agent-base
|
||||
|
||||
USER root
|
||||
|
||||
# Install the official kimi-code CLI. NO version pin (CEO decision,
|
||||
# 2026-07-28 — latest at build, always adapt: Kimi's CLI is very young and
|
||||
# has already shipped a breaking rename within a week of release). The
|
||||
# install script itself SHA-256-verifies the binary it fetches.
|
||||
# KIMI_INSTALL_DIR splits the binary (/usr/local/bin/kimi — needs root to
|
||||
# write) from KIMI_CODE_HOME's mutable per-agent state (~/.kimi-code,
|
||||
# rendered fresh at container start, never baked into the image — see
|
||||
# roboco.llm.providers.kimi_cli_config): the installer's own default
|
||||
# co-locates both under ~/.kimi-code, which would mix a writable binary path
|
||||
# into the exact tree the entrypoint later writes credentials/config into.
|
||||
# Build-fails-loud verification (a broken install fails the build here, not
|
||||
# at spawn); the resolved version is captured to both the build log (RUN
|
||||
# output) and a baked-in file for runtime attribution — Docker has no native
|
||||
# mechanism to compute a LABEL value from a RUN command's own output, so the
|
||||
# file is the durable per-image provenance record (a record, not a pin: the
|
||||
# next build always reinstalls whatever is latest that day).
|
||||
ENV KIMI_INSTALL_DIR=/usr/local
|
||||
RUN curl -fsSL https://code.kimi.com/kimi-code/install.sh -o /tmp/kimi-install.sh \
|
||||
&& bash /tmp/kimi-install.sh \
|
||||
&& command -v kimi \
|
||||
&& kimi --version | tee /etc/kimi-cli-version \
|
||||
&& rm -rf /tmp/*
|
||||
|
||||
# Entrypoint: symlink the mounted credential in, render config.toml/mcp.json/
|
||||
# AGENTS.md, then run kimi headless (overrides the base image's `claude`
|
||||
# entrypoint). Pre-create + chown ~/.kimi-code (mirrors the gemini image —
|
||||
# the entrypoint's own symlink/render steps then just write into it).
|
||||
COPY docker/scripts/kimi-cli-agent-entrypoint.sh /app/scripts/kimi-cli-agent-entrypoint.sh
|
||||
COPY docker/scripts/kimi-bash-guard-wrapper.sh /app/scripts/kimi-bash-guard-wrapper.sh
|
||||
RUN chmod 0755 /app/scripts/kimi-cli-agent-entrypoint.sh /app/scripts/kimi-bash-guard-wrapper.sh \
|
||||
&& mkdir -p /home/agent/.kimi-code \
|
||||
&& chown -R agent:agent /home/agent/.kimi-code
|
||||
|
||||
USER agent
|
||||
|
||||
LABEL role="kimi-cli-runtime"
|
||||
LABEL description="Kimi (Moonshot) agent runtime — Kimi K3 via the official kimi CLI"
|
||||
LABEL kimi.cli.pinned="false"
|
||||
|
||||
# Runtime self-update is pure spawn latency + an unreviewed binary fetch in
|
||||
# an ephemeral container (an update can't persist anyway) — suppressed at
|
||||
# the env level; `[upgrade] auto_install=false` in the rendered config.toml
|
||||
# is the belt-and-suspenders config-level twin (roboco.llm.providers.kimi_cli_config).
|
||||
ENV KIMI_CODE_NO_AUTO_UPDATE=1
|
||||
|
||||
ENTRYPOINT ["/app/scripts/kimi-cli-agent-entrypoint.sh"]
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# Kimi's [[hooks]] TOML entry has no `env` field — an entry carrying one is
|
||||
# silently dropped whole (live-verified: "Ignored invalid config ... hooks",
|
||||
# run continues with NO hooks installed at all). ROBOCO_GUARD_SKIP_GIT=1 must
|
||||
# therefore ride a wrapper's own export, not the hook config, since kimi's
|
||||
# `command` is a plain path (unverified whether it shell-interprets the
|
||||
# string, so `env VAR=1 cmd` is not assumed safe).
|
||||
export ROBOCO_GUARD_SKIP_GIT=1
|
||||
exec /app/scripts/bash-guard-hook.sh "$@"
|
||||
Executable
+144
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env bash
|
||||
# Entrypoint for the roboco-agent-kimi image (one-shot delivery roles only —
|
||||
# see docker/agent-kimi.Dockerfile for the V1 scope note).
|
||||
#
|
||||
# Runs an agent on Moonshot's official `kimi` (kimi-code) CLI, authenticated
|
||||
# by a Kimi subscription via a symlinked-in
|
||||
# ~/.kimi-code/credentials/kimi-code.json + oauth/ (the shared RW auth mount)
|
||||
# — the parity analogue of the codex-cli entrypoint's mounted ~/.codex. The
|
||||
# gateway, identity, and workspace are mounted by the orchestrator's shared
|
||||
# container assembly (the same that wires Claude/grok/codex); this entrypoint
|
||||
# symlinks the host credential in, renders the kimi runtime config from that
|
||||
# mount, and runs the CLI headless.
|
||||
set -euo pipefail
|
||||
|
||||
# Split-install sanity: docker/agent-kimi.Dockerfile installs the binary to
|
||||
# KIMI_INSTALL_DIR=/usr/local (split from KIMI_CODE_HOME's mutable state) —
|
||||
# verify it actually resolved on PATH before doing any other work.
|
||||
command -v kimi >/dev/null || {
|
||||
echo "[kimi] kimi CLI not found on PATH — image build is broken." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Symlink phase. The orchestrator mounts the host ~/.kimi-code DIRECTORY
|
||||
# READ-WRITE at this path (roboco.llm.providers.kimi._append_kimi_auth_mount).
|
||||
# Moonshot's refresh token is rotation-with-short-reuse-grace, not truly
|
||||
# reusable (live-verified: a per-container COPY cross-invalidates the shared
|
||||
# chain once the grace window passes — see roboco.llm.providers.kimi's
|
||||
# module docstring), so every container must redeem the SAME chain the host
|
||||
# uses: symlink credentials/ AND oauth/ (the cross-process refresh lock
|
||||
# directory — entirely missing from the old copy-in, and load-bearing: it's
|
||||
# what serializes concurrent redemptions across containers + the host)
|
||||
# straight into the image's own, writable ~/.kimi-code. config.toml/mcp.json/
|
||||
# AGENTS.md are still rendered fresh below, never symlinked.
|
||||
AUTH_DIR="/home/agent/.kimi-code-auth"
|
||||
mkdir -p /home/agent/.kimi-code
|
||||
if [ -d "$AUTH_DIR/credentials" ]; then
|
||||
mkdir -p "$AUTH_DIR/oauth"
|
||||
ln -sfn "$AUTH_DIR/credentials" /home/agent/.kimi-code/credentials
|
||||
ln -sfn "$AUTH_DIR/oauth" /home/agent/.kimi-code/oauth
|
||||
fi
|
||||
|
||||
# Render ~/.kimi-code/config.toml (managed provider/model blocks + telemetry/
|
||||
# upgrade knobs + per-role [[permission.rules]] + the bash-guard [[hooks]])
|
||||
# + mcp.json + AGENTS.md. Run from /app so `python -m` resolves the INSTALLED
|
||||
# roboco package: dev/doc/qa agents run at their workspace-clone cwd, whose
|
||||
# own roboco/ dir would shadow it on the sys.path front (the same
|
||||
# ModuleNotFound lesson the codex/grok entrypoints document).
|
||||
( cd /app && python -m roboco.llm.providers.kimi_cli_config )
|
||||
|
||||
# Prompt-injection guard (parity with the Claude/grok/codex path): the task
|
||||
# prompt is DATA, not instructions — refuse a poisoned one before the model
|
||||
# ever sees it. The composed role blueprint travels separately via the
|
||||
# additive AGENTS.md (rendered above), so only the raw task prompt is
|
||||
# screened here. Run from /app too.
|
||||
if ! ( cd /app && python -m roboco.agent_sdk.prompt_guard "${ROBOCO_INITIAL_PROMPT:-}" ); then
|
||||
echo "Refusing to run: task prompt matched a prompt-injection pattern." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Auth fail-fast guard. D2 resolved Kimi's refresh token as
|
||||
# rotation-with-short-reuse-grace over ONE shared chain (symlinked, not
|
||||
# copied — see above) — each container self-refreshes through the CLI's own
|
||||
# cross-process lock, so there is no orchestrator-side refresh daemon to
|
||||
# backstop, only this preflight: read the symlinked
|
||||
# credentials/kimi-code.json's expires_at (a plain JSON field, no JWT
|
||||
# decode) and refuse fast (exit 78 / EX_CONFIG) on missing/expired, instead
|
||||
# of the CLI hanging or failing deep into the run.
|
||||
if ! ( cd /app && python -m roboco.llm.providers.kimi_cli_config --check ); then
|
||||
echo "[kimi] auth credential missing or expired — refusing to run. Run" \
|
||||
"\`kimi login\` on the host (or set ROBOCO_HOST_KIMI_DIR to the" \
|
||||
"directory holding credentials/kimi-code.json) before spawning Kimi" \
|
||||
"agents." >&2
|
||||
exit 78
|
||||
fi
|
||||
|
||||
# Run the agent. `< /dev/null` keeps the headless run from blocking on
|
||||
# stdin. We do NOT `exec`: the script regains control to classify the exit
|
||||
# code + capture usage. The container's cwd is already the agent's
|
||||
# workspace (the orchestrator sets it via docker run -w, mirroring the
|
||||
# Claude/grok/codex path) — captured here BEFORE the usage-capture step's
|
||||
# `cd /app` needs it to locate the right sessions/wd_<cwd-basename>_*/ dir.
|
||||
# The prompt travels only as an env-var expansion into a single quoted argv
|
||||
# token (never re-parsed by the shell) — the same injection-safety property
|
||||
# as the grok/gemini path's env-var prompt passing; the composed role
|
||||
# blueprint is NOT folded in here (unlike codex) since it already reached
|
||||
# the model via the additive AGENTS.md rendered above.
|
||||
WORKDIR="$PWD"
|
||||
RUN_LOG="/tmp/kimi-run.jsonl"
|
||||
ERR_LOG="/tmp/kimi-run.err"
|
||||
|
||||
# `--output-format stream-json` streams JSONL to stdout; `tee` shows it live
|
||||
# via `docker logs` (parity with the Claude/grok/codex path) while ALSO
|
||||
# capturing it to RUN_LOG for the usage-capture + sniff reads below. Never
|
||||
# pipe this through `head` (a known EPIPE hazard on an early-closing reader —
|
||||
# `tee` alone is safe, it always drains stdin to completion). stderr goes to
|
||||
# ERR_LOG and is surfaced after the run.
|
||||
set +e
|
||||
kimi -p "${ROBOCO_INITIAL_PROMPT:-}" \
|
||||
--output-format stream-json \
|
||||
-m "${ROBOCO_AGENT_MODEL:-kimi-code/k3}" \
|
||||
< /dev/null 2> "$ERR_LOG" | tee "$RUN_LOG"
|
||||
run_rc=${PIPESTATUS[0]}
|
||||
set -e
|
||||
[ -s "$ERR_LOG" ] && cat "$ERR_LOG" >&2
|
||||
|
||||
# Capture token usage from the session's wire.jsonl (kimi's stdout carries no
|
||||
# usage summary of its own, unlike codex/gemini — see kimi_cli_usage for the
|
||||
# session-dir resolution). Best-effort; never fails the run. Run from /app
|
||||
# for the same module-resolution reason as the render above; ROBOCO_KIMI_WORKDIR
|
||||
# carries the captured workspace cwd so the usage reader can find the right
|
||||
# sessions/wd_<cwd-basename>_*/ directory after this subshell's own `cd /app`.
|
||||
( cd /app && ROBOCO_KIMI_RUN_LOG="$RUN_LOG" ROBOCO_KIMI_WORKDIR="$WORKDIR" \
|
||||
python -m roboco.llm.providers.kimi_cli_usage ) || true
|
||||
|
||||
# Kimi has NO documented exit-code taxonomy for `-p` (a claimed 75/1 split is
|
||||
# unverified noise) — every failure looks the same at the process level.
|
||||
# Classify the run WITHOUT scanning the full transcript: the model's own
|
||||
# on-topic prose can false-positive a raw grep by construction — kimi_cli_sniff
|
||||
# extracts ONLY structured error fields off error-bearing JSONL events plus
|
||||
# stderr and classifies THAT, never stdout's echoed assistant/tool content.
|
||||
# Mirrors the codex/grok/gemini entrypoints' exit-75/78 convention so the
|
||||
# orchestrator's existing park-and-probe logic, scoped by provider_type,
|
||||
# handles all four providers identically:
|
||||
# - rate-limit/quota -> exit 75 (EX_TEMPFAIL): the orchestrator PARKS the
|
||||
# provider instead of the dispatcher respawning the same task every tick.
|
||||
# - auth/membership failure (a lapsed subscription or an expired credential
|
||||
# discovered mid-run, past the --check backstop above) -> exit 78
|
||||
# (EX_CONFIG): parked the same way as a pre-run auth miss.
|
||||
SNIFF="$( (cd /app && python -m roboco.llm.providers.kimi_cli_sniff "$RUN_LOG" "$ERR_LOG") 2>/dev/null || true)"
|
||||
if [ "$SNIFF" = "rate_limit" ]; then
|
||||
echo "[kimi] rate-limited — exiting 75 so the orchestrator parks the" \
|
||||
"provider; the task is retried when the limit lifts." >&2
|
||||
exit 75
|
||||
fi
|
||||
if [ "$SNIFF" = "auth" ]; then
|
||||
echo "[kimi] auth/membership failure detected mid-run — exiting 78 so the" \
|
||||
"orchestrator parks the provider until the credential is refreshed." >&2
|
||||
exit 78
|
||||
fi
|
||||
|
||||
# A graceful exit without a terminal verb is handled server-side by the
|
||||
# orchestrator (_handle_stopped_container substitutes the still-owned task) —
|
||||
# the kimi-cli runtime needs no in-container SDK server for that.
|
||||
exit "$run_rc"
|
||||
Reference in New Issue
Block a user