Skip to content

refactor(agent): move Claude core-agent launchers into src/agent/claude/cli/ - #1891

Merged
jsun-m merged 4 commits into
sonichi:mainfrom
jsun-m:feat/agent-claude-restructure
Jul 1, 2026
Merged

jsun-m merged 4 commits into
sonichi:mainfrom
jsun-m:feat/agent-claude-restructure

Conversation

@jsun-m

@jsun-m jsun-m commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

What

Moves the two scripts that initialize + onboard the Claude Code core agent out of scripts/ into a dedicated src/agent/claude/cli/:

  • scripts/start-cli.sh → src/agent/claude/cli/start-cli.sh — the canonical sutando-core tmux launcher/restarter
  • scripts/sutando-shell-setup.sh → src/agent/claude/cli/sutando-shell-setup.sh — the claude-sutando config-dir alias onboarding

Why

The core-agent launch/onboarding logic was scattered in scripts/. Consolidating it under src/agent/claude/ gives it a clear home — and room for a second initialization flow (a headless / alternate-provider launcher) to grow alongside it without more scripts/ sprawl. git mv preserves history.

Changes

  • Re-points every caller: 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 in sutando_config.py / sutando_config.ts / util_paths.ts.
  • Fixes repo-root self-location in both scripts: 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.
  • Updates the affected tests — path constants + the fake-repo integration-test layouts that copy the real script and rely on its self-location.
  • Adds 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, and health-check-recover-core all pass. start-cli-claude-config-dir is 3/4 — the single failure is pre-existing on main (its expectation assumes $SUTANDO_WORKSPACE is honored for workspace resolution, which was removed in v0.8 / #1440); it's unrelated to this move.

🤖 Generated with Claude Code

…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>
jsun-m added a commit to jsun-m/sutando that referenced this pull request Jul 1, 2026
…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>
@jsun-m
jsun-m force-pushed the feat/agent-claude-restructure branch from 6cebfcf to cdf1c28 Compare July 1, 2026 22:53
jsun-m added a commit to jsun-m/sutando that referenced this pull request Jul 1, 2026
…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>
jsun-m added a commit to jsun-m/sutando that referenced this pull request Jul 1, 2026
…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>
@jsun-m
jsun-m force-pushed the feat/agent-claude-restructure branch from cdf1c28 to c76c277 Compare July 1, 2026 22:58
jsun-m added a commit to jsun-m/sutando that referenced this pull request Jul 1, 2026
…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>
jsun-m added a commit to jsun-m/sutando that referenced this pull request Jul 1, 2026
…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 john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

@cla-assistant check

@jsun-m
jsun-m merged commit 1d94039 into sonichi:main Jul 1, 2026
6 checks passed
jsun-m added a commit to jsun-m/sutando that referenced this pull request Jul 2, 2026
…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>
amkunte added a commit to amkunte/sutando that referenced this pull request Jul 13, 2026
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
john-the-dev added a commit that referenced this pull request Aug 24, 2026
#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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants