feat(steev): Wave 8 PAUSE-walk — apply Q4-Q10 + bte leak fix + proton-tools SKILL.md
Q4: confirm personal-scope discriminators (chat_facing, delegates_to=[ceo-planb], sovereign_only=false) Q5: drop google-workspace cred — builtin manages own OAuth via Hermes hub (not credctl vault) Q6: split proton-bridge-imap → proton-bridge-imap-user + proton-bridge-imap-pass (vault exact-match) Q7: rename perplexity-api → perplexity (vault exact-match) Q8: add 3 proton vault entries (account-email, account-password, mailbox-password) Q9: install.sh F6 — MCP allowlist materialization; wires 3 proton MCPs, removes bte (hard-rule leak) Q10: macOS-only externals annotated os_constraint:darwin; install.sh F7 emits INFO on non-Darwin credbridge.sh: drop google-workspace case, rewrite proton-bridge to use 2 vault entries, rename perplexity case Disclosure §7 rewritten with 6 credentials matching vault exact-name policy (DISCLOSURE-SCHEMA §4.5) Disclosure §12 PAUSE table marked all 8 rows RESOLVED (rows 1-7 Wave 8, row 8 Wave 7) Untracked skills/proton-tools/SKILL.md (90 lines, declared in manifest since Wave 4) — committed for clone-ability Verified: hermes -p steev skills list → 6 enabled (matches disclosure.skills declaration) hermes -p steev mcp list → 3 entries (proton-calendar, proton-email, proton-contacts); bte removed F7 on Linux host correctly suppresses macOS-only externals Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
959b8c8871
commit
2491d48151
@ -76,17 +76,20 @@ No direct HTTP/gRPC sovereign API calls. Indirect access flows through the (curr
|
|||||||
|
|
||||||
No `cortex/L6-*` or `cortex/PG-*` libraries consumed at runtime. `lib/` scripts (`credbridge.sh`, `validate_access.sh`) are repo-local utility shims, not cortex tools.
|
No `cortex/L6-*` or `cortex/PG-*` libraries consumed at runtime. `lib/` scripts (`credbridge.sh`, `validate_access.sh`) are repo-local utility shims, not cortex tools.
|
||||||
|
|
||||||
## §7 Credentials (3 declared)
|
## §7 Credentials (6 declared)
|
||||||
|
|
||||||
Per `disclosure.credentials` allowlist. Names + scopes only — NEVER values. Pre-push check 6.d enforces vault_name exact-match.
|
Per `disclosure.credentials` allowlist. Names + scopes only — NEVER values. Pre-push check 6.d enforces vault_name exact-match. **Wave 8 (2026-05-24): aligned with vault.**
|
||||||
|
|
||||||
| Vault name | Status | Scope | Used by | Governance |
|
| Vault name | Status | Scope | Used by | Governance |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| `google-workspace` | required | read-write | `credbridge.sh` | JP-personal; Gmail+Calendar+Contacts for briefing + inbox triage |
|
| `proton-bridge-imap-user` | required | read | `credbridge.sh` | JP-personal; local Proton Bridge IMAP/SMTP username (himalaya path) |
|
||||||
| `proton-bridge-imap` | required | read-write | `credbridge.sh` | JP-personal; local Proton Bridge IMAP/SMTP (himalaya path) |
|
| `proton-bridge-imap-pass` | required | read | `credbridge.sh` | JP-personal; local Proton Bridge IMAP/SMTP password (himalaya path) |
|
||||||
| `perplexity-api` | optional | read | `credbridge.sh` | JP-personal; WebSearch fallback (MCP path preferred) |
|
| `perplexity` | optional | read | `credbridge.sh` | JP-personal; WebSearch fallback (MCP path preferred) |
|
||||||
|
| `proton-account-email` | required | read | `credbridge.sh`, `mcp_proton_email` | JP-personal; Proton account email (consumed by proton-email MCP server) |
|
||||||
|
| `proton-account-password` | required | read | `credbridge.sh`, `mcp_proton_email` | JP-personal; Proton account password (consumed by proton-email MCP server) |
|
||||||
|
| `proton-mailbox-password` | required | read | `credbridge.sh`, `mcp_proton_email` | JP-personal; Proton mailbox E2E key for mail decryption |
|
||||||
|
|
||||||
> **PENDING JP REVIEW** — Per Wave-3 recommendations §5a, all three declared names are reported by audit as not exact-matching the vault (`credctl list` shows `proton-bridge-imap-pass`/`-user` split, `perplexity` without `-api`, and `google-workspace` plausibly absent or composite). Cred-rename rows are governance-class W3.4 and require JP decision (manifest-rename vs vault-rename vs bundle-indirection) — surfaced in §12.
|
> **google-workspace removed Wave 8** — Hermes builtin `google-workspace` skill manages its own OAuth flow via Hermes hub, not credctl vault. credbridge.sh google-workspace case dropped accordingly.
|
||||||
|
|
||||||
## §8 Cron (1)
|
## §8 Cron (1)
|
||||||
|
|
||||||
@ -118,20 +121,25 @@ Per `disclosure.credentials` allowlist. Names + scopes only — NEVER values. Pr
|
|||||||
- Standards: `../sot/04-STANDARDS/FRONTMATTER-SPEC.md`, `../sot/04-STANDARDS/SOT-ENFORCEMENT.md`, `../sot/04-STANDARDS/DISCLOSURE-SCHEMA.md`
|
- Standards: `../sot/04-STANDARDS/FRONTMATTER-SPEC.md`, `../sot/04-STANDARDS/SOT-ENFORCEMENT.md`, `../sot/04-STANDARDS/DISCLOSURE-SCHEMA.md`
|
||||||
- Brand master ref: omitted (scope: personal) — steev serves JP personally, not a brand/org
|
- Brand master ref: omitted (scope: personal) — steev serves JP personally, not a brand/org
|
||||||
|
|
||||||
## §12 Open issues + next steps (PENDING JP REVIEW)
|
## §12 Open issues + next steps
|
||||||
|
|
||||||
Rows below are **PAUSED for JP** per W3.4 governance-class rule. Wave-4 applies auto-approved rows only (REMOVE bte MCP + DROP 17 builtins + scaffold disclosure block). JP must mark each PAUSE row approve/reject/edit before next apply wave.
|
All 8 Wave-3 PAUSE rows resolved in **Wave 8 (2026-05-24)**. Audit trail retained below.
|
||||||
|
|
||||||
| # | Topic | Recommended action | Why PAUSED |
|
| # | Topic | Resolution | Wave |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 1 | Personal-scope discriminator values (`chat_facing: true`, `delegates_to: [ceo-planb]`, `sovereign_only: false`) | Confirm values | New disclosure surface; JP confirms intent matches CLAUDE.md L7-L8 + CONTRACT delegation chain |
|
| 1 | Personal-scope discriminator values (`chat_facing: true`, `delegates_to: [ceo-planb]`, `sovereign_only: false`) | **CONFIRMED** (Q4). Matches CLAUDE.md L7-L8 + CONTRACT delegation chain. | 8 |
|
||||||
| 2 | Cred `google-workspace` not in vault | (a) add composite OAuth JSON to vault, OR (b) split manifest into per-cred entries matching vault | Cred binding (W3.4) |
|
| 2 | Cred `google-workspace` not in vault | **REMOVED** (Q5 + scope-expansion). Builtin manages own OAuth via Hermes hub; no credctl vault entry needed. credbridge.sh google-workspace case dropped. | 8 |
|
||||||
| 3 | Cred `proton-bridge-imap` vs vault `proton-bridge-imap-pass` + `proton-bridge-imap-user` | Rename manifest entry to TWO entries matching vault | Cred binding (W3.4) |
|
| 3 | Cred `proton-bridge-imap` vs vault `proton-bridge-imap-pass` + `proton-bridge-imap-user` | **SPLIT** (Q6). Manifest split into 2 entries matching vault. credbridge.sh exports both `PROTON_BRIDGE_IMAP_USER` + `PROTON_BRIDGE_IMAP_PASSWORD`. | 8 |
|
||||||
| 4 | Cred `perplexity-api` vs vault `perplexity` | Rename manifest declaration `perplexity-api` → `perplexity` (exact-match per schema §4.5) | Cred binding (W3.4) |
|
| 4 | Cred `perplexity-api` vs vault `perplexity` | **RENAMED** (Q7). Manifest + credbridge.sh updated to `perplexity` (exact-match per schema §4.5). | 8 |
|
||||||
| 5 | 5 vault entries plausibly steev-scope but undeclared (`proton-account-email`, `proton-account-password`, `proton-mailbox-password`, `proton-bridge-imap-pass`, `proton-bridge-imap-user`) | ADD to `disclosure.credentials` after MCP install confirms which are consumed | Cred binding (W3.4); also depends on MCP install (row 6) |
|
| 5 | 3 proton vault entries undeclared (`proton-account-email`, `proton-account-password`, `proton-mailbox-password`) | **ADDED** (Q8). Declared in `disclosure.credentials` w/ `used_by: [credbridge.sh, mcp_proton_email]`. The other 2 (`proton-bridge-imap-pass/-user`) covered by row 3. | 8 |
|
||||||
| 6 | 4 declared MCP servers absent from `hermes mcp list` (`mcp_proton_calendar`, `mcp_proton_email`, `mcp_proton_contacts`, `mcp_perplexity`) | Confirm install order — Wave-4 install.sh patch, or deferred | Install gap; cred-adjacent |
|
| 6 | 4 declared MCP servers absent from `hermes mcp list` (`mcp_proton_calendar`, `mcp_proton_email`, `mcp_proton_contacts`, `mcp_perplexity`) | **MATERIALIZED 3/4** (Q9). install.sh F6 wires 3 proton MCPs into per-profile config from `optional_tools`. Also removed bte (hard-rule leak discovered Wave 8). mcp_perplexity DEFERRED (server not in global `hermes mcp list`). | 8 |
|
||||||
| 7 | macOS-only externals (`apple-notes`, `apple-reminders`, `imessage`) in `expected_external_skills` | Gate on OS in `install.sh`, or document as macOS-host-only | OS-platform decision |
|
| 7 | macOS-only externals (`apple-notes`, `apple-reminders`, `imessage`) in `expected_external_skills` | **OS-GATED** (Q10). Annotated `os_constraint: darwin`. install.sh F7 emits INFO on non-Darwin hosts that these are unavailable. | 8 |
|
||||||
| 8 | Pre-push hook check 6 not yet wired (curator/lib/pre-push.sh patch belongs to Wave-5+) | Wire check 6 per DISCLOSURE-SCHEMA §6 | Cross-profile rollup (Wave-5) |
|
| 8 | Pre-push hook check 6 not yet wired (curator/lib/pre-push.sh patch belongs to Wave-5+) | **WIRED** (Wave 7 D6). Subrepo pre-push hook installed via `install.sh F4`; main repo hook covers 6.a-6.f. | 7 |
|
||||||
|
|
||||||
|
### Wave 8 follow-ups (not PAUSE — separate work)
|
||||||
|
|
||||||
|
- **mcp_perplexity install** — server doesn't exist in global `hermes mcp list`. When provisioned, install.sh F6 will materialize automatically (no code change).
|
||||||
|
- **Per-tool enumeration in `disclosure.mcp_servers`** — currently `[]` w/ install.sh F6 driven from `optional_tools`. Wave 8.5: introspect each MCP server, populate `disclosure.mcp_servers[*].tools[]` per DISCLOSURE-SCHEMA §4.2.
|
||||||
|
|
||||||
## §13 Related
|
## §13 Related
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
# written to disk.
|
# written to disk.
|
||||||
#
|
#
|
||||||
# Usage: credbridge.sh <tool> [args...]
|
# Usage: credbridge.sh <tool> [args...]
|
||||||
# tools: google-workspace | proton-bridge | perplexity
|
# tools: proton-bridge | perplexity
|
||||||
#
|
#
|
||||||
# Per PROFILE-DISTRIBUTION-PROTOCOL §3 (shared core, "credbridge" row) and §6
|
# Per PROFILE-DISTRIBUTION-PROTOCOL §3 (shared core, "credbridge" row) and §6
|
||||||
# (Conventions → Secrets), every profile distribution exposes credentials via
|
# (Conventions → Secrets), every profile distribution exposes credentials via
|
||||||
@ -13,13 +13,16 @@
|
|||||||
# This is the personal-assistant variant of the credbridge pattern. Steev's
|
# This is the personal-assistant variant of the credbridge pattern. Steev's
|
||||||
# cred surface is narrow by design:
|
# cred surface is narrow by design:
|
||||||
#
|
#
|
||||||
# - google-workspace: Gmail + Calendar + Contacts (OAuth blob from credctl)
|
# - proton-bridge: IMAP/SMTP user + password for the local Proton Bridge
|
||||||
# - proton-bridge: IMAP/SMTP password for the local Proton Bridge T6
|
# T6 sidecar — gives Steev access to JP's Proton mail
|
||||||
# sidecar — gives Steev access to JP's Proton mail via
|
# via himalaya (cleartext on 127.0.0.1 only)
|
||||||
# himalaya (cleartext on 127.0.0.1 only)
|
|
||||||
# - perplexity: Perplexity API key for WebSearch toolset (lightweight
|
# - perplexity: Perplexity API key for WebSearch toolset (lightweight
|
||||||
# — most Steev work uses the perplexity MCP instead)
|
# — most Steev work uses the perplexity MCP instead)
|
||||||
#
|
#
|
||||||
|
# Wave 8 (2026-05-24): google-workspace case REMOVED — Hermes builtin
|
||||||
|
# google-workspace skill manages its own OAuth flow via the Hermes hub, not
|
||||||
|
# the credctl vault. Vault contains no google-workspace-* entries.
|
||||||
|
#
|
||||||
# Plan B marketing platforms (WooCommerce, Mailchimp, Meta, GA4, etc.) are OUT
|
# Plan B marketing platforms (WooCommerce, Mailchimp, Meta, GA4, etc.) are OUT
|
||||||
# OF SCOPE here — that's cmo-planb's credbridge. Steev MUST NEVER resolve a
|
# OF SCOPE here — that's cmo-planb's credbridge. Steev MUST NEVER resolve a
|
||||||
# marketing platform credential. The CLAUDE.md "no access to Plan B marketing
|
# marketing platform credential. The CLAUDE.md "no access to Plan B marketing
|
||||||
@ -27,8 +30,6 @@
|
|||||||
#
|
#
|
||||||
# Design notes (same as cmo/credbridge.sh — shared core):
|
# Design notes (same as cmo/credbridge.sh — shared core):
|
||||||
# - credctl values read into local vars, exported straight to the child env
|
# - credctl values read into local vars, exported straight to the child env
|
||||||
# - JSON-valued creds (google-workspace OAuth) parsed via `node -e` reading
|
|
||||||
# from stdin so the value never lands on argv / process list
|
|
||||||
# - No `echo $secret`. set +x stays off.
|
# - No `echo $secret`. set +x stays off.
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@ -39,7 +40,7 @@ STEEV_LIB="${STEEV_LIB:-/home/svrnty/.hermes/steev}"
|
|||||||
|
|
||||||
die() { printf '{"error":"%s"}\n' "$1" >&2; exit 1; }
|
die() { printf '{"error":"%s"}\n' "$1" >&2; exit 1; }
|
||||||
|
|
||||||
[ $# -ge 1 ] || die "usage: credbridge.sh <google-workspace|proton-bridge|perplexity> [args...]"
|
[ $# -ge 1 ] || die "usage: credbridge.sh <proton-bridge|perplexity> [args...]"
|
||||||
TOOL="$1"; shift
|
TOOL="$1"; shift
|
||||||
|
|
||||||
[ -x "$CREDCTL" ] || die "credctl not found/executable at $CREDCTL"
|
[ -x "$CREDCTL" ] || die "credctl not found/executable at $CREDCTL"
|
||||||
@ -51,44 +52,30 @@ cred_raw() {
|
|||||||
| sed -n '/^Value:/,$p' | sed '1s/^Value:[[:space:]]*//'
|
| sed -n '/^Value:/,$p' | sed '1s/^Value:[[:space:]]*//'
|
||||||
}
|
}
|
||||||
|
|
||||||
# json_field <json> <key> — extract a string field via node; value never on argv.
|
|
||||||
json_field() {
|
|
||||||
printf '%s' "$1" | node -e '
|
|
||||||
let s="";process.stdin.on("data",d=>s+=d);
|
|
||||||
process.stdin.on("end",()=>{try{const o=JSON.parse(s);
|
|
||||||
const v=o[process.argv[1]];process.stdout.write(v==null?"":String(v));
|
|
||||||
}catch(e){process.stdout.write("");}});' "$2"
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$TOOL" in
|
case "$TOOL" in
|
||||||
google-workspace)
|
|
||||||
# Gmail Data API + Calendar API + People API all expect a bearer token
|
|
||||||
# minted from this service-account / OAuth blob. The blob is JSON; we
|
|
||||||
# export the whole document so the child CLI can introspect scope.
|
|
||||||
GW_JSON="$(cred_raw google-workspace)"
|
|
||||||
[ -n "$GW_JSON" ] || die "credctl: google-workspace not set"
|
|
||||||
export GOOGLE_WORKSPACE_CREDENTIALS_JSON="$GW_JSON"
|
|
||||||
exec "$@"
|
|
||||||
;;
|
|
||||||
proton-bridge)
|
proton-bridge)
|
||||||
# Steev reads JP's Proton inbox via the local Proton Bridge IMAP daemon
|
# Steev reads JP's Proton inbox via the local Proton Bridge IMAP daemon
|
||||||
# (T6 sidecar — see PROFILE-DISTRIBUTION-PROTOCOL §4.T6). credctl stores
|
# (T6 sidecar — see PROFILE-DISTRIBUTION-PROTOCOL §4.T6). credctl stores
|
||||||
# the bridge password (rotates when JP rotates the bridge).
|
# user + password as separate vault entries (Wave 8 aligned to vault).
|
||||||
PB_PASS="$(cred_raw proton-bridge-imap)"
|
PB_USER="$(cred_raw proton-bridge-imap-user)"
|
||||||
[ -n "$PB_PASS" ] || die "credctl: proton-bridge-imap not set"
|
[ -n "$PB_USER" ] || die "credctl: proton-bridge-imap-user not set"
|
||||||
|
PB_PASS="$(cred_raw proton-bridge-imap-pass)"
|
||||||
|
[ -n "$PB_PASS" ] || die "credctl: proton-bridge-imap-pass not set"
|
||||||
|
export PROTON_BRIDGE_IMAP_USER="$PB_USER"
|
||||||
export PROTON_BRIDGE_IMAP_PASSWORD="$PB_PASS"
|
export PROTON_BRIDGE_IMAP_PASSWORD="$PB_PASS"
|
||||||
exec "$@"
|
exec "$@"
|
||||||
;;
|
;;
|
||||||
perplexity)
|
perplexity)
|
||||||
# Lightweight WebSearch path. Most Steev research goes through the
|
# Lightweight WebSearch path. Most Steev research goes through the
|
||||||
# perplexity MCP server (which holds its own key); this credbridge entry
|
# perplexity MCP server (which holds its own key); this credbridge entry
|
||||||
# exists for scripts that need a raw key (rare).
|
# exists for scripts that need a raw key (rare). Wave 8 renamed
|
||||||
PPL_KEY="$(cred_raw perplexity-api)"
|
# vault entry `perplexity-api` → `perplexity`.
|
||||||
[ -n "$PPL_KEY" ] || die "credctl: perplexity-api not set"
|
PPL_KEY="$(cred_raw perplexity)"
|
||||||
|
[ -n "$PPL_KEY" ] || die "credctl: perplexity not set"
|
||||||
export PERPLEXITY_API_KEY="$PPL_KEY"
|
export PERPLEXITY_API_KEY="$PPL_KEY"
|
||||||
exec "$@"
|
exec "$@"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
die "unknown tool: $TOOL (allowed: google-workspace|proton-bridge|perplexity)"
|
die "unknown tool: $TOOL (allowed: proton-bridge|perplexity)"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
64
install.sh
64
install.sh
@ -310,8 +310,72 @@ HOOK_EOF
|
|||||||
echo " F4 installed: $HOOK_DST"
|
echo " F4 installed: $HOOK_DST"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# F6 — MCP server materialization (Wave 8 Q9)
|
||||||
|
# Reads manifest.optional_tools (mcp_<server-name-with-underscores> aliases),
|
||||||
|
# maps to runtime MCP server names (hyphenated), copies global config block
|
||||||
|
# into per-profile config.yaml. Removes non-declared MCPs (closes bte leak).
|
||||||
|
if [ "$DRY" = 1 ]; then
|
||||||
|
echo "DRY: F6 materialize MCP allowlist → $PROFILE_CFG"
|
||||||
|
elif command -v yq >/dev/null 2>&1 && [ -f "$HERMES_HOME/config.yaml" ]; then
|
||||||
|
# Declared MCP set (mcp_proton_calendar → proton-calendar etc).
|
||||||
|
DECLARED_MCPS=$(yq -r '.optional_tools[]?' "$REPO/manifest.yaml" 2>/dev/null | sed 's/^mcp_//; s/_/-/g')
|
||||||
|
if [ -z "$DECLARED_MCPS" ]; then
|
||||||
|
echo " F6: no optional_tools declared — skip"
|
||||||
|
else
|
||||||
|
mkdir -p "$(dirname "$PROFILE_CFG")"
|
||||||
|
[ -f "$PROFILE_CFG" ] || : > "$PROFILE_CFG"
|
||||||
|
F6_ADDED=0; F6_REMOVED=0; F6_MISSING=0
|
||||||
|
# Set the per-profile mcp_servers block from the declared list. Existing
|
||||||
|
# entries NOT in declared list are dropped (denylist enforcement).
|
||||||
|
GLOBAL_CFG="$HERMES_HOME/config.yaml"
|
||||||
|
python3 - "$GLOBAL_CFG" "$PROFILE_CFG" "$DECLARED_MCPS" <<'PY'
|
||||||
|
import sys, yaml
|
||||||
|
gcfg, pcfg, declared_str = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||||
|
declared = [s.strip() for s in declared_str.splitlines() if s.strip()]
|
||||||
|
g = yaml.safe_load(open(gcfg).read()) or {}
|
||||||
|
p = yaml.safe_load(open(pcfg).read()) or {}
|
||||||
|
g_mcps = g.get('mcp_servers', {}) or {}
|
||||||
|
new_block = {}
|
||||||
|
missing = []
|
||||||
|
for name in declared:
|
||||||
|
if name in g_mcps:
|
||||||
|
new_block[name] = g_mcps[name]
|
||||||
|
else:
|
||||||
|
missing.append(name)
|
||||||
|
prev = set((p.get('mcp_servers') or {}).keys())
|
||||||
|
new = set(new_block.keys())
|
||||||
|
added = sorted(new - prev)
|
||||||
|
removed = sorted(prev - new)
|
||||||
|
p['mcp_servers'] = new_block
|
||||||
|
open(pcfg, 'w').write(yaml.safe_dump(p, sort_keys=False, allow_unicode=True))
|
||||||
|
for n in added: print(f" F6 + {n}")
|
||||||
|
for n in removed: print(f" F6 - {n} (denied)")
|
||||||
|
for n in missing: print(f" F6 ⚠ {n} (declared but not in global mcp_servers — skipped)")
|
||||||
|
print(f" F6 wrote mcp_servers: {len(new_block)} entr{'y' if len(new_block)==1 else 'ies'}")
|
||||||
|
PY
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " WARN: F6 yq/global config missing — skipping MCP materialization"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# F7 — macOS-only externals OS-gate (Wave 8 Q10)
|
||||||
|
# Reads expected_external_skills entries with os_constraint: darwin and emits
|
||||||
|
# an INFO line on non-Darwin hosts. No install action (these are external
|
||||||
|
# prereqs, not provisioned by this installer); annotation is the audit record.
|
||||||
|
HOST_OS="$(uname -s 2>/dev/null || echo Unknown)"
|
||||||
|
if [ "$DRY" = 1 ]; then
|
||||||
|
echo "DRY: F7 OS-gate check (host=$HOST_OS)"
|
||||||
|
elif command -v yq >/dev/null 2>&1; then
|
||||||
|
MACOS_ONLY=$(yq -r '.expected_external_skills[] | select(type == "!!map") | select(.os_constraint == "darwin") | .name' "$REPO/manifest.yaml" 2>/dev/null || true)
|
||||||
|
if [ -n "$MACOS_ONLY" ] && [ "$HOST_OS" != "Darwin" ]; then
|
||||||
|
echo " F7 INFO: macOS-only externals declared but host=$HOST_OS — unavailable:"
|
||||||
|
while IFS= read -r s; do [ -n "$s" ] && echo " - $s"; done <<< "$MACOS_ONLY"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "== done =="
|
echo "== done =="
|
||||||
echo " verify skills: hermes -p steev skills list | grep steev-agent"
|
echo " verify skills: hermes -p steev skills list | grep steev-agent"
|
||||||
|
echo " verify mcp servers: hermes -p steev mcp list"
|
||||||
echo " verify assignee registered: hermes kanban assignees | grep steev"
|
echo " verify assignee registered: hermes kanban assignees | grep steev"
|
||||||
echo " start gateway (when ready): hermes profile gateway start steev"
|
echo " start gateway (when ready): hermes profile gateway start steev"
|
||||||
|
|||||||
@ -44,12 +44,16 @@ lib:
|
|||||||
# tree (~/.hermes/skills/) or external skill libraries the principal already installed.
|
# tree (~/.hermes/skills/) or external skill libraries the principal already installed.
|
||||||
expected_external_skills:
|
expected_external_skills:
|
||||||
- google-workspace # Gmail + Calendar + Contacts
|
- google-workspace # Gmail + Calendar + Contacts
|
||||||
- apple-notes # macOS-local via osascript
|
|
||||||
- apple-reminders # macOS-local via osascript
|
|
||||||
- obsidian # ~/vaults/steev PKM
|
- obsidian # ~/vaults/steev PKM
|
||||||
- himalaya # IMAP/SMTP via proton-bridge sidecar
|
- himalaya # IMAP/SMTP via proton-bridge sidecar
|
||||||
- imessage # macOS-local
|
|
||||||
- perplexity # WebSearch toolset (lightweight; MCP preferred)
|
- perplexity # WebSearch toolset (lightweight; MCP preferred)
|
||||||
|
# macOS-only skills (Wave 8 Q10): install.sh F7 emits info on non-Darwin hosts.
|
||||||
|
- name: apple-notes
|
||||||
|
os_constraint: darwin
|
||||||
|
- name: apple-reminders
|
||||||
|
os_constraint: darwin
|
||||||
|
- name: imessage
|
||||||
|
os_constraint: darwin
|
||||||
|
|
||||||
# MCP servers Steev consumes. Names match runtime-prefixed form (mcp_<server>_<tool>).
|
# MCP servers Steev consumes. Names match runtime-prefixed form (mcp_<server>_<tool>).
|
||||||
optional_tools:
|
optional_tools:
|
||||||
@ -61,15 +65,26 @@ optional_tools:
|
|||||||
requires_tools: [terminal, memory_tool]
|
requires_tools: [terminal, memory_tool]
|
||||||
|
|
||||||
credentials: # validated by validate_access.sh
|
credentials: # validated by validate_access.sh
|
||||||
- name: google-workspace
|
# Wave 8 (2026-05-24): aligned with vault exact-match per DISCLOSURE-SCHEMA §4.5.
|
||||||
purpose: Gmail + Calendar + Contacts read/write for daily briefing + inbox triage
|
# google-workspace removed — builtin manages its own OAuth via Hermes hub (not credctl vault).
|
||||||
|
- name: proton-bridge-imap-user
|
||||||
|
purpose: Proton Bridge IMAP/SMTP username (himalaya path)
|
||||||
resolved_via: credbridge.sh
|
resolved_via: credbridge.sh
|
||||||
- name: proton-bridge-imap
|
- name: proton-bridge-imap-pass
|
||||||
purpose: local Proton Bridge IMAP/SMTP password (himalaya path)
|
purpose: Proton Bridge IMAP/SMTP password (himalaya path)
|
||||||
resolved_via: credbridge.sh
|
resolved_via: credbridge.sh
|
||||||
- name: perplexity-api
|
- name: perplexity
|
||||||
purpose: Perplexity API key for raw WebSearch (MCP path preferred)
|
purpose: Perplexity API key for raw WebSearch (MCP path preferred)
|
||||||
resolved_via: credbridge.sh
|
resolved_via: credbridge.sh
|
||||||
|
- name: proton-account-email
|
||||||
|
purpose: Proton account email (consumed by proton-email MCP server)
|
||||||
|
resolved_via: credbridge.sh
|
||||||
|
- name: proton-account-password
|
||||||
|
purpose: Proton account password (consumed by proton-email MCP server)
|
||||||
|
resolved_via: credbridge.sh
|
||||||
|
- name: proton-mailbox-password
|
||||||
|
purpose: Proton mailbox E2E key for mail decryption (consumed by proton-email MCP server)
|
||||||
|
resolved_via: credbridge.sh
|
||||||
|
|
||||||
db:
|
db:
|
||||||
file: steev.db # runtime state; created from schema.sql; never committed
|
file: steev.db # runtime state; created from schema.sql; never committed
|
||||||
@ -140,26 +155,47 @@ disclosure:
|
|||||||
justification: "CEO delegation transport — steev → ceo-planb (steev-agent SKILL.md L83)"
|
justification: "CEO delegation transport — steev → ceo-planb (steev-agent SKILL.md L83)"
|
||||||
|
|
||||||
mcp_servers: [] # DENY-BY-DEFAULT. bte REMOVED (hard-rule fix).
|
mcp_servers: [] # DENY-BY-DEFAULT. bte REMOVED (hard-rule fix).
|
||||||
# proton-* + perplexity MCP installs PENDING JP review
|
# Wave 8 (2026-05-24): install.sh F6 wires the 3 proton MCPs
|
||||||
# (install-gap row in DISCLOSURE.md §12).
|
# (proton-calendar, proton-email, proton-contacts) into per-profile
|
||||||
|
# runtime config from manifest.optional_tools. mcp_perplexity is
|
||||||
|
# deferred — server not yet in `hermes mcp list`.
|
||||||
|
# Per-tool enumeration in disclosure.mcp_servers DEFERRED to
|
||||||
|
# Wave 8.5 (requires tool introspection per server).
|
||||||
|
|
||||||
sovereign_apis: [] # 0 direct HTTP/gRPC calls (per audit §3)
|
sovereign_apis: [] # 0 direct HTTP/gRPC calls (per audit §3)
|
||||||
|
|
||||||
cortex_tools: [] # steev does not consume cortex/L6-* or cortex/PG-*
|
cortex_tools: [] # steev does not consume cortex/L6-* or cortex/PG-*
|
||||||
|
|
||||||
credentials:
|
credentials:
|
||||||
- vault_name: google-workspace
|
# Wave 8 (2026-05-24) — aligned with vault per DISCLOSURE-SCHEMA §4.5 (exact-match).
|
||||||
|
# google-workspace removed (Hermes builtin self-manages OAuth, not in credctl vault).
|
||||||
|
- vault_name: proton-bridge-imap-user
|
||||||
status: required
|
status: required
|
||||||
scope: read-write
|
scope: read
|
||||||
used_by: [credbridge.sh]
|
used_by: [credbridge.sh]
|
||||||
governance: "JP-personal; Gmail+Calendar+Contacts for briefing + inbox triage"
|
governance: "JP-personal; local Proton Bridge IMAP/SMTP username (himalaya path)"
|
||||||
- vault_name: proton-bridge-imap
|
- vault_name: proton-bridge-imap-pass
|
||||||
status: required
|
status: required
|
||||||
scope: read-write
|
scope: read
|
||||||
used_by: [credbridge.sh]
|
used_by: [credbridge.sh]
|
||||||
governance: "JP-personal; local Proton Bridge IMAP/SMTP (himalaya path)"
|
governance: "JP-personal; local Proton Bridge IMAP/SMTP password (himalaya path)"
|
||||||
- vault_name: perplexity-api
|
- vault_name: perplexity
|
||||||
status: optional
|
status: optional
|
||||||
scope: read
|
scope: read
|
||||||
used_by: [credbridge.sh]
|
used_by: [credbridge.sh]
|
||||||
governance: "JP-personal; WebSearch fallback (MCP path preferred)"
|
governance: "JP-personal; WebSearch fallback (MCP path preferred)"
|
||||||
|
- vault_name: proton-account-email
|
||||||
|
status: required
|
||||||
|
scope: read
|
||||||
|
used_by: [credbridge.sh, mcp_proton_email]
|
||||||
|
governance: "JP-personal; Proton account email (consumed by proton-email MCP server)"
|
||||||
|
- vault_name: proton-account-password
|
||||||
|
status: required
|
||||||
|
scope: read
|
||||||
|
used_by: [credbridge.sh, mcp_proton_email]
|
||||||
|
governance: "JP-personal; Proton account password (consumed by proton-email MCP server)"
|
||||||
|
- vault_name: proton-mailbox-password
|
||||||
|
status: required
|
||||||
|
scope: read
|
||||||
|
used_by: [credbridge.sh, mcp_proton_email]
|
||||||
|
governance: "JP-personal; Proton mailbox E2E key for mail decryption"
|
||||||
|
|||||||
91
skills/proton-tools/SKILL.md
Normal file
91
skills/proton-tools/SKILL.md
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
---
|
||||||
|
name: proton-tools
|
||||||
|
description: "When Steev needs to access JP's Proton account — Calendar, Mail, or Contacts. Use this skill to discover which tool answers the user's question, and how to call it. Covers all 24 Proton MCP tools across the three cortex MCP servers (proton-calendar, proton-email, proton-contacts). Triggers: any request involving JP's calendar (events, meetings, availability), mail (inbox, send, reply, search, folders), or contacts (lookup, add, search). Drive is NOT in scope — defer Drive requests."
|
||||||
|
metadata:
|
||||||
|
version: 1.0.0
|
||||||
|
hermes:
|
||||||
|
requires_mcp_servers: [proton-calendar, proton-email, proton-contacts]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Proton Tools — Calendar + Mail + Contacts
|
||||||
|
|
||||||
|
Authoritative reference for the 24 tools exposed by three cortex MCP servers — `proton-calendar` (8 tools), `proton-email` (10 tools), `proton-contacts` (6 tools). Each MCP facade dials a long-running gRPC gate that holds the Proton session.
|
||||||
|
|
||||||
|
## Hard rules
|
||||||
|
|
||||||
|
- **Drive is out of scope.** If the user asks about Proton Drive files/folders, say so and defer — there is no `drive_*` tool. Roadmap: `rclone-module` MCP wrap.
|
||||||
|
- **Destructive tools require explicit confirmation.** `email_send`, `email_reply`, `email_forward`, `calendar_delete`, `contacts_delete`. Never call these without quoting back the action + target + asking JP to confirm.
|
||||||
|
- **Date inputs are ISO 8601** (`2026-05-23T14:00:00-04:00`). Convert relative dates ("tomorrow", "next Tuesday") into ISO before tool call.
|
||||||
|
- **Pagination**: `email_list`, `calendar_events`, `contacts_list` are paginated. Default page size is small (~20). Fetch additional pages only when the user asks for more.
|
||||||
|
|
||||||
|
## When to use which tool
|
||||||
|
|
||||||
|
### Calendar (8 tools)
|
||||||
|
|
||||||
|
| User intent | Tool |
|
||||||
|
|---|---|
|
||||||
|
| "What calendars do I have?" | `calendar_list` |
|
||||||
|
| "What's on my calendar today/this week?" | `calendar_events` with date range |
|
||||||
|
| "What's coming up?" "Next few meetings?" | `calendar_upcoming` |
|
||||||
|
| "Find meetings about X" | `calendar_search` |
|
||||||
|
| "Show me details of [event]" | `calendar_event_get` |
|
||||||
|
| "Schedule a meeting with…" | `calendar_create` (confirm first) |
|
||||||
|
| "Move my 3pm to 4pm" | `calendar_update` |
|
||||||
|
| "Cancel my 3pm" | `calendar_delete` (DESTRUCTIVE — confirm) |
|
||||||
|
|
||||||
|
### Mail (10 tools)
|
||||||
|
|
||||||
|
| User intent | Tool |
|
||||||
|
|---|---|
|
||||||
|
| "How many unread?" "What folders?" | `email_folders` |
|
||||||
|
| "Show me my inbox" "Latest emails" | `email_list` (folder=INBOX) |
|
||||||
|
| "Open that email" | `email_read` by UID |
|
||||||
|
| "Search inbox for…" | `email_search` |
|
||||||
|
| "Send an email to…" | `email_send` (DESTRUCTIVE — draft + confirm) |
|
||||||
|
| "Reply to that" | `email_reply` (DESTRUCTIVE — draft + confirm) |
|
||||||
|
| "Forward this to…" | `email_forward` (DESTRUCTIVE — confirm) |
|
||||||
|
| "Archive that" | `email_archive` |
|
||||||
|
| "Mark as read/unread" | `email_mark_read` / `email_mark_unread` |
|
||||||
|
|
||||||
|
### Contacts (6 tools)
|
||||||
|
|
||||||
|
| User intent | Tool |
|
||||||
|
|---|---|
|
||||||
|
| "Who do I have in contacts?" | `contacts_list` |
|
||||||
|
| "Look up [person]" | `contacts_search` |
|
||||||
|
| "Pull up [person]'s details" | `contacts_get` |
|
||||||
|
| "Add [person] to contacts" | `contacts_create` |
|
||||||
|
| "Update [person]'s email/phone" | `contacts_update` |
|
||||||
|
| "Remove [person]" | `contacts_delete` (DESTRUCTIVE — confirm) |
|
||||||
|
|
||||||
|
## Daily briefing — tool order
|
||||||
|
|
||||||
|
When JP asks for the morning briefing, query in this order:
|
||||||
|
|
||||||
|
1. `calendar_upcoming` (hours=24) → events today
|
||||||
|
2. `email_folders` → unread counts
|
||||||
|
3. `email_list` (folder=INBOX, limit=10) → recent inbox
|
||||||
|
4. `email_search` (folder=INBOX, query="from:important-person OR is:flagged") → priorities
|
||||||
|
|
||||||
|
Don't dump raw output. Synthesize. Lead with what's actionable in JP's voice.
|
||||||
|
|
||||||
|
## Search composition
|
||||||
|
|
||||||
|
For broad questions like "anything from [person] this week":
|
||||||
|
- `email_search` (folder=INBOX, query="from:<person>")
|
||||||
|
- `calendar_search` (query="<person>")
|
||||||
|
- `contacts_search` (query="<person>")
|
||||||
|
Run in parallel. Merge results. Group by source.
|
||||||
|
|
||||||
|
## Error handling
|
||||||
|
|
||||||
|
- **"WaitReady timeout"** → proton connector still booting. Retry once after 2-3s. If still failing, say so + suggest JP check `hermes mcp test proton`.
|
||||||
|
- **403 / scope error** → proton session expired. Tool handler should re-auth automatically; if not, JP needs to re-run setup.
|
||||||
|
- **Network / 5xx** → transient. Retry once. If persistent, report and stop.
|
||||||
|
|
||||||
|
## What NOT to do
|
||||||
|
|
||||||
|
- Don't paginate aggressively — fetch one page, summarize, ask if JP wants more.
|
||||||
|
- Don't auto-send drafts. Even after JP says "send" once, re-quote subject + recipient on the next compose.
|
||||||
|
- Don't synthesize calendar events from email content unless JP explicitly asks ("add this to my calendar").
|
||||||
|
- Don't enumerate every contact when JP asks "who's [person]" — use search, not list.
|
||||||
Loading…
Reference in New Issue
Block a user