mirror of
https://github.com/forcedotcom/afv-library.git
synced 2026-08-10 17:41:49 +08:00
Data Cloud 360° view of a single Agentforce session — DC-only, zero
Splunk dependency. Pulls 24 STDM + GenAI DMOs via the Data Cloud Query
REST API, assembles a hierarchical session tree (Interaction → Step →
Generation → GatewayRequest), and renders a human-readable markdown
summary with transcript + per-turn topic/action invocations + LLM
generations + tool calls + audit chain.
Migrated as a standalone Apache-2.0 skill from an internal hub plugin —
self-contained, no sibling-skill or plugin dependencies.
What this skill answers:
- "Trace session <uuid>" / "Summarize what happened in <0Mw…>"
- "Find escalated sessions today on Messaging in <org>"
- Session discovery by time / agent / channel / outcome / conversation
text when the user has no session id
What it does NOT answer (use a different surface):
- Design-time architecture — use investigating-agentforce-architecture
- Runtime planner availability — DC alone can't tell you which
topic/action was eligible for the classifier on a given turn
Skill layout:
- 8 Python pipeline modules (fetch_dc, assemble_dc, render_dc,
discover_sessions, resolve_session, dc, storage, config)
- 4 _shared helpers (paths, fs_guard, sql, __init__) with skill-scoped
DATA_ROOT (~/.claude/data/investigating-agentforce-d360/)
- 26 SQL templates under assets/dc/
- 27 test files (367 tests + 18 subtests, 100% passing)
- 3 reference docs (artifacts.md, dc_dmo_fields.md,
dc_pipeline_contract.md)
- SKILL.md (sf-skills frontmatter, license: Apache-2.0,
metadata.version: "1.0")
- README.md (external-facing quick-start)
- tools/grant_allowlist.py (idempotent first-run permission grant)
- tools/archive_data_dir.sh (opt-in stop-hook tarballer)
Quality gates:
- pytest scripts/tests/: 367 passed + 18 subtests, 0 failures
- npm run validate:skills: 62 of 62 skill(s) checked, 0 errors
- Live end-to-end runs against 3 real Salesforce sessions exercising
both the full-tree and STDM-lag gateway-direct render branches
- 4 independent code-review rounds (correctness, security, markdown,
architecture-critic) — all findings addressed
Customer-data hygiene: no live tenant ids, no internal sprint markers,
no hub/sibling-skill references. Synthetic fixtures look obviously
synthetic (`019dface-…` UUIDs, `0MwTESTMSG…` MessagingSession ids,
`00DTESTORG…` org ids, `MyAgent` placeholder agent name).
Sibling skill: investigating-agentforce-architecture (PR #278) — same
migration pattern, design-time metadata; complementary scope.
152 lines
5.6 KiB
Python
152 lines
5.6 KiB
Python
"""Canonical path helpers for investigating-agentforce-d360.
|
|
|
|
Layout:
|
|
|
|
~/.claude/data/investigating-agentforce-d360/
|
|
└── <org_id_15>/
|
|
└── <agent_api_name>__<agent_version>/
|
|
└── <session_id>/ ← per-session DC artifacts
|
|
├── dc.*.json
|
|
├── dc._session_manifest.json
|
|
├── dc._session_tree.json
|
|
└── dc._session_summary.md
|
|
|
|
Validation strategy
|
|
-------------------
|
|
The three "agent-identity" segments (org_id_15, agent_api_name, agent_version)
|
|
are validated by the regex helpers in the sibling ``fs_guard`` module. Any
|
|
segment containing ``..``, ``/``, or characters outside its regex charclass
|
|
raises ``PathValidationError`` — direct ``Path`` composition from unvalidated
|
|
input would be a path-traversal vulnerability and is prohibited.
|
|
|
|
``session_id`` is validated locally via ``SESSION_ID_RE.fullmatch`` because
|
|
fs_guard's ``api_name`` rule is too permissive for it (session IDs can contain
|
|
hyphens, which API names cannot). The regex is fully anchored (``\\A...\\Z``)
|
|
and call sites MUST use ``fullmatch`` — ``match`` would silently accept a
|
|
traversal suffix like ``abc\\n../etc``.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
from . import fs_guard
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Roots
|
|
# -----------------------------------------------------------------------------
|
|
|
|
DATA_ROOT: Path = Path.home() / ".claude" / "data" / "investigating-agentforce-d360"
|
|
CACHE_ROOT: Path = Path.home() / ".claude" / "cache" / "investigating-agentforce-d360"
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Session-ID validation
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Fully anchored — call sites MUST use fullmatch (tested). Characters allowed:
|
|
# alphanumeric + underscore + hyphen, matching both UUIDs and MessagingSession
|
|
# IDs.
|
|
SESSION_ID_RE = re.compile(r"\A[A-Za-z0-9_\-]+\Z") # @rule-suppress starter-sec-002 — re.compile, not eval/exec
|
|
|
|
|
|
class PathValidationError(ValueError):
|
|
"""Raised when a path segment fails validation.
|
|
|
|
Wraps fs_guard's ``ValidationError`` as well as local ``session_id``
|
|
rejections so callers have a single exception type to catch.
|
|
"""
|
|
|
|
def __init__(self, label: str, reason: str) -> None:
|
|
self.label = label
|
|
self.reason = reason
|
|
super().__init__(f"{label}: {reason}")
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Validation helpers
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
def _validate_agent_triple(
|
|
org_id_15: str, agent_api_name: str, agent_version: str
|
|
) -> None:
|
|
"""Validate the three identity segments. Raises PathValidationError on
|
|
failure, wrapping fs_guard's ValidationError so callers don't need to
|
|
know about fs_guard internals."""
|
|
try:
|
|
fs_guard.validate_org_id_15(org_id_15, label="org_id_15")
|
|
fs_guard.validate_api_name(agent_api_name, label="agent_api_name")
|
|
fs_guard.validate_agent_version(agent_version, label="agent_version")
|
|
except fs_guard.ValidationError as e:
|
|
raise PathValidationError(e.label, e.reason) from e
|
|
|
|
|
|
def validate_session_id(session_id: str) -> None:
|
|
"""Validate session_id via SESSION_ID_RE.fullmatch.
|
|
|
|
Raises :class:`PathValidationError` on any of: None, non-string, empty,
|
|
regex miss. This is the public validator every caller that composes a
|
|
Path segment from a session-id MUST go through — direct
|
|
``Path / session_id`` composition bypasses the anchored-regex check.
|
|
"""
|
|
if session_id is None:
|
|
raise PathValidationError("session_id", "is None")
|
|
if not isinstance(session_id, str):
|
|
raise PathValidationError(
|
|
"session_id", f"must be str, got {type(session_id).__name__}"
|
|
)
|
|
if not session_id:
|
|
raise PathValidationError("session_id", "must not be empty")
|
|
if not SESSION_ID_RE.fullmatch(session_id):
|
|
preview = session_id[:40]
|
|
raise PathValidationError(
|
|
"session_id",
|
|
f"does not match {SESSION_ID_RE.pattern} (preview={preview!r})",
|
|
)
|
|
|
|
|
|
_validate_session_id = validate_session_id # backward-compat alias
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Path builders
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
def agent_dir(
|
|
org_id_15: str, agent_api_name: str, agent_version: str
|
|
) -> Path:
|
|
"""Return ``DATA_ROOT/<org_id_15>/<agent_api_name>__<agent_version>/``.
|
|
|
|
All three segments are regex-validated before being joined. Any segment
|
|
containing ``..``, ``/``, or characters outside its regex charclass
|
|
raises ``PathValidationError``.
|
|
"""
|
|
_validate_agent_triple(org_id_15, agent_api_name, agent_version)
|
|
return DATA_ROOT / org_id_15 / f"{agent_api_name}__{agent_version}"
|
|
|
|
|
|
def session_dir(
|
|
org_id_15: str,
|
|
agent_api_name: str,
|
|
agent_version: str,
|
|
session_id: str,
|
|
) -> Path:
|
|
"""Return
|
|
``DATA_ROOT/<org_id_15>/<agent_api_name>__<agent_version>/<session_id>/``.
|
|
|
|
All four segments are validated. The first three go through fs_guard;
|
|
``session_id`` goes through ``SESSION_ID_RE.fullmatch``. Any rejection
|
|
raises ``PathValidationError``.
|
|
"""
|
|
_validate_agent_triple(org_id_15, agent_api_name, agent_version)
|
|
validate_session_id(session_id)
|
|
return (
|
|
DATA_ROOT
|
|
/ org_id_15
|
|
/ f"{agent_api_name}__{agent_version}"
|
|
/ session_id
|
|
)
|