diff --git a/template/v4/Dockerfile b/template/v4/Dockerfile index 6852c125d..5de24923a 100644 --- a/template/v4/Dockerfile +++ b/template/v4/Dockerfile @@ -102,6 +102,8 @@ RUN apt-get update && apt-get upgrade -y && \ COPY dirs/ ${DIRECTORY_TREE_STAGE_DIR}/ RUN rsync -a ${DIRECTORY_TREE_STAGE_DIR}/ / && \ rm -rf ${DIRECTORY_TREE_STAGE_DIR} && \ + # Clone skills from manifest config (must run after COPY dirs/ + rsync which provides skills-manifest.json) + bash /etc/sagemaker/skills/clone_skills.sh && \ # CodeEditor - download the extensions mkdir -p /etc/code-editor/extensions /etc/code-editor/extensions-sagemaker-ui && \ while IFS= read -r url || [ -n "$url" ]; do \ diff --git a/template/v4/dirs/etc/sagemaker/sagemaker-default-agent.json b/template/v4/dirs/etc/sagemaker/sagemaker-default-agent.json new file mode 100644 index 000000000..844d994c0 --- /dev/null +++ b/template/v4/dirs/etc/sagemaker/sagemaker-default-agent.json @@ -0,0 +1,21 @@ +{ + "name": "sagemaker_default", + "description": "SageMaker AI assistant with pre-installed skills for model customization, evaluation, deployment, and HyperPod operations.", + "prompt": "You are a specialized AI assistant for Amazon SageMaker Studio. You can help with general purpose tasks, but you also have expertise in SageMaker workflows and skills.", + "mcpServers": {}, + "tools": ["*"], + "toolAliases": {}, + "allowedTools": [ + "@jupyter_mcp_server/read_notebook", + "@jupyter_mcp_server/read_notebook_cells", + "@jupyter_mcp_server/open_file", + "@jupyter_mcp_server/select_cell", + "@jupyter_mcp_server/get_active_notebook", + "@jupyter_mcp_server/get_active_cell_id", + "@jupyter_mcp_server/get_open_documents" + ], + "resources": ["skill://.kiro/skills/**/SKILL.md"], + "hooks": {}, + "toolsSettings": {}, + "includeMcpJson": true +} diff --git a/template/v4/dirs/etc/sagemaker/skills/clone_skills.sh b/template/v4/dirs/etc/sagemaker/skills/clone_skills.sh new file mode 100755 index 000000000..2127391ad --- /dev/null +++ b/template/v4/dirs/etc/sagemaker/skills/clone_skills.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Clones skills from repos listed in skills-manifest.json during Docker build. +set -eu + +MANIFEST="/etc/sagemaker/skills/skills-manifest.json" +DEST="/etc/sagemaker/skills" + +jq -c '.[]' "$MANIFEST" | while read -r entry; do + repo=$(echo "$entry" | jq -r '.repo') + spath=$(echo "$entry" | jq -r '.path') + git clone --depth 1 "$repo" /tmp/skill-repo + cp -r "/tmp/skill-repo/$spath"/* "$DEST"/ + rm -rf /tmp/skill-repo +done diff --git a/template/v4/dirs/etc/sagemaker/skills/skills-manifest.json b/template/v4/dirs/etc/sagemaker/skills/skills-manifest.json new file mode 100644 index 000000000..f1c73c692 --- /dev/null +++ b/template/v4/dirs/etc/sagemaker/skills/skills-manifest.json @@ -0,0 +1,6 @@ +[ + { + "repo": "https://github.com/awslabs/agent-plugins.git", + "path": "plugins/sagemaker-ai/skills" + } +] diff --git a/template/v4/dirs/etc/sagemaker/skills/sync_skills.sh b/template/v4/dirs/etc/sagemaker/skills/sync_skills.sh new file mode 100755 index 000000000..d46d7fdad --- /dev/null +++ b/template/v4/dirs/etc/sagemaker/skills/sync_skills.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# Syncs pre-packaged SageMaker skills from image to user's EBS. +set -eu + +IMAGE_SKILLS_DIR="/etc/sagemaker/skills" +EBS_SKILLS_DIR="$HOME/.agent/skills" +LOCK_FILE="$EBS_SKILLS_DIR/.sagemaker-lock" + +# Agent targets to symlink skills into (add new agents here) +AGENT_SKILLS_DIRS=("$HOME/.kiro/skills") + +compute_checksum() { + (cd "$1" && find . -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum | awk '{print $1}') +} + +get_locked_checksum() { + [ -f "$LOCK_FILE" ] && jq -r --arg s "$1" '.skills[$s].checksum // empty' "$LOCK_FILE" 2>/dev/null +} + +set_locked_checksum() { + [ -f "$LOCK_FILE" ] || echo '{"skills":{}}' > "$LOCK_FILE" + jq --arg s "$1" --arg c "$2" '.skills[$s].checksum = $c' "$LOCK_FILE" > "$LOCK_FILE.tmp" + mv "$LOCK_FILE.tmp" "$LOCK_FILE" +} + +mkdir -p "$EBS_SKILLS_DIR" +for dir in "${AGENT_SKILLS_DIRS[@]}"; do mkdir -p "$dir"; done + +if [ ! -d "$IMAGE_SKILLS_DIR" ]; then + echo "No bundled skills found at $IMAGE_SKILLS_DIR, skipping." + exit 0 +fi + +for skill_path in "$IMAGE_SKILLS_DIR"/*/; do + [ -d "$skill_path" ] || continue + skill_name=$(basename "$skill_path") + ebs_skill="$EBS_SKILLS_DIR/$skill_name" + image_checksum=$(compute_checksum "$skill_path") + + if [ ! -d "$ebs_skill" ]; then + cp -r "$skill_path" "$ebs_skill" + set_locked_checksum "$skill_name" "$image_checksum" + echo "Installed skill '$skill_name'" + else + recorded_checksum=$(get_locked_checksum "$skill_name") + current_checksum=$(compute_checksum "$ebs_skill") + + if [ "$current_checksum" = "$recorded_checksum" ]; then + if [ "$image_checksum" != "$recorded_checksum" ]; then + rm -rf "$ebs_skill" + cp -r "$skill_path" "$ebs_skill" + set_locked_checksum "$skill_name" "$image_checksum" + echo "Updated skill '$skill_name'" + else + echo "Skill '$skill_name' already current, skipping" + fi + else + echo "Skipping skill '$skill_name' — user modified" + fi + fi + + # Create symlinks for all agent targets + for agent_dir in "${AGENT_SKILLS_DIRS[@]}"; do + link="$agent_dir/$skill_name" + if [ ! -e "$link" ]; then + ln -s "$ebs_skill" "$link" + echo "Created symlink $link -> $ebs_skill" + fi + done +done + +echo "Skills sync complete." diff --git a/template/v4/dirs/etc/sagemaker/sync_agent.sh b/template/v4/dirs/etc/sagemaker/sync_agent.sh new file mode 100755 index 000000000..348a38a55 --- /dev/null +++ b/template/v4/dirs/etc/sagemaker/sync_agent.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# Syncs agent config (subagent, default agent settings) for SMAI spaces. +set -eu + +AGENT_CONFIG_SRC="/etc/sagemaker/sagemaker-default-agent.json" +AGENT_CONFIG_DST="$HOME/.kiro/agents/sagemaker-default-agent.json" +KIRO_CLI_SETTINGS="$HOME/.kiro/settings/cli.json" + +# Install subagent config (always overwrite) +mkdir -p "$HOME/.kiro/agents" +cp "$AGENT_CONFIG_SRC" "$AGENT_CONFIG_DST" +echo "Installed agent config to $AGENT_CONFIG_DST" + +# Set default agent only if user hasn't configured one +mkdir -p "$HOME/.kiro/settings" +if [ ! -f "$KIRO_CLI_SETTINGS" ]; then + echo '{"chat.defaultAgent":"sagemaker_default"}' > "$KIRO_CLI_SETTINGS" + echo "Created default agent settings" +elif ! jq -e '."chat.defaultAgent"' "$KIRO_CLI_SETTINGS" >/dev/null 2>&1; then + jq '. + {"chat.defaultAgent":"sagemaker_default"}' "$KIRO_CLI_SETTINGS" > "$KIRO_CLI_SETTINGS.tmp" + mv "$KIRO_CLI_SETTINGS.tmp" "$KIRO_CLI_SETTINGS" + echo "Added default agent to existing settings" +fi diff --git a/template/v4/dirs/usr/local/bin/start-jupyter-server b/template/v4/dirs/usr/local/bin/start-jupyter-server index 0b59c7024..5ffb153a8 100755 --- a/template/v4/dirs/usr/local/bin/start-jupyter-server +++ b/template/v4/dirs/usr/local/bin/start-jupyter-server @@ -14,6 +14,17 @@ else micromamba remove -y sagemaker-studio-dataengineering-sessions sagemaker-studio-dataengineering-extensions # Disable SMUS-specific extensions jupyter labextension disable sagemaker-data-explorer:plugin + # Disable old Q chat extension + jupyter labextension disable @amzn/amazon_sagemaker_jupyter_ai_q_developer +fi + +# Setup skills and subagent for SMAI private spaces only +if [ -n "$SAGEMAKER_APP_TYPE_LOWERCASE" ] && [ "$SAGEMAKER_SPACE_TYPE_LOWERCASE" != "shared" ]; then + # Sync pre-packaged skills to EBS + bash /etc/sagemaker/skills/sync_skills.sh 2>&1 || echo "Warning: skills sync failed, continuing..." + + # Sync agent config (subagent + default agent settings) + bash /etc/sagemaker/sync_agent.sh 2>&1 || echo "Warning: agent config sync failed, continuing..." fi # Enable S3AG plugin if TIP is enabled