diff --git a/.github/workflows/traiage.yaml b/.github/workflows/traiage.yaml index 708bc4611c..4e22978cdf 100644 --- a/.github/workflows/traiage.yaml +++ b/.github/workflows/traiage.yaml @@ -84,8 +84,6 @@ jobs: RUN_ID: ${{ github.run_id }} TEMPLATE_PARAMETERS: ${{ secrets.TRAIAGE_TEMPLATE_PARAMETERS }} TEMPLATE_PRESET: ${{ inputs.template_preset }} - # TODO: replace with coder exp task command - APP_SLUG: ccw run: | # Fetch issue description using `gh` CLI issue_description=$(gh issue view "${ISSUE_URL}") @@ -105,7 +103,7 @@ jobs: export TASK_NAME="${PREFIX}-${CONTEXT_KEY}-${RUN_ID}" echo "Creating task: $TASK_NAME" ./scripts/traiage.sh create - ./scripts/traiage.sh wait + coder exp task status "$TASK_NAME" --watch echo "TASK_NAME=${TASK_NAME}" >> "${GITHUB_OUTPUT}" echo "TASK_NAME=${TASK_NAME}" >> "${GITHUB_ENV}" @@ -122,7 +120,6 @@ jobs: id: generate-summary env: ARCHIVE_URL: ${{ steps.create-archive.outputs.archive_url }} - APP_SLUG: ccw BUCKET_PREFIX: "gs://coder-traiage-outputs/traiage" CONTEXT_KEY: ${{ steps.extract-context.outputs.context_key }} GITHUB_TOKEN: ${{ github.token }} diff --git a/scripts/traiage.sh b/scripts/traiage.sh index 78639885ed..05a3f9bbad 100755 --- a/scripts/traiage.sh +++ b/scripts/traiage.sh @@ -30,8 +30,8 @@ create() { set -e if [[ "${TASK_NAME}" == $(jq -r '.name' <<<"${task_json}") ]]; then - # TODO(Cian): Send PROMPT to the agent in the existing workspace. - echo "Task \"${TASK_NAME}\" already exists." + echo "Task \"${TASK_NAME}\" already exists. Sending prompt to existing task." + prompt exit 0 fi @@ -67,193 +67,55 @@ ssh_config() { } prompt() { - requiredenvs CODER_URL CODER_SESSION_TOKEN TASK_NAME APP_SLUG PROMPT + requiredenvs CODER_URL CODER_SESSION_TOKEN TASK_NAME PROMPT - wait_agentapi_stable + ${CODER_BIN} \ + --url "${CODER_URL}" \ + --token "${CODER_SESSION_TOKEN}" \ + exp tasks status "${TASK_NAME}" \ + --watch >/dev/null - username=$(curl \ - --fail \ - --header "Coder-Session-Token: ${CODER_SESSION_TOKEN}" \ - --location \ - --show-error \ - --silent \ - "${CODER_URL}/api/v2/users/me" | jq -r '.username') + ${CODER_BIN} \ + --url "${CODER_URL}" \ + --token "${CODER_SESSION_TOKEN}" \ + exp tasks send "${TASK_NAME}" \ + --stdin \ + <<<"${PROMPT}" - payload="{ - \"content\": \"${PROMPT}\", - \"type\": \"user\" - }" + ${CODER_BIN} \ + --url "${CODER_URL}" \ + --token "${CODER_SESSION_TOKEN}" \ + exp tasks status "${TASK_NAME}" \ + --watch >/dev/null - response=$(curl \ - --data-raw "${payload}" \ - --fail \ - --header "Content-Type: application/json" \ - --header "Coder-Session-Token: ${CODER_SESSION_TOKEN}" \ - --location \ - --request POST \ - --show-error \ - --silent \ - "${CODER_URL}/@${username}/${TASK_NAME}/apps/${APP_SLUG}/message" | jq -r '.ok') - if [[ "${response}" != "true" ]]; then - echo "Failed to send prompt" - exit 1 - fi - - # Wait for agentapi to process the response and return the last agent message - wait_agentapi_stable - - CODER_USERNAME="${username}" last_message + last_message } last_message() { - requiredenvs CODER_URL CODER_SESSION_TOKEN CODER_USERNAME TASK_NAME APP_SLUG PROMPT - last_msg=$(curl \ - --fail \ - --header "Content-Type: application/json" \ - --header "Coder-Session-Token: ${CODER_SESSION_TOKEN}" \ - --location \ - --show-error \ - --silent \ - "${CODER_URL}/@${CODER_USERNAME}/${TASK_NAME}/apps/${APP_SLUG}/messages" | - jq -r 'last(.messages[] | select(.role=="agent") | [.])') + requiredenvs CODER_URL CODER_SESSION_TOKEN TASK_NAME PROMPT + last_msg_json=$( + ${CODER_BIN} \ + --url "${CODER_URL}" \ + --token "${CODER_SESSION_TOKEN}" \ + exp tasks logs "${TASK_NAME}" \ + --output json + ) + last_output_msg=$(jq -r 'last(.[] | select(.type=="output")) | .content' <<<"${last_msg_json}") + # HACK: agentapi currently doesn't split multiple messages, so you can end up with tool + # call responses in the output. + last_msg=$(tac <<<"${last_output_msg}" | sed '/^● /q' | tr -d '●' | tac) echo "${last_msg}" } wait_agentapi_stable() { - requiredenvs CODER_URL CODER_SESSION_TOKEN TASK_NAME APP_SLUG - username=$(curl \ - --fail \ - --header "Coder-Session-Token: ${CODER_SESSION_TOKEN}" \ - --location \ - --show-error \ - --silent \ - "${CODER_URL}/api/v2/users/me" | jq -r '.username') + requiredenvs CODER_URL CODER_SESSION_TOKEN TASK_NAME - for attempt in {1..120}; do - # First wait for task to start - set +o pipefail - task_status=$("${CODER_BIN}" \ - --url "${CODER_URL}" \ - --token "${CODER_SESSION_TOKEN}" \ - exp tasks status "${TASK_NAME}" \ - --output json | - jq -r '.status') - set -o pipefail - echo "Task status is ${task_status}" - if [[ "${task_status}" == "running" ]]; then - echo "Task is running" - break - fi - echo "Waiting for task status to be running (attempt ${attempt}/120)" - sleep 5 - done - - for attempt in {1..120}; do - # Workspace agent must be healthy - set +o pipefail - healthy=$("${CODER_BIN}" \ - --url "${CODER_URL}" \ - --token "${CODER_SESSION_TOKEN}" \ - exp tasks status "${TASK_NAME}" \ - --output json | - jq -r '.workspace_agent_health.healthy') - set -o pipefail - if [[ "${healthy}" == "true" ]]; then - echo "Workspace agent is healthy" - break - fi - echo "Workspace agent not yet healthy (attempt ${attempt}/120)" - sleep 5 - done - - for attempt in {1..120}; do - # AgentAPI application should not be 404'ing - agentapi_app_status_code=$(curl \ - --header "Content-Type: application/json" \ - --header "Coder-Session-Token: ${CODER_SESSION_TOKEN}" \ - --location \ - --request GET \ - --show-error \ - --silent \ - --output /dev/null \ - --write-out '%{http_code}' \ - "${CODER_URL}/@${username}/${TASK_NAME}/apps/${APP_SLUG}/status") - echo "Workspace app ${APP_SLUG} returned ${agentapi_app_status_code}" - if [[ "${agentapi_app_status_code}" == "200" ]]; then - echo "AgentAPI is running" - break - fi - echo "AgentAPI not yet running (attempt ${attempt}/120)" - sleep 5 - done - - for attempt in {1..120}; do - # AgentAPI must be stable - set +o pipefail - agentapi_status=$(curl \ - --header "Content-Type: application/json" \ - --header "Coder-Session-Token: ${CODER_SESSION_TOKEN}" \ - --location \ - --request GET \ - --show-error \ - --silent \ - "${CODER_URL}/@${username}/${TASK_NAME}/apps/${APP_SLUG}/status" | jq -r '.status') - set -o pipefail - if [[ "${agentapi_status}" == "stable" ]]; then - echo "AgentAPI stable" - break - fi - echo "Waiting for AgentAPI to report stable status (attempt ${attempt}/120)" - sleep 5 - done - - # At this point the task is running, the workspace agent is healthy, - # the AgentAPI app is running and the AgentAPI reports stable status. - # Now we wait for the Task Agent to report 'idle', 'complete', or 'failure'. - for attempt in {1..120}; do - task_json=$(${CODER_BIN} \ - --url "${CODER_URL}" \ - --token "${CODER_SESSION_TOKEN}" \ - exp tasks status "${TASK_NAME}" \ - --output json) - set +o pipefail - current_state=$(jq -r '.current_state.state' <<<"${task_json}") - current_message=$(jq -r '.current_state.message' <<<"${task_json}") - set -o pipefail - - # The current_state or current_message may be null if the Task Agent - # has not yet reported its status. - if [[ -z "${current_state}" ]] || [[ -z "${current_message}" ]]; then - echo "Waiting for Task Agent to report state (attempt ${attempt}/120)" - sleep 5 - continue - fi - break - done - - # Finally, wait for the Task Agent to report 'idle', 'complete', or 'failure'. This is unbounded, as we don't know how long the agent will take. - while true; do - task_json=$(${CODER_BIN} \ - --url "${CODER_URL}" \ - --token "${CODER_SESSION_TOKEN}" \ - exp tasks status "${TASK_NAME}" \ - --output json) - set +o pipefail - - current_state=$(jq -r '.current_state.state' <<<"${task_json}") - current_message=$(jq -r '.current_state.message' <<<"${task_json}") - set -o pipefail - if [[ "${current_state}" == "working" ]]; then - echo "Task Agent is ${current_state} and reports: \"${current_message}\"" - echo "Waiting for Task Agent to report idle state (attempt ${attempt}/120)" - sleep 5 - continue - else - echo "Task Agent is ${current_state} and reports: \"${current_message}\"" - break - fi - done + ${CODER_BIN} \ + --url "${CODER_URL}" \ + --token "${CODER_SESSION_TOKEN}" \ + exp tasks status "${TASK_NAME}" \ + --watch } archive() { @@ -287,7 +149,7 @@ archive() { } summary() { - requiredenvs CODER_URL CODER_SESSION_TOKEN TASK_NAME APP_SLUG + requiredenvs CODER_URL CODER_SESSION_TOKEN TASK_NAME ssh_config # We want the heredoc to be expanded locally and not remotely.