@@ -0,0 +1,5 @@
|
|||||||
|
# SOUL - researcher
|
||||||
|
|
||||||
|
You are the researcher seat of the Mosaic fleet. You are curious, methodical,
|
||||||
|
and precise. You cite what you know, admit what you do not, and never guess
|
||||||
|
when you can verify.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"agentVersion": 1,
|
||||||
|
"name": "researcher",
|
||||||
|
"role": "researcher",
|
||||||
|
"capabilities": { "tools": ["read", "bash"] }
|
||||||
|
}
|
||||||
@@ -24,6 +24,8 @@ services:
|
|||||||
# Interactive TUI mode + agent identity (M13, set by scripts/agent.sh)
|
# Interactive TUI mode + agent identity (M13, set by scripts/agent.sh)
|
||||||
MOSAIC_INTERACTIVE: ${MOSAIC_INTERACTIVE:-}
|
MOSAIC_INTERACTIVE: ${MOSAIC_INTERACTIVE:-}
|
||||||
MOSAIC_AGENT_NAME: ${MOSAIC_AGENT_NAME:-}
|
MOSAIC_AGENT_NAME: ${MOSAIC_AGENT_NAME:-}
|
||||||
|
MOSAIC_AGENT_ROLE: ${MOSAIC_AGENT_ROLE:-}
|
||||||
|
MOSAIC_AGENT_SOUL_FILE: ${MOSAIC_AGENT_SOUL_FILE:-}
|
||||||
# mock adapter only: verbatim response for deterministic seam tests
|
# mock adapter only: verbatim response for deterministic seam tests
|
||||||
MOSAIC_MOCK_RESPONSE: ${MOSAIC_MOCK_RESPONSE:-}
|
MOSAIC_MOCK_RESPONSE: ${MOSAIC_MOCK_RESPONSE:-}
|
||||||
# Documented container auth alternative: provider API key via
|
# Documented container auth alternative: provider API key via
|
||||||
|
|||||||
+56
-5
@@ -41,11 +41,60 @@ load_config
|
|||||||
load_release
|
load_release
|
||||||
bootstrap_runtime_dir
|
bootstrap_runtime_dir
|
||||||
|
|
||||||
|
# Agent seat definition (M15): when agents/<name>/agent.json exists it is
|
||||||
|
# strictly validated and its values become defaults (CLI flags override).
|
||||||
|
# The seat's SOUL.md overrides the contract persona; governance contracts
|
||||||
|
# are never overridden.
|
||||||
|
AGENTS_DIR="${MOSAIC_AGENTS_DIR:-agents}"
|
||||||
|
ROLE=""
|
||||||
|
DEFCAPS=""
|
||||||
|
if [ -f "$AGENTS_DIR/$NAME/agent.json" ]; then
|
||||||
|
DEFAULTS_FILE="$(mktemp)"
|
||||||
|
node -e '
|
||||||
|
const fs = require("fs");
|
||||||
|
const p = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
||||||
|
if (p.agentVersion !== 1) process.exit(2);
|
||||||
|
const ID = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
||||||
|
if (typeof p.name !== "string" || !ID.test(p.name)) process.exit(2);
|
||||||
|
if (p.role !== undefined && (typeof p.role !== "string" || !ID.test(p.role))) process.exit(2);
|
||||||
|
let tools = "";
|
||||||
|
if (p.capabilities !== undefined) {
|
||||||
|
if (typeof p.capabilities !== "object" || p.capabilities === null || Array.isArray(p.capabilities)) process.exit(2);
|
||||||
|
for (const k of Object.keys(p.capabilities)) if (k !== "tools") process.exit(2);
|
||||||
|
if (!Array.isArray(p.capabilities.tools) || p.capabilities.tools.some(t => !/^[a-z]+$/.test(t))) process.exit(2);
|
||||||
|
tools = p.capabilities.tools.join(",");
|
||||||
|
}
|
||||||
|
fs.writeFileSync(process.argv[2], "AGENT_DEF_ROLE=" + (p.role || "") + "\nAGENT_DEF_CAPS=" + tools + "\n");
|
||||||
|
' "$AGENTS_DIR/$NAME/agent.json" "$DEFAULTS_FILE" || { rm -f "$DEFAULTS_FILE"; echo "agent: invalid agent definition" >&2; exit 2; }
|
||||||
|
AGENT_DEF_ROLE=""; AGENT_DEF_CAPS=""
|
||||||
|
while IFS= read -r line; do
|
||||||
|
case "$line" in
|
||||||
|
AGENT_DEF_ROLE=*) AGENT_DEF_ROLE="${line#AGENT_DEF_ROLE=}" ;;
|
||||||
|
AGENT_DEF_CAPS=*) AGENT_DEF_CAPS="${line#AGENT_DEF_CAPS=}" ;;
|
||||||
|
esac
|
||||||
|
done < "$DEFAULTS_FILE"
|
||||||
|
rm -f "$DEFAULTS_FILE"
|
||||||
|
ROLE="$AGENT_DEF_ROLE"
|
||||||
|
DEFCAPS="$AGENT_DEF_CAPS"
|
||||||
|
[ -r "$AGENTS_DIR/$NAME/SOUL.md" ] || { echo "agent: definition dir missing SOUL.md: $AGENTS_DIR/$NAME" >&2; exit 4; }
|
||||||
|
mkdir -p "$MOSAIC_DEV_DIR/agents/$NAME"
|
||||||
|
cp "$AGENTS_DIR/$NAME/SOUL.md" "$MOSAIC_DEV_DIR/agents/$NAME/SOUL.md"
|
||||||
|
export MOSAIC_AGENT_SOUL_FILE="/var/lib/mosaic/agents/$NAME/SOUL.md"
|
||||||
|
# Seat record: written once at instantiation.
|
||||||
|
SEAT="$MOSAIC_DEV_DIR/agents/$NAME/seat.json"
|
||||||
|
if [ ! -f "$SEAT" ]; then
|
||||||
|
printf '{"seatVersion":1,"name":"%s","role":"%s","instantiatedAt":"%s"}\n' \
|
||||||
|
"$NAME" "$ROLE" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$SEAT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
SESSION="${SESSION:-agent-$NAME}"
|
SESSION="${SESSION:-agent-$NAME}"
|
||||||
mkdir -p "$MOSAIC_DEV_DIR/sessions/$SESSION"
|
mkdir -p "$MOSAIC_DEV_DIR/sessions/$SESSION"
|
||||||
export MOSAIC_SESSION_DIR="/var/lib/mosaic/sessions/$SESSION"
|
export MOSAIC_SESSION_DIR="/var/lib/mosaic/sessions/$SESSION"
|
||||||
export MOSAIC_AGENT_NAME="$NAME"
|
export MOSAIC_AGENT_NAME="$NAME"
|
||||||
|
[ -n "$ROLE" ] && export MOSAIC_AGENT_ROLE="$ROLE"
|
||||||
export MOSAIC_INTERACTIVE=1
|
export MOSAIC_INTERACTIVE=1
|
||||||
|
if [ -z "$TOOLS" ] && [ -n "$DEFCAPS" ]; then TOOLS="$DEFCAPS"; fi
|
||||||
export MOSAIC_TOOLS="${TOOLS:+$TOOLS}"
|
export MOSAIC_TOOLS="${TOOLS:+$TOOLS}"
|
||||||
|
|
||||||
if [ -n "$MISSION" ]; then
|
if [ -n "$MISSION" ]; then
|
||||||
@@ -55,11 +104,13 @@ if [ -n "$MISSION" ]; then
|
|||||||
export MOSAIC_MISSION_FILE="/var/lib/mosaic/agent-missions/$NAME.json"
|
export MOSAIC_MISSION_FILE="/var/lib/mosaic/agent-missions/$NAME.json"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$WORKSPACE" ]; then
|
# Workspace (M13): defaults to a persistent per-agent workspace
|
||||||
case "$WORKSPACE" in *[!A-Za-z0-9._-]*|'') echo "agent: invalid workspace name" >&2; exit 4;; esac
|
# (workspaces/<agent>) so the agent has a real, host-visible home instead
|
||||||
mkdir -p "$MOSAIC_DEV_DIR/workspaces/$WORKSPACE"
|
# of the container's neutral /workspace. Override with --workspace <ws>.
|
||||||
export MOSAIC_WORKSPACE="/var/lib/mosaic/workspaces/$WORKSPACE"
|
[ -n "$WORKSPACE" ] || WORKSPACE="$NAME"
|
||||||
fi
|
case "$WORKSPACE" in *[!A-Za-z0-9._-]*|'') echo "agent: invalid workspace name" >&2; exit 4;; esac
|
||||||
|
mkdir -p "$MOSAIC_DEV_DIR/workspaces/$WORKSPACE"
|
||||||
|
export MOSAIC_WORKSPACE="/var/lib/mosaic/workspaces/$WORKSPACE"
|
||||||
|
|
||||||
echo "agent: launching TUI agent '$NAME' (session: $SESSION, adapter: $MOSAIC_ADAPTER, model: $MOSAIC_MODEL)"
|
echo "agent: launching TUI agent '$NAME' (session: $SESSION, adapter: $MOSAIC_ADAPTER, model: $MOSAIC_MODEL)"
|
||||||
echo "agent: contracts + $([ -n "$MISSION" ] && echo 'mission' || echo 'no mission') loaded; exit the TUI with /quit"
|
echo "agent: contracts + $([ -n "$MISSION" ] && echo 'mission' || echo 'no mission') loaded; exit the TUI with /quit"
|
||||||
|
|||||||
@@ -50,4 +50,11 @@ bootstrap_runtime_dir() {
|
|||||||
echo "bootstrap: created $MOSAIC_DEV_DIR"
|
echo "bootstrap: created $MOSAIC_DEV_DIR"
|
||||||
fi
|
fi
|
||||||
touch "$MOSAIC_DEV_DIR/$POC_ROOT_MARKER"
|
touch "$MOSAIC_DEV_DIR/$POC_ROOT_MARKER"
|
||||||
|
# Live user context layer (M14): seeded once, owned by the user from
|
||||||
|
# then on; dispatched to every agent launch without rebuilds.
|
||||||
|
mkdir -p "$MOSAIC_DEV_DIR/user"
|
||||||
|
if [ ! -f "$MOSAIC_DEV_DIR/user/USER.md" ]; then
|
||||||
|
printf '# User\n\nDescribe yourself, your machine, and your preferences here.\nThis file is dispatched to every Mosaic agent launch.\n' \
|
||||||
|
> "$MOSAIC_DEV_DIR/user/USER.md"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,6 +236,17 @@ EOF
|
|||||||
printf '{"missionVersion":1,"id":"m-pol","objective":"o","capabilities":{"tools":["sudo"]}}' > "$SANDBOX/pol-m.json"
|
printf '{"missionVersion":1,"id":"m-pol","objective":"o","capabilities":{"tools":["sudo"]}}' > "$SANDBOX/pol-m.json"
|
||||||
expect_exit "invalid mission capabilities rejected" 2 -- \
|
expect_exit "invalid mission capabilities rejected" 2 -- \
|
||||||
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" $TASK validate "$SANDBOX/pol-t.json"
|
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" $TASK validate "$SANDBOX/pol-t.json"
|
||||||
|
|
||||||
|
# live user context (M14): dispatched to every launch without rebuild
|
||||||
|
mkdir -p "$SANDBOX/data/user"
|
||||||
|
printf '\nUSER-CANON-MARKER\n' >> "$SANDBOX/data/user/USER.md"
|
||||||
|
expect_exit "task run with user layer present" 0 -- \
|
||||||
|
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" MOSAIC_MOCK_RESPONSE=MOSAIC_HELLO_OK \
|
||||||
|
scripts/run-task.sh run "$SANDBOX/ok.json"
|
||||||
|
grep -q 'USER CONTEXT: USER.md' "$SANDBOX/data/system-prompt.md" \
|
||||||
|
&& grep -q 'USER-CANON-MARKER' "$SANDBOX/data/system-prompt.md" \
|
||||||
|
&& check "user context dispatched into generated prompt" 0 \
|
||||||
|
|| check "user context dispatched into generated prompt" 1
|
||||||
else
|
else
|
||||||
echo "skip adapter seam cases (docker daemon unavailable)"
|
echo "skip adapter seam cases (docker daemon unavailable)"
|
||||||
fi
|
fi
|
||||||
|
|||||||
+39
-4
@@ -1,14 +1,33 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Load the four immutable contract files in fixed order and write the
|
# Load agent context and write the generated system prompt to
|
||||||
# generated system prompt to /var/lib/mosaic/system-prompt.md.
|
# /var/lib/mosaic/system-prompt.md.
|
||||||
#
|
#
|
||||||
# Order is normative: CONSTITUTION.md, STANDARDS.md, SOUL.md, USER.md.
|
# Layers, in normative order:
|
||||||
|
# 1. Immutable contracts (image): CONSTITUTION, STANDARDS, SOUL
|
||||||
|
# 2. Agent identity (when the launcher names the agent)
|
||||||
|
# 3. Mission (when the task/launcher provides one)
|
||||||
|
# 4. Live user context (M14): <dataRoot>/user/*.md - user-owned,
|
||||||
|
# dispatched to every launch without rebuilds
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
CONTRACT_DIR="${1:-/opt/mosaic/contracts}"
|
CONTRACT_DIR="${1:-/opt/mosaic/contracts}"
|
||||||
OUT="${2:-/var/lib/mosaic/system-prompt.md}"
|
OUT="${2:-/var/lib/mosaic/system-prompt.md}"
|
||||||
|
|
||||||
FILES="CONSTITUTION.md STANDARDS.md SOUL.md USER.md"
|
FILES="CONSTITUTION.md STANDARDS.md"
|
||||||
|
|
||||||
|
# SOUL slot (M15): the contract SOUL.md is the DEFAULT persona; a launched
|
||||||
|
# agent seat overrides it with its own runtime SOUL (governance contracts
|
||||||
|
# are never overridden).
|
||||||
|
SOUL_SRC="$CONTRACT_DIR/SOUL.md"
|
||||||
|
SOUL_HEADER="SOUL.md"
|
||||||
|
if [ -n "${MOSAIC_AGENT_SOUL_FILE:-}" ]; then
|
||||||
|
if [ ! -r "$MOSAIC_AGENT_SOUL_FILE" ]; then
|
||||||
|
echo "load-contracts: agent SOUL not readable: $MOSAIC_AGENT_SOUL_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
SOUL_SRC="$MOSAIC_AGENT_SOUL_FILE"
|
||||||
|
SOUL_HEADER="SOUL.md (agent seat override)"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -d "$CONTRACT_DIR" ]; then
|
if [ ! -d "$CONTRACT_DIR" ]; then
|
||||||
echo "load-contracts: contract directory not found: $CONTRACT_DIR" >&2
|
echo "load-contracts: contract directory not found: $CONTRACT_DIR" >&2
|
||||||
@@ -33,14 +52,30 @@ for f in $FILES; do
|
|||||||
printf '\n' >> "$TEMP"
|
printf '\n' >> "$TEMP"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
printf '===== CONTRACT: %s =====\n' "$SOUL_HEADER" >> "$TEMP"
|
||||||
|
cat "$SOUL_SRC" >> "$TEMP"
|
||||||
|
printf '\n' >> "$TEMP"
|
||||||
|
|
||||||
# Agent identity (M13): when the launcher names the agent, the generated
|
# Agent identity (M13): when the launcher names the agent, the generated
|
||||||
# prompt states it - SOUL.md provides the persona, this provides the name.
|
# prompt states it - SOUL.md provides the persona, this provides the name.
|
||||||
if [ -n "${MOSAIC_AGENT_NAME:-}" ]; then
|
if [ -n "${MOSAIC_AGENT_NAME:-}" ]; then
|
||||||
printf '===== AGENT IDENTITY =====\n' >> "$TEMP"
|
printf '===== AGENT IDENTITY =====\n' >> "$TEMP"
|
||||||
printf 'agent name: %s\n' "$MOSAIC_AGENT_NAME" >> "$TEMP"
|
printf 'agent name: %s\n' "$MOSAIC_AGENT_NAME" >> "$TEMP"
|
||||||
|
[ -n "${MOSAIC_AGENT_ROLE:-}" ] && printf 'agent role: %s\n' "$MOSAIC_AGENT_ROLE" >> "$TEMP"
|
||||||
printf '\n' >> "$TEMP"
|
printf '\n' >> "$TEMP"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Live user context (M14): every *.md in /var/lib/mosaic/user (sorted) is
|
||||||
|
# appended - the user owns this layer and edits it without rebuilds.
|
||||||
|
USER_DIR="/var/lib/mosaic/user"
|
||||||
|
if [ -d "$USER_DIR" ]; then
|
||||||
|
for f in $(ls "$USER_DIR"/*.md 2>/dev/null | sort); do
|
||||||
|
printf '===== USER CONTEXT: %s =====\n' "$(basename "$f")" >> "$TEMP"
|
||||||
|
cat "$f" >> "$TEMP"
|
||||||
|
printf '\n' >> "$TEMP"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
# Sanctioned mission injection point (M4): when the task runner provides a
|
# Sanctioned mission injection point (M4): when the task runner provides a
|
||||||
# mission snapshot, its objective and directives are appended AFTER the
|
# mission snapshot, its objective and directives are appended AFTER the
|
||||||
# immutable contracts. Runtime data; never part of the contract fixtures.
|
# immutable contracts. Runtime data; never part of the contract fixtures.
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
# POC user
|
# POC user
|
||||||
|
|
||||||
This is an isolated local runtime test.
|
This is an isolated local runtime test.
|
||||||
|
|
||||||
|
The user's name is Jason.
|
||||||
Reference in New Issue
Block a user