Repository navigation
refactor(agent): move Claude core-agent launchers into src/agent/claude/cli/ - #1891
Conversation
…de/cli/ Extract the two scripts that initialize + onboard the Claude Code core agent out of scripts/ into a dedicated src/agent/claude/cli/, so the launch/onboarding logic lives in one place (and a future headless / alternate-provider launcher can grow alongside it): scripts/start-cli.sh -> src/agent/claude/cli/start-cli.sh scripts/sutando-shell-setup.sh -> src/agent/claude/cli/sutando-shell-setup.sh Re-points every caller -- startup.sh, health-check.py (_default_core_restart), Sutando.app (main.swift), sutando-migrate.sh, the health-check-fallback launchd plist, docs/host-cli-bindings.md, and comment refs in sutando_config.py/ts + util_paths.ts -- and fixes each script's repo-root self-location (dirname/.. -> dirname/../../../.. now that they are four levels deep). Updates the affected tests and adds src/agent/claude/README.md. Pure move + reference update; no behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…de_sdk) Add an Agent-SDK-based core (core_config.core_type: claude_sdk) that runs a persistent, observable Claude Code session behind a live browser UI, and carries the full Sutando runtime. Builds on sonichi#1891 + sonichi#1892. - sdk/session-server.ts: one long-lived @anthropic-ai/claude-agent-sdk query() with async-iterable input (persistent multi-turn) -> SSE event stream + minimal ui.html + POST /input. Loads CLAUDE.md + skills (settingSources + claude_code systemPrompt preset) so it is not a bare Q&A session; replays event history to each new SSE client so a browser refresh shows the full session. - Hybrid runtime: the Node host owns task ingestion (watch tasks/, stage on pickup, inject, archive on result), the per-host liveness heartbeat, and cron/proactive scheduling (reads crons.json, injects each prompt at its time -- CronCreate is not available in an SDK session). SUTANDO_HOST_OWNS_WATCHER=1 no-ops the agent's Monitor watcher so tasks do not double-process. - src/startup.sh routes the core by core_config.core_type (claude_cli -> tmux core; claude_sdk -> session-server, backgrounded, heartbeat deferred to core_heartbeat.py). restart.sh/stop.sh now stop the session-server, and watch-tasks-stream.sh gains the host-owns-watcher no-op guard. Stacked on sonichi#1891 + sonichi#1892 -- review after they merge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6cebfcf to
cdf1c28
Compare
…onfig) Add a high-level `core_config` block that lets the core agent run against a custom Anthropic-compatible provider + API token instead of the Claude.ai subscription, plus per-class model overrides for subtasks. Builds on the src/agent/claude/ restructure (sonichi#1891). - core_config: { core_type, provider, auth_env, model, models{opus,sonnet,haiku} } resolved by src/sutando_config.py::resolve_core_config + the sutando-config.sh `core-config` getter. - provider-env.sh (shared helper): resolves the provider endpoint + token (env or vault) + the model aliases (ANTHROPIC_DEFAULT_{OPUS,SONNET,HAIKU}_MODEL). - cli/start-cli.sh: when core_config.provider is set, boots the SAME interactive core against the provider -- sheds the inherited credential-proxy base URL, env token overrides OAuth, refuses if a provider is set but no token resolves. - cli/dashp.sh: headless one-shot `claude -p` against the provider. - auth_env defaults to the ANTHROPIC_SUBSCRIPTION sentinel (subscription OAuth, no token); a real token env (ANTHROPIC_AUTH_TOKEN / ANTHROPIC_API_KEY) selects Bearer vs x-api-key, with the other cleared to avoid a mixed-header conflict. Stacked on sonichi#1891 -- review after it merges. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…de_sdk) Add an Agent-SDK-based core (core_config.core_type: claude_sdk) that runs a persistent, observable Claude Code session behind a live browser UI, and carries the full Sutando runtime. Builds on sonichi#1891 + sonichi#1892. - sdk/session-server.ts: one long-lived @anthropic-ai/claude-agent-sdk query() with async-iterable input (persistent multi-turn) -> SSE event stream + minimal ui.html + POST /input. Loads CLAUDE.md + skills (settingSources + claude_code systemPrompt preset) so it is not a bare Q&A session; replays event history to each new SSE client so a browser refresh shows the full session. - Hybrid runtime: the Node host owns task ingestion (watch tasks/, stage on pickup, inject, archive on result), the per-host liveness heartbeat, and cron/proactive scheduling (reads crons.json, injects each prompt at its time -- CronCreate is not available in an SDK session). SUTANDO_HOST_OWNS_WATCHER=1 no-ops the agent's Monitor watcher so tasks do not double-process. - src/startup.sh routes the core by core_config.core_type (claude_cli -> tmux core; claude_sdk -> session-server, backgrounded, heartbeat deferred to core_heartbeat.py). restart.sh/stop.sh now stop the session-server, and watch-tasks-stream.sh gains the host-owns-watcher no-op guard. Stacked on sonichi#1891 + sonichi#1892 -- review after they merge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…er paths Moving start-cli.sh / sutando-shell-setup.sh into src/agent/claude/cli/ removed the old scripts/ paths outright. Callers in the repo were repointed, but a not-yet-rebuilt Sutando.app binary has repoRoot + "/scripts/start-cli.sh" compiled in and would hard-fail after a `git pull` until rebuilt (same for an in-flight health-check --recover-core). Add thin exec-forwarding shims at the old paths for one release so those lagging callers keep working; remove after all callers reference the new path. New tests/start-cli-compat-shim.test.sh asserts the shims exist, are executable, parse, and forward — without launching the live core. Also fix two stale comments left by the move: the config-dir test said "three levels up" (now four: cli → claude → agent → src → repo) and the migrate-import test referenced the pre-move scripts/sutando-shell-setup.sh path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cdf1c28 to
c76c277
Compare
…onfig) Add a high-level `core_config` block that lets the core agent run against a custom Anthropic-compatible provider + API token instead of the Claude.ai subscription, plus per-class model overrides for subtasks. Builds on the src/agent/claude/ restructure (sonichi#1891). - core_config: { core_type, provider, auth_env, model, models{opus,sonnet,haiku} } resolved by src/sutando_config.py::resolve_core_config + the sutando-config.sh `core-config` getter. - provider-env.sh (shared helper): resolves the provider endpoint + token (env or vault) + the model aliases (ANTHROPIC_DEFAULT_{OPUS,SONNET,HAIKU}_MODEL). - cli/start-cli.sh: when core_config.provider is set, boots the SAME interactive core against the provider -- sheds the inherited credential-proxy base URL, env token overrides OAuth, refuses if a provider is set but no token resolves. - cli/dashp.sh: headless one-shot `claude -p` against the provider. - auth_env defaults to the ANTHROPIC_SUBSCRIPTION sentinel (subscription OAuth, no token); a real token env (ANTHROPIC_AUTH_TOKEN / ANTHROPIC_API_KEY) selects Bearer vs x-api-key, with the other cleared to avoid a mixed-header conflict. Stacked on sonichi#1891 -- review after it merges. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…de_sdk) Add an Agent-SDK-based core (core_config.core_type: claude_sdk) that runs a persistent, observable Claude Code session behind a live browser UI, and carries the full Sutando runtime. Builds on sonichi#1891 + sonichi#1892. - sdk/session-server.ts: one long-lived @anthropic-ai/claude-agent-sdk query() with async-iterable input (persistent multi-turn) -> SSE event stream + minimal ui.html + POST /input. Loads CLAUDE.md + skills (settingSources + claude_code systemPrompt preset) so it is not a bare Q&A session; replays event history to each new SSE client so a browser refresh shows the full session. - Hybrid runtime: the Node host owns task ingestion (watch tasks/, stage on pickup, inject, archive on result), the per-host liveness heartbeat, and cron/proactive scheduling (reads crons.json, injects each prompt at its time -- CronCreate is not available in an SDK session). SUTANDO_HOST_OWNS_WATCHER=1 no-ops the agent's Monitor watcher so tasks do not double-process. - src/startup.sh routes the core by core_config.core_type (claude_cli -> tmux core; claude_sdk -> session-server, backgrounded, heartbeat deferred to core_heartbeat.py). restart.sh/stop.sh now stop the session-server, and watch-tasks-stream.sh gains the host-owns-watcher no-op guard. Stacked on sonichi#1891 + sonichi#1892 -- review after they merge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
john-the-dev
left a comment
There was a problem hiding this comment.
Reviewed at current head (c76c277ba). Sound refactor — moves the Claude core-agent launchers into src/agent/claude/cli/, with main.swift:77 re-pointed in-PR so from-source builds are unaffected.
The one real concern from the first pass — the old scripts git-mv'd with no shim, which would hard-fail a not-yet-rebuilt Sutando.app (the path is compiled in, so it lags a git pull) or an in-flight health-check --recover-core during the pull→rebuild window — is now resolved by the one-release compat shims (scripts/{start-cli,sutando-shell-setup}.sh exec-forwarding to the new homes) plus the start-cli-compat-shim smoke test.
No blocking issues. Approving. Nit: remember to delete the shims after one release (they're self-documented as such).
|
@cla-assistant check |
…de_sdk) Add an Agent-SDK-based core (core_config.core_type: claude_sdk) that runs a persistent, observable Claude Code session behind a live browser UI, and carries the full Sutando runtime. Builds on sonichi#1891 + sonichi#1892. - sdk/session-server.ts: one long-lived @anthropic-ai/claude-agent-sdk query() with async-iterable input (persistent multi-turn) -> SSE event stream + minimal ui.html + POST /input. Loads CLAUDE.md + skills (settingSources + claude_code systemPrompt preset) so it is not a bare Q&A session; replays event history to each new SSE client so a browser refresh shows the full session. - Hybrid runtime: the Node host owns task ingestion (watch tasks/, stage on pickup, inject, archive on result), the per-host liveness heartbeat, and cron/proactive scheduling (reads crons.json, injects each prompt at its time -- CronCreate is not available in an SDK session). SUTANDO_HOST_OWNS_WATCHER=1 no-ops the agent's Monitor watcher so tasks do not double-process. - src/startup.sh routes the core by core_config.core_type (claude_cli -> tmux core; claude_sdk -> session-server, backgrounded, heartbeat deferred to core_heartbeat.py). restart.sh/stop.sh now stop the session-server, and watch-tasks-stream.sh gains the host-owns-watcher no-op guard. Stacked on sonichi#1891 + sonichi#1892 -- review after they merge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Brings local-main current with upstream (merge-base sonichi#1697 → main @62f7767, 146 upstream commits) while preserving local-only features. Conflict resolutions (policy: take upstream for infra it refactored; keep local features ported onto the new APIs): - startup.sh / start-cli.sh: upstream relocated the launcher to src/agent/claude/cli/start-cli.sh (sonichi#1891). Took the shim; ported local #123 credential-proxy routing (ANTHROPIC_BASE_URL:7846) into the new launcher via CORE_ENV_ARGS + export. - discord-bridge: kept local CHANNELS_DIR (Charlie isolated-instance, SUTANDO_DISCORD_CHANNELS_DIR) but repointed its default to upstream's claude_home_path(); folded upstream media-headers + write-instrumentation into the task writer while keeping task: LAST (security invariant #44). - telegram-bridge: adopted upstream dict send_reply + obs events, but kept local retry-on-transient-failure delivery loop (#8/#22) and 409 multi-host backoff (merged with upstream 429 backoff). Fixed auto-merge artifacts (duplicated poll_progress + progress_stream import). - health-check: unioned local notes-symlink + Charlie co-host checks with upstream electron-impostor detection; kept sms-bridge coverage on upstream's claude_home_path() base. - voice-agent: kept local dead-air narration; dropped writeVoiceMetrics() (upstream refactored it into createSessionRecorder / recorder.flush()). - github-webhook: moved task: last to satisfy the field-order invariant. Verified: task-field-order (8 writers) + 18 targeted bridge/health tests pass; tsc --noEmit clean; all touched Python compiles; shell scripts valid. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rmm5NP2XFC3n8fYoGP2kYx
#3363) PR #1891 moved the Claude-specific shell setup under src/agent/claude/cli/ and added a one-release compat shim for scripts/sutando-shell-setup.sh, but deliberately skipped one for the generic core launcher (its own test said so: "The generic core launcher has no old-path shim: all callers use src/agent/start-cli.sh directly"). That assumption was false: ag2-space/ag2space-cinny-desktop's backend-supervisor.mjs still resolves scripts/start-cli.sh, and silently no-ops with zero logging when it's missing - the core agent never starts, and every other service still reports ready (#3362). The primary fix for that lives in ag2space-cinny-desktop (loud logging + fallback path ordering); this shim is the defensive half sutando itself can ship, for already-built app binaries that can't be patched. Matches the existing scripts/sutando-shell-setup.sh shim exactly (same exec-forward pattern), and extends tests/start-cli-compat-shim.test.sh to cover both shims - keyed per name since the two forward to different targets, and with the file's own now-false "no old-path shim" claim removed. Closes #3362 Co-authored-by: Rui Wang <52230987+john-the-dev@users.noreply.github.com>
What
Moves the two scripts that initialize + onboard the Claude Code core agent out of
scripts/into a dedicatedsrc/agent/claude/cli/:scripts/start-cli.sh→src/agent/claude/cli/start-cli.sh— the canonicalsutando-coretmux launcher/restarterscripts/sutando-shell-setup.sh→src/agent/claude/cli/sutando-shell-setup.sh— theclaude-sutandoconfig-dir alias onboardingWhy
The core-agent launch/onboarding logic was scattered in
scripts/. Consolidating it undersrc/agent/claude/gives it a clear home — and room for a second initialization flow (a headless / alternate-provider launcher) to grow alongside it without morescripts/sprawl.git mvpreserves history.Changes
src/startup.sh,src/health-check.py(_default_core_restart),src/Sutando/main.swift,scripts/sutando-migrate.sh, the health-check-fallback launchd plist,docs/host-cli-bindings.md, and comment refs insutando_config.py/sutando_config.ts/util_paths.ts.dirname/..→dirname/../../../..(they're four levels deep now). This caught two path-built-from-parts references (health-check.py's restart call and a test) that a string-only sweep would miss.src/agent/claude/README.md.Pure move + reference update — no behavior change.
Tests
start-cli-model-pin,sutando-shell-setup(+ argparse),startup-shell-setup-wireup, andhealth-check-recover-coreall pass.start-cli-claude-config-diris 3/4 — the single failure is pre-existing onmain(its expectation assumes$SUTANDO_WORKSPACEis honored for workspace resolution, which was removed in v0.8 / #1440); it's unrelated to this move.🤖 Generated with Claude Code