feat(bootstrap): add shared session bootstrap and machine-alias resolver
This commit is contained in:
14
policies/MACHINE-ALIASES.yaml
Normal file
14
policies/MACHINE-ALIASES.yaml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
version: 1
|
||||||
|
machines:
|
||||||
|
- hostname: mac-5
|
||||||
|
aliases: mac5,control,openclaw-control
|
||||||
|
default_agent_suffix: mac5
|
||||||
|
- hostname: lingyuzeng-X99-TI-D4-PLUS
|
||||||
|
aliases: rtx2080ti,gpu-gateway,group
|
||||||
|
default_agent_suffix: rtx2080ti
|
||||||
|
- hostname: mac-6
|
||||||
|
aliases: mac6,executor,node-exec
|
||||||
|
default_agent_suffix: mac6
|
||||||
|
- hostname: mac-7
|
||||||
|
aliases: mac7,browser,node-browser
|
||||||
|
default_agent_suffix: mac7
|
||||||
48
policies/SESSION-BOOTSTRAP.md
Normal file
48
policies/SESSION-BOOTSTRAP.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# SESSION BOOTSTRAP (Shared Memory)
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
- 所有客户端(OpenClaw / Claude / Codex / Cursor)共享同一记忆系统。
|
||||||
|
- Durable memory authority 是 `collective-memory-repo`。
|
||||||
|
- 群体权威召回只认远程 `memory-gateway`。
|
||||||
|
|
||||||
|
## 统一架构
|
||||||
|
- Memory repo: `/Users/lingyuzeng/project/collective-memory-repo`
|
||||||
|
- Gateway URL: `http://100.64.0.45:8787`
|
||||||
|
- Gateway MCP URL: `http://100.64.0.45:8787/mcp`
|
||||||
|
- 记忆层规则:
|
||||||
|
- 共享长期事实:`shared/long-term/`
|
||||||
|
- 每日记录:`daily/`
|
||||||
|
- 任务协作:`tasks/<task-id>/`
|
||||||
|
- Agent 私域:`agents/<agent-id>/`
|
||||||
|
|
||||||
|
## 分支规则
|
||||||
|
- 长期协作默认:`branch=main`
|
||||||
|
- 并发任务协作:`branch=task/<task-id>`
|
||||||
|
- 群体查询必须带:`require_latest=true`
|
||||||
|
|
||||||
|
## 读写规则
|
||||||
|
- Runtime 文件(persona/tools/heartbeat)留在各自本机 runtime 目录。
|
||||||
|
- Durable facts 只能写入 `collective-memory-repo` 的对应 lane。
|
||||||
|
- `vaults/memory/*` 是兼容区,不是长期真相源。
|
||||||
|
|
||||||
|
## 会话初始化必填上下文
|
||||||
|
在新会话第一条消息中提供:
|
||||||
|
- `machine_hostname`
|
||||||
|
- `machine_alias`
|
||||||
|
- `agent_id`
|
||||||
|
- `memory_branch`(默认 `main`)
|
||||||
|
- `task_id`(如有)
|
||||||
|
|
||||||
|
## 示例(首条消息)
|
||||||
|
```text
|
||||||
|
@/Users/lingyuzeng/project/collective-memory-repo/policies/SESSION-BOOTSTRAP.md
|
||||||
|
machine_hostname=mac-5
|
||||||
|
machine_alias=mac5
|
||||||
|
agent_id=codex-mac5
|
||||||
|
memory_branch=main
|
||||||
|
```
|
||||||
|
|
||||||
|
## MCP 接入要求
|
||||||
|
- 先在客户端注册 MCP server(URL 指向 `...:8787/mcp`)。
|
||||||
|
- 再通过 `@SESSION-BOOTSTRAP.md` 注入架构与规则。
|
||||||
|
- 客户端本地检索可保留,但群体结论以 gateway 结果为准。
|
||||||
75
scripts/print-session-bootstrap.sh
Executable file
75
scripts/print-session-bootstrap.sh
Executable file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'USAGE'
|
||||||
|
Usage:
|
||||||
|
scripts/print-session-bootstrap.sh [--tool <tool>] [--branch <branch>] [--task-id <id>] [--gateway-url <url>] [--hostname <name>]
|
||||||
|
|
||||||
|
Prints a ready-to-paste first message for Claude/Codex/Cursor/OpenClaw sessions.
|
||||||
|
USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
TOOL="codex"
|
||||||
|
BRANCH="main"
|
||||||
|
TASK_ID=""
|
||||||
|
GATEWAY_URL="http://100.64.0.45:8787"
|
||||||
|
HOSTNAME_INPUT=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--tool)
|
||||||
|
TOOL="${2:-}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--branch)
|
||||||
|
BRANCH="${2:-}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--task-id)
|
||||||
|
TASK_ID="${2:-}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--gateway-url)
|
||||||
|
GATEWAY_URL="${2:-}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--hostname)
|
||||||
|
HOSTNAME_INPUT="${2:-}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "ERROR: unknown arg: $1" >&2
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||||
|
ARGS=(--field all --tool "$TOOL")
|
||||||
|
if [[ -n "$HOSTNAME_INPUT" ]]; then
|
||||||
|
ARGS+=(--hostname "$HOSTNAME_INPUT")
|
||||||
|
fi
|
||||||
|
INFO="$($REPO_ROOT/scripts/resolve-machine-alias.sh "${ARGS[@]}")"
|
||||||
|
|
||||||
|
hostname_v="$(printf '%s\n' "$INFO" | awk -F= '$1=="hostname"{print $2}')"
|
||||||
|
alias_v="$(printf '%s\n' "$INFO" | awk -F= '$1=="alias"{print $2}')"
|
||||||
|
agent_id_v="$(printf '%s\n' "$INFO" | awk -F= '$1=="agent_id"{print $2}')"
|
||||||
|
|
||||||
|
cat <<OUT
|
||||||
|
@/Users/lingyuzeng/project/collective-memory-repo/policies/SESSION-BOOTSTRAP.md
|
||||||
|
machine_hostname=${hostname_v}
|
||||||
|
machine_alias=${alias_v}
|
||||||
|
agent_id=${agent_id_v}
|
||||||
|
memory_branch=${BRANCH}
|
||||||
|
memory_gateway=${GATEWAY_URL}
|
||||||
|
OUT
|
||||||
|
|
||||||
|
if [[ -n "$TASK_ID" ]]; then
|
||||||
|
echo "task_id=${TASK_ID}"
|
||||||
|
fi
|
||||||
177
scripts/resolve-machine-alias.sh
Executable file
177
scripts/resolve-machine-alias.sh
Executable file
@@ -0,0 +1,177 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'USAGE'
|
||||||
|
Usage:
|
||||||
|
scripts/resolve-machine-alias.sh [--hostname <name>] [--field alias|suffix|agent-id|all] [--tool <tool>]
|
||||||
|
|
||||||
|
Defaults:
|
||||||
|
--hostname: current host shortname (hostname -s)
|
||||||
|
--field: all
|
||||||
|
--tool: openclaw
|
||||||
|
|
||||||
|
Reads mapping from policies/MACHINE-ALIASES.yaml.
|
||||||
|
USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
HOSTNAME_INPUT="$(hostname -s 2>/dev/null || hostname)"
|
||||||
|
FIELD="all"
|
||||||
|
TOOL="openclaw"
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--hostname)
|
||||||
|
HOSTNAME_INPUT="${2:-}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--field)
|
||||||
|
FIELD="${2:-}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--tool)
|
||||||
|
TOOL="${2:-}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "ERROR: unknown arg: $1" >&2
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
case "$FIELD" in
|
||||||
|
alias|suffix|agent-id|all) ;;
|
||||||
|
*)
|
||||||
|
echo "ERROR: --field must be one of alias|suffix|agent-id|all" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
||||||
|
if [[ -z "$REPO_ROOT" ]]; then
|
||||||
|
echo "ERROR: run inside collective-memory-repo" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
MAP_FILE="$REPO_ROOT/policies/MACHINE-ALIASES.yaml"
|
||||||
|
if [[ ! -f "$MAP_FILE" ]]; then
|
||||||
|
echo "ERROR: mapping file not found: $MAP_FILE" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
trim() {
|
||||||
|
local s="$1"
|
||||||
|
s="${s#${s%%[![:space:]]*}}"
|
||||||
|
s="${s%${s##*[![:space:]]}}"
|
||||||
|
printf '%s' "$s"
|
||||||
|
}
|
||||||
|
|
||||||
|
contains_csv_value() {
|
||||||
|
local csv="$1"
|
||||||
|
local needle="$2"
|
||||||
|
IFS=',' read -r -a parts <<< "$csv"
|
||||||
|
for p in "${parts[@]}"; do
|
||||||
|
if [[ "$(trim "$p")" == "$needle" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
record_hostname=""
|
||||||
|
record_aliases=""
|
||||||
|
record_suffix=""
|
||||||
|
matched_alias=""
|
||||||
|
matched_suffix=""
|
||||||
|
matched_hostname=""
|
||||||
|
found="false"
|
||||||
|
|
||||||
|
finalize_record() {
|
||||||
|
if [[ "$found" == "true" || -z "$record_hostname" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$HOSTNAME_INPUT" == "$record_hostname" ]]; then
|
||||||
|
found="true"
|
||||||
|
elif contains_csv_value "$record_aliases" "$HOSTNAME_INPUT"; then
|
||||||
|
found="true"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$found" == "true" ]]; then
|
||||||
|
matched_hostname="$record_hostname"
|
||||||
|
matched_suffix="$record_suffix"
|
||||||
|
|
||||||
|
if contains_csv_value "$record_aliases" "$HOSTNAME_INPUT"; then
|
||||||
|
matched_alias="$HOSTNAME_INPUT"
|
||||||
|
else
|
||||||
|
IFS=',' read -r first_alias _ <<< "$record_aliases"
|
||||||
|
matched_alias="$(trim "$first_alias")"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
while IFS= read -r raw_line || [[ -n "$raw_line" ]]; do
|
||||||
|
line="${raw_line%%#*}"
|
||||||
|
line="$(trim "$line")"
|
||||||
|
[[ -z "$line" ]] && continue
|
||||||
|
|
||||||
|
if [[ "$line" =~ ^-[[:space:]]hostname:[[:space:]]*(.+)$ ]]; then
|
||||||
|
finalize_record
|
||||||
|
record_hostname="$(trim "${BASH_REMATCH[1]}")"
|
||||||
|
record_aliases=""
|
||||||
|
record_suffix=""
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$line" =~ ^aliases:[[:space:]]*(.+)$ ]]; then
|
||||||
|
record_aliases="$(trim "${BASH_REMATCH[1]}")"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$line" =~ ^default_agent_suffix:[[:space:]]*(.+)$ ]]; then
|
||||||
|
record_suffix="$(trim "${BASH_REMATCH[1]}")"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
done < "$MAP_FILE"
|
||||||
|
|
||||||
|
finalize_record
|
||||||
|
|
||||||
|
if [[ "$found" != "true" ]]; then
|
||||||
|
# fallback: use normalized hostname directly
|
||||||
|
fallback_suffix="$(printf '%s' "$HOSTNAME_INPUT" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9-' '-')"
|
||||||
|
fallback_suffix="${fallback_suffix#-}"
|
||||||
|
fallback_suffix="${fallback_suffix%-}"
|
||||||
|
[[ -z "$fallback_suffix" ]] && fallback_suffix="host"
|
||||||
|
|
||||||
|
matched_hostname="$HOSTNAME_INPUT"
|
||||||
|
matched_alias="$fallback_suffix"
|
||||||
|
matched_suffix="$fallback_suffix"
|
||||||
|
fi
|
||||||
|
|
||||||
|
agent_id="${TOOL}-${matched_suffix}"
|
||||||
|
|
||||||
|
case "$FIELD" in
|
||||||
|
alias)
|
||||||
|
printf '%s\n' "$matched_alias"
|
||||||
|
;;
|
||||||
|
suffix)
|
||||||
|
printf '%s\n' "$matched_suffix"
|
||||||
|
;;
|
||||||
|
agent-id)
|
||||||
|
printf '%s\n' "$agent_id"
|
||||||
|
;;
|
||||||
|
all)
|
||||||
|
cat <<OUT
|
||||||
|
hostname=$matched_hostname
|
||||||
|
alias=$matched_alias
|
||||||
|
suffix=$matched_suffix
|
||||||
|
agent_id=$agent_id
|
||||||
|
OUT
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -1,5 +1,16 @@
|
|||||||
# New Agent Onboarding Template
|
# New Agent Onboarding Template
|
||||||
|
|
||||||
|
## 0) Register MCP once per client
|
||||||
|
|
||||||
|
- Codex:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
codex mcp add memory-gateway --url http://100.64.0.45:8787/mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
- Claude/Cursor: 在对应客户端 MCP 配置中添加同一 URL:
|
||||||
|
- `http://100.64.0.45:8787/mcp`
|
||||||
|
|
||||||
## 1) Create agent lane
|
## 1) Create agent lane
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -19,7 +30,20 @@ scripts/add-agent.sh kimi-helper
|
|||||||
scripts/add-agent.sh <agent-id> --openclaw-config ~/.openclaw/openclaw.json
|
scripts/add-agent.sh <agent-id> --openclaw-config ~/.openclaw/openclaw.json
|
||||||
```
|
```
|
||||||
|
|
||||||
## 3) Commit and push
|
## 3) Session first message (recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/lingyuzeng/project/collective-memory-repo
|
||||||
|
scripts/print-session-bootstrap.sh --tool codex --branch main
|
||||||
|
```
|
||||||
|
|
||||||
|
任务会话示例:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scripts/print-session-bootstrap.sh --tool codex --branch task/openclawd-migration-2026-03 --task-id openclawd-migration-2026-03
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4) Commit and push
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add agents/<agent-id>
|
git add agents/<agent-id>
|
||||||
@@ -27,7 +51,7 @@ git commit -m "feat(agent): add <agent-id> memory lane"
|
|||||||
git push
|
git push
|
||||||
```
|
```
|
||||||
|
|
||||||
## 4) Verify memory-gateway visibility
|
## 5) Verify memory-gateway visibility
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsS http://100.64.0.45:8787/health
|
curl -fsS http://100.64.0.45:8787/health
|
||||||
@@ -36,7 +60,7 @@ printf '{"branch":"main","require_latest":true}' \
|
|||||||
http://100.64.0.45:8787/memory/status
|
http://100.64.0.45:8787/memory/status
|
||||||
```
|
```
|
||||||
|
|
||||||
## 5) Query smoke test
|
## 6) Query smoke test
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
printf '{"query":"<agent-id>","branch":"main","require_latest":true,"top_k":3}' \
|
printf '{"query":"<agent-id>","branch":"main","require_latest":true,"top_k":3}' \
|
||||||
|
|||||||
Reference in New Issue
Block a user