@rallycry/conveyor-agent 10.13.9 → 10.13.11

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.
@@ -34,9 +34,8 @@ start_workspace_sshd() {
34
34
  # The postgres / firebase wait moved into `conveyor-agent` (setup waitForSidecars())
35
35
  # so it gates only the project setupCommand/startCommand and never the agent's
36
36
  # thinking loop — even on-demand pods now launch the agent without blocking on the
37
- # slow firebase emulator. The agent keeps the same per-target deadlines learned
38
- # here (postgres 30s, firebase 60s). See:
39
- # docs/superpowers/specs/2026-06-28-agent-launch-before-sidecar-setup-design.md
37
+ # slow firebase emulator. The agent keeps separate per-target deadlines here
38
+ # (postgres 30s, firebase 60s).
40
39
 
41
40
  # ── Refresh agent runner to latest before launch (payload-independent) ──
42
41
  # The image version can be stale vs. what the API expects (protocol/contract
@@ -143,7 +142,20 @@ reclaim_home_ownership() {
143
142
  done
144
143
  }
145
144
 
146
- start_workspace_sshd
145
+ # ── Container role (workbench split) ──
146
+ # Split-mode pods run TWO app containers from this same entrypoint:
147
+ # agent — supervisor + conveyor-agent only (protected, restartPolicy
148
+ # Never semantics); no repo, no sshd, no git prep.
149
+ # workbench — the workspace: git prep, sshd, claude + all workloads behind
150
+ # the launcher daemon (native sidecar, restartPolicy Always).
151
+ # Unset = today's monolith container; every guard below must be a no-op then.
152
+ CONTAINER_ROLE="${CONVEYOR_CONTAINER_ROLE:-}"
153
+
154
+ # Split-mode pods: SSH attach lands in the WORKBENCH container (the workspace
155
+ # lives there); the agent container skips sshd.
156
+ if [ "${CONTAINER_ROLE}" != "agent" ]; then
157
+ start_workspace_sshd
158
+ fi
147
159
 
148
160
  # ── Required env vars (injected by pod spec — v3: exactly these two) ──
149
161
  : "${CONVEYOR_API_URL:?CONVEYOR_API_URL is required}"
@@ -166,8 +178,7 @@ reclaim_home_ownership
166
178
 
167
179
  # ═══════════════════════════════════════════════════════════════════════════
168
180
  # PULL-BASED BOOTSTRAP — poll until the reconciler binds this pod to a
169
- # Workspace. 204 = still unbound; 200 = full bundle. No HMAC, no hostname
170
- # derivation, no exec-push, no /tmp/assignment.json.
181
+ # Workspace. 204 = still unbound; 200 = full bundle.
171
182
  # ═══════════════════════════════════════════════════════════════════════════
172
183
  # Bash runs as PID 1: without a trap, SIGTERM is ignored during standby and
173
184
  # every pool drain/scale-down rides the full terminationGracePeriod (180s) to
@@ -442,6 +453,13 @@ if [ -n "${CONVEYOR_USER_ID}" ] && [ -n "${USER_HOME_PROJECT_ID}" ] && [ -d "${U
442
453
  # blocks wrong-account attribution.
443
454
  CRED_SHARED="${USER_HOME_ROOT}/.claude/.credentials.json"
444
455
  CRED_POD_LOCAL="/home/conveyor/.claude-credentials.pod.json"
456
+ # Split-mode pods: the agent container WRITES credentials (harness auth) and
457
+ # claude in the workbench READS them through the shared-dir symlink — so the
458
+ # pod-local file must live on the shared emptyDir. Still pod-local, so the
459
+ # concurrent-pods clobber fix above is preserved.
460
+ if [ -n "${CONVEYOR_SHARED_DIR:-}" ]; then
461
+ CRED_POD_LOCAL="${CONVEYOR_SHARED_DIR}/claude-credentials.pod.json"
462
+ fi
445
463
  if [ -f "${CRED_SHARED}" ] && [ ! -L "${CRED_SHARED}" ]; then
446
464
  # Legacy shared regular file: carry its contents into this pod so auth
447
465
  # survives the cutover; the agent re-synthesizes from its own token at
@@ -464,6 +482,14 @@ else
464
482
  # No persistent home — the CLI reads the pod-local config; seed it there so
465
483
  # non-FUSE pods get the same first-run gate suppression as the mounted path.
466
484
  seed_claude_json /home/conveyor/.claude.json
485
+ # Split-mode pods without a persistent user-home: ~/.claude must still cross
486
+ # the container boundary (claude writes transcripts in the workbench; the
487
+ # agent's tailer reads them), so it lives on the shared emptyDir.
488
+ if [ -n "${CONVEYOR_SHARED_DIR:-}" ]; then
489
+ mkdir -p "${CONVEYOR_SHARED_DIR}/claude-home/.claude" 2>/dev/null || true
490
+ rm -rf /home/conveyor/.claude 2>/dev/null || true
491
+ ln -sfn "${CONVEYOR_SHARED_DIR}/claude-home/.claude" /home/conveyor/.claude
492
+ fi
467
493
  boot_mark "no user-home mount — pod-local seed only"
468
494
  fi
469
495
 
@@ -636,6 +662,12 @@ configure_graphify_env
636
662
  # ═══════════════════════════════════════════════════════════════════════════
637
663
  GIT_READY_MARKER="/workspaces/.conveyor-git-ready"
638
664
  GIT_FAILED_MARKER="/workspaces/.conveyor-git-failed"
665
+ # Split-mode pods: the markers live on the shared emptyDir so the workbench's
666
+ # git prep is visible to the agent container (which never sees /workspaces).
667
+ if [ -n "${CONVEYOR_SHARED_DIR:-}" ]; then
668
+ GIT_READY_MARKER="${CONVEYOR_SHARED_DIR}/git-ready"
669
+ GIT_FAILED_MARKER="${CONVEYOR_SHARED_DIR}/git-failed"
670
+ fi
639
671
 
640
672
  reset_tracked_repo_changes_before_assignment_checkout() {
641
673
  # The baked/pooled repo is not user-owned until this git gate succeeds. Reset
@@ -878,15 +910,23 @@ prepare_workspace_git() {
878
910
  # A leftover ready marker from a PRIOR pod on the baked image would be
879
911
  # catastrophic: the agent would spawn Claude before THIS pod's git runs. Clear
880
912
  # both before starting the background prep.
881
- rm -f "$GIT_READY_MARKER" "$GIT_FAILED_MARKER"
882
- # Ensure the repo dir exists for launch on BOTH paths (the background clone
883
- # populates an empty dir on the non-image path; the pod-image path already has
884
- # repo/.git).
885
- mkdir -p /workspaces/repo
913
+ #
914
+ # Split-mode agent container: the WORKBENCH owns the repo and the git prep and
915
+ # writes the shared markers; this container only reads them. It must not clear
916
+ # them either — the workbench may have already written ready.
917
+ if [ "${CONTAINER_ROLE}" != "agent" ]; then
918
+ rm -f "$GIT_READY_MARKER" "$GIT_FAILED_MARKER"
919
+ # Ensure the repo dir exists for launch on BOTH paths (the background clone
920
+ # populates an empty dir on the non-image path; the pod-image path already has
921
+ # repo/.git).
922
+ mkdir -p /workspaces/repo
886
923
 
887
- # Run the full git prep in the background so the agent can connect (card lights
888
- # up) and stream setup output while git finishes.
889
- prepare_workspace_git &
924
+ # Run the full git prep in the background so the agent can connect (card lights
925
+ # up) and stream setup output while git finishes.
926
+ prepare_workspace_git &
927
+ else
928
+ echo "[pool] agent role — skipping git prep (workbench owns the workspace)"
929
+ fi
890
930
 
891
931
  # Signal to the agent that bash owns the git block (skip the agent's OWN
892
932
  # git-sync fallback) and that it must await the ready marker before spawning
@@ -897,10 +937,52 @@ export CONVEYOR_GIT_FAILED_MARKER="$GIT_FAILED_MARKER"
897
937
  # Target the repo regardless of when the background clone lands.
898
938
  export CONVEYOR_WORKSPACE=/workspaces/repo
899
939
 
940
+ # ── Workbench container (split-mode): exec the launcher daemon as PID 1 ──
941
+ # No crash loop here — the workbench is a native sidecar (restartPolicy
942
+ # Always); the kubelet owns restarts. Git prep above ran in THIS container
943
+ # (the repo lives here); the daemon serves the agent container's exec/pty/
944
+ # snapshot/file operations over loopback (token-authed, see workbench/server).
945
+ if [ "${CONTAINER_ROLE}" = "workbench" ]; then
946
+ # Bounded git-prep retry, backgrounded so the daemon starts immediately
947
+ # (the startupProbe gates pod readiness on the daemon listening). The repo
948
+ # lives HERE, so the retry must run here — the agent container's launch-loop
949
+ # retry is role-guarded off (found live 2026-07-16: an unguarded agent-side
950
+ # retry cloned the repo into the agent's own dead overlay).
951
+ (
952
+ _wb_git_attempts=0
953
+ while true; do
954
+ sleep 10
955
+ [ -f "$GIT_READY_MARKER" ] && break
956
+ if [ -f "$GIT_FAILED_MARKER" ]; then
957
+ if [ "$_wb_git_attempts" -ge 3 ]; then
958
+ echo "[pool] workbench git prep failed 3 times — giving up (agent surfaces the failure)."
959
+ break
960
+ fi
961
+ _wb_git_attempts=$((_wb_git_attempts + 1))
962
+ echo "[pool] workbench git prep failed — retrying (attempt ${_wb_git_attempts}/3)..."
963
+ rm -f "$GIT_READY_MARKER" "$GIT_FAILED_MARKER"
964
+ prepare_workspace_git
965
+ fi
966
+ done
967
+ ) &
968
+ cd /workspaces/repo
969
+ boot_mark "launching workbench daemon (env-inject → daemon-launch total)"
970
+ echo "[pool] Launching workbench launcher daemon..."
971
+ export CONVEYOR_MODE=workbench
972
+ exec conveyor-agent
973
+ fi
974
+
900
975
  # Launch agent — exit-code-aware restart loop.
901
976
  # Exit 0 = clean shutdown (idle timeout, task complete) — pod dies.
902
977
  # Non-zero = crash — retry after a brief pause.
903
- cd /workspaces/repo
978
+ #
979
+ # Split-mode agent container: /workspaces/repo in THIS container is a stale
980
+ # baked overlay, never the live tree — cwd must not resolve into it.
981
+ if [ "${CONTAINER_ROLE}" = "agent" ]; then
982
+ cd /home/conveyor
983
+ else
984
+ cd /workspaces/repo
985
+ fi
904
986
  boot_mark "launching agent (env-inject → agent-launch total)"
905
987
  echo "[pool] Launching agent..."
906
988
  set +e
@@ -935,7 +1017,9 @@ report_agent_crash() {
935
1017
  }
936
1018
 
937
1019
  while true; do
938
- if [ -f "$GIT_FAILED_MARKER" ]; then
1020
+ # Split-mode agent container: the workbench owns git prep AND its retry —
1021
+ # re-running prep here would clone into this container's dead overlay.
1022
+ if [ -f "$GIT_FAILED_MARKER" ] && [ "${CONTAINER_ROLE}" != "agent" ]; then
939
1023
  if [ "$_git_prep_attempts" -lt "$GIT_PREP_MAX_RETRIES" ]; then
940
1024
  _git_prep_attempts=$((_git_prep_attempts + 1))
941
1025
  echo "[pool] git prep previously failed — retrying (attempt ${_git_prep_attempts}/${GIT_PREP_MAX_RETRIES})..."